diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index 845ca06..0000000
--- a/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-.pc
diff --git a/COPYING b/COPYING
deleted file mode 100644
index 754bfe4..0000000
--- a/COPYING
+++ /dev/null
@@ -1,20 +0,0 @@
-The MIT License (MIT)
-
-Copyright (c) 2009-2015 Various Contributors (see individual files and source control)
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
-the Software, and to permit persons to whom the Software is furnished to do so,
-subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
-FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
-COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
-IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/README b/README
new file mode 100644
index 0000000..bc136d0
--- /dev/null
+++ b/README
@@ -0,0 +1,5 @@
+Repository discontinued
+-----------------------
+
+The repository is for historical purposes only, it was obsoleted and
+no longer available starting with mr14.0.
diff --git a/README.md b/README.md
deleted file mode 100644
index 631e620..0000000
--- a/README.md
+++ /dev/null
@@ -1,128 +0,0 @@
-examples of messages for testing funtionality
----------------------------------------------
-
-* mod_sipwise_lastactivity:
-```
-
-
-
-```
-
-```
-
-
-
-```
-
-* mod_sipwise_vcard_cusax:
-```
-
-
-
-```
-response:
-```
-
-
- 43991002@192.168.1.102
- sip:43991002@192.168.1.102
- 43991002
- default-customer@default.invalid
-
-
-```
-
-* mod_sipwise_vhosts_sql:
-```
-
-
-
-```
-response:
-```
-
-
-
-
-
-
-```
-
-* mod_sipwise_vjud:
-
-discover search fields
-```
-
-
-
-```
-response:
-```
-
-
- Use the enclosed form to search
-
- User Directory Search
- Please provide the following information to search for subscribers
- jabber:iq:search
-
-
-
-
-
-
-```
-
-search domain:
-```
-
-
-
- jabber:iq:search
-
-
- 192.168.1.102
-
-
-
-
-```
-response:
-```
-
-
-
- jabber:iq:search
-
- - 192.168.1.102
-
-
-
-```
-
-search number:
-```
-
-
-
- jabber:iq:search
-
-
- 43991003
-
-
-
-
-```
-response:
-```
-
-
-
- jabber:iq:search
-
- - 43991003@192.168.1.102
-
-
-
-```
diff --git a/debian/.gitignore b/debian/.gitignore
deleted file mode 100644
index 0954f39..0000000
--- a/debian/.gitignore
+++ /dev/null
@@ -1,7 +0,0 @@
-*.debhelper
-*.log
-*.substvars
-/.debhelper/
-/debhelper-build-stamp
-/files
-/ngcp-prosody-modules/
diff --git a/debian/changelog b/debian/changelog
deleted file mode 100644
index 9866288..0000000
--- a/debian/changelog
+++ /dev/null
@@ -1,676 +0,0 @@
-ngcp-prosody-modules (14.1.0.0+0~mr14.1.0.0) unstable; urgency=medium
-
- * New release.
-
- -- Sipwise Jenkins Builder Mon, 24 Nov 2025 16:57:25 +0100
-
-ngcp-prosody-modules (14.0.0.0+0~mr14.0.0.0) unstable; urgency=medium
-
- * New release.
-
- -- Sipwise Jenkins Builder Mon, 08 Sep 2025 11:45:56 +0200
-
-ngcp-prosody-modules (13.5.0.0+0~mr13.5.0.0) unstable; urgency=medium
-
- * New release.
-
- -- Sipwise Jenkins Builder Fri, 20 Jun 2025 10:44:56 +0200
-
-ngcp-prosody-modules (13.4.0.0+0~mr13.4.0.0) unstable; urgency=medium
-
- * New release.
-
- -- Sipwise Jenkins Builder Thu, 10 Apr 2025 08:29:21 +0200
-
-ngcp-prosody-modules (13.3.0.0+0~mr13.3.0.0) unstable; urgency=medium
-
- * New release.
-
- -- Sipwise Jenkins Builder Tue, 28 Jan 2025 15:15:59 +0100
-
-ngcp-prosody-modules (13.2.0.0+0~mr13.2.0.0) unstable; urgency=medium
-
- [ Richard Fuchs ]
- * [01bb8cb] MT#53790 pushd: rename `gcm` to `fcm`
-
- -- Sipwise Jenkins Builder Mon, 09 Dec 2024 11:28:18 +0100
-
-ngcp-prosody-modules (13.1.0.0+0~mr13.1.0.0) unstable; urgency=medium
-
- * New release.
-
- -- Sipwise Jenkins Builder Tue, 01 Oct 2024 10:01:17 +0200
-
-ngcp-prosody-modules (13.0.0.0+0~mr13.0.0.0) unstable; urgency=medium
-
- * New release.
-
- -- Sipwise Jenkins Builder Sun, 28 Jul 2024 12:04:04 +0200
-
-ngcp-prosody-modules (12.5.0.0+0~mr12.5.0.0) unstable; urgency=medium
-
- * New release.
-
- -- Sipwise Jenkins Builder Wed, 22 May 2024 14:32:48 +0200
-
-ngcp-prosody-modules (12.4.0.0+0~mr12.4.0.0) unstable; urgency=medium
-
- * New release.
-
- -- Sipwise Jenkins Builder Fri, 15 Mar 2024 08:49:29 +0100
-
-ngcp-prosody-modules (12.3.0.0+0~mr12.3.0.0) unstable; urgency=medium
-
- * New release.
-
- -- Sipwise Jenkins Builder Mon, 15 Jan 2024 14:48:24 +0100
-
-ngcp-prosody-modules (12.2.0.0+0~mr12.2.0.0) unstable; urgency=medium
-
- * New release.
-
- -- Sipwise Jenkins Builder Wed, 29 Nov 2023 13:38:06 +0100
-
-ngcp-prosody-modules (12.1.0.0+0~mr12.1.0.0) unstable; urgency=medium
-
- [ Guillem Jover ]
- * [bef0870] MT#58356 Update packaging for bookworm
-
- [ Victor Seva ]
- * [6957e9c] MT#58471 refresh modules
-
- [ Marco Capetta ]
- * [02a6ec0] MT#58471 Fix mod_carbons_adhoc call to adhoc module
-
- -- Sipwise Jenkins Builder Sun, 15 Oct 2023 08:25:07 +0200
-
-ngcp-prosody-modules (12.0.1.0+0~mr12.0.1.0) unstable; urgency=medium
-
- [ Mykola Malkov ]
- * [29dbb14] MT#57453 Switch docker image to bookworm
-
- -- Sipwise Jenkins Builder Wed, 30 Aug 2023 18:05:32 +0200
-
-ngcp-prosody-modules (11.5.0.0+0~mr11.5.0.0) unstable; urgency=medium
-
- * New release.
-
- -- Sipwise Jenkins Builder Mon, 26 Jun 2023 14:11:45 +0200
-
-ngcp-prosody-modules (11.4.0.0+0~mr11.4.0.0) unstable; urgency=medium
-
- * New release.
-
- -- Sipwise Jenkins Builder Fri, 24 Mar 2023 13:05:07 +0100
-
-ngcp-prosody-modules (11.3.0.0+0~mr11.3.0.0) unstable; urgency=medium
-
- [ Michael Prokop ]
- * [0fac3f5] MT#55819 MT#55524 Depend on either lua-rex-pcre or lua-rex-pcre2
-
- [ Victor Seva ]
- * [50c3253] MT#55920 Support rex_pcre2 with Debian/bookworm
-
- -- Sipwise Jenkins Builder Tue, 17 Jan 2023 17:44:38 +0100
-
-ngcp-prosody-modules (11.2.0.0+0~mr11.2.0.0) unstable; urgency=medium
-
- * New release.
-
- -- Sipwise Jenkins Builder Tue, 04 Oct 2022 13:52:12 +0200
-
-ngcp-prosody-modules (11.1.0.0+0~mr11.1.0.0) unstable; urgency=medium
-
- * New release.
-
- -- Sipwise Jenkins Builder Mon, 22 Aug 2022 20:02:57 +0200
-
-ngcp-prosody-modules (11.0.0.0+0~mr11.0.0.0) unstable; urgency=medium
-
- * New release.
-
- -- Sipwise Jenkins Builder Tue, 07 Jun 2022 11:45:14 +0200
-
-ngcp-prosody-modules (10.5.0.0+0~mr10.5.0.0) unstable; urgency=medium
-
- * New release.
-
- -- Sipwise Jenkins Builder Thu, 31 Mar 2022 00:01:48 +0200
-
-ngcp-prosody-modules (10.4.0.0+0~mr10.4.0.0) unstable; urgency=medium
-
- * New release.
-
- -- Sipwise Jenkins Builder Mon, 24 Jan 2022 16:57:42 +0100
-
-ngcp-prosody-modules (10.3.0.0+0~mr10.3.0.0) unstable; urgency=medium
-
- [ Guillem Jover ]
- * [2080a38] TT#124273 Update packaging for bullseye
- * [13049ba] TT#124273 Move override_dh_auto_clean actions into debian/clean
- * [b8e90ca] TT#124273 Use execute_after_ instead of override_ for dh command
-
- [ Victor Seva ]
- * [1511c9a] TT#150950 sipwise_pushd: support new mobile_push_enable values
- * [6ae871f] TT#150950 sipwise_pushd: don't need to check dom preferences
-
- -- Sipwise Jenkins Builder Tue, 23 Nov 2021 14:19:49 +0100
-
-ngcp-prosody-modules (10.2.0.0+0~mr10.2.0.0) unstable; urgency=medium
-
- [ Victor Seva ]
- * [45c42ff] TT#137602 vhost_sql: prepare host from sql
- * [a7e1cd5] TT#137602 auth_sql: lower domain for comparation
-
- -- Sipwise Jenkins Builder Tue, 05 Oct 2021 13:20:47 +0200
-
-ngcp-prosody-modules (10.1.0.0+0~mr10.1.0.0) unstable; urgency=medium
-
- * New release.
-
- -- Sipwise Jenkins Builder Tue, 24 Aug 2021 16:53:07 +0200
-
-ngcp-prosody-modules (10.0.0.0+0~mr10.0.0.0) unstable; urgency=medium
-
- [ Manuel Montecelo ]
- * [5f829f3] TT#118659 Switch Dockerfile from Debian buster to bullseye
-
- -- Sipwise Jenkins Builder Mon, 14 Jun 2021 16:38:22 +0200
-
-ngcp-prosody-modules (9.5.0.0+0~mr9.5.0.0) unstable; urgency=medium
-
- * New release.
-
- -- Sipwise Jenkins Builder Tue, 20 Apr 2021 11:59:27 +0200
-
-ngcp-prosody-modules (9.4.0.0+0~mr9.4.0.0) unstable; urgency=medium
-
- * New release.
-
- -- Sipwise Jenkins Builder Mon, 15 Feb 2021 17:54:21 +0100
-
-ngcp-prosody-modules (9.3.0.0+0~mr9.3.0.0) unstable; urgency=medium
-
- * New release.
-
- -- Sipwise Jenkins Builder Tue, 05 Jan 2021 15:33:09 +0100
-
-ngcp-prosody-modules (9.2.0.0+0~mr9.2.0.0) unstable; urgency=medium
-
- [ Victor Seva ]
- * [181deaf] TT#92952 sipwise_pushd: fix mobile_push_silent_list query
- * [4b1b193] TT#96550 mod_sipwise_redis_*: add some more debug info at add_host()
- * [e7ecca6] TT#15822 mod_sipwise_pushd: fix error introduced back in 2017
- * [addb5e5] TT#97400 sipwise_vhosts_sql: fix [de]activation of vhost via ngcp-panel
- * [2cee084] TT#99900 remove utils/table.lua and use the one from lua-ngcp-kamailio
- * [dde6a2f] TT#96550 mod_muc interface changed since 0.11.0
-
- -- Sipwise Jenkins Builder Mon, 09 Nov 2020 16:26:34 +0100
-
-ngcp-prosody-modules (9.1.0.0+0~mr9.1.0.0) unstable; urgency=medium
-
- * New release.
-
- -- Sipwise Jenkins Builder Mon, 31 Aug 2020 21:55:41 +0200
-
-ngcp-prosody-modules (9.0.0.0+0~mr9.0.0.0) unstable; urgency=medium
-
- * New release.
-
- -- Sipwise Jenkins Builder Tue, 21 Jul 2020 14:49:26 +0200
-
-ngcp-prosody-modules (8.6.0.0+0~mr8.6.0.0) unstable; urgency=medium
-
- * New release.
-
- -- Sipwise Jenkins Builder Tue, 14 Jul 2020 11:54:14 +0200
-
-ngcp-prosody-modules (8.5.0.0+0~mr8.5.0.0) unstable; urgency=medium
-
- * New release.
-
- -- Sipwise Jenkins Builder Tue, 19 May 2020 11:11:46 +0200
-
-ngcp-prosody-modules (8.4.0.0+0~mr8.4.0.0) unstable; urgency=medium
-
- [ Richard Fuchs ]
- * [06bdfa9] TT#20350 mod_auth_sql: check subscriber lock level
-
- -- Sipwise Jenkins Builder Mon, 16 Mar 2020 11:17:03 +0100
-
-ngcp-prosody-modules (8.3.0.0+0~mr8.3.0.0) unstable; urgency=medium
-
- [ Victor Seva ]
- * [0f25a1c] TT#72257 refresh plugins from upstream
- * [34e3345] TT#35053 remove mod_mam patch for sipwise_archive.lib
- * [4fb063d] TT#66583 remove modules merged at prosody 0.11 version
- * [937d0dd] TT#66583 remove patch for own module
- * [f9b0c6f] TT#66583 fix load module error
- * [157f5fe] TT#66583 changes needed for support 0.11
- * [a1721ca] TT#66583 remove unussed mod_sipwise_offline
-
- [ Alexander Lutay ]
- * [731a058] TT#66583 Add util/prosody_sipwise_migrator for upgrade to prosody 0.11
-
- -- Sipwise Jenkins Builder Mon, 27 Jan 2020 12:25:14 +0100
-
-ngcp-prosody-modules (8.2.0.0+0~mr8.2.0.0) unstable; urgency=medium
-
- * New release.
-
- -- Sipwise Jenkins Builder Mon, 25 Nov 2019 14:58:26 +0100
-
-ngcp-prosody-modules (8.1.0.0+0~mr8.1.0.0) unstable; urgency=medium
-
- [ Manuel Montecelo ]
- * [5c9c265] Revert "TT#44917 Ship certain modules only on Debian/stretch"
- * [3f09215] TT#66582 Change the dependency on prosody, it does not work with later versions
-
- [ Sipwise Jenkins Builder ]
-
- -- Sipwise Jenkins Builder Tue, 08 Oct 2019 09:11:47 +0200
-
-ngcp-prosody-modules (8.0.0.0+0~mr8.0.0.0) unstable; urgency=medium
-
- [ Alexander Lutay ]
- * [b898a6c] TT#60950 Switch prosody docker to buster
-
- [ Guillem Jover ]
- * [9cbd440] TT#61954 Add missing COPYING file from upstream
- * [c873da3] TT#61954 Update copyright Format URL to use https
- * [22a5ddd] TT#61954 Rework debian/copyright file
- * [4211f6d] TT#61954 Update copyright years
- * [3ab509e] TT#61954 Set debhelper compat level in Build-Depends instead of debian/compat
-
- [ Sipwise Jenkins Builder ]
-
- -- Sipwise Jenkins Builder Mon, 05 Aug 2019 20:59:38 +0200
-
-ngcp-prosody-modules (7.5.0.0+0~mr7.5.0.0) unstable; urgency=medium
-
- * New release.
-
- -- Sipwise Jenkins Builder Mon, 17 Jun 2019 14:39:21 +0200
-
-ngcp-prosody-modules (7.4.0.0+0~mr7.4.0.0) unstable; urgency=medium
-
- * New release.
-
- -- Sipwise Jenkins Builder Thu, 25 Apr 2019 14:31:30 +0200
-
-ngcp-prosody-modules (7.3.0.0+0~mr7.3.0.0) unstable; urgency=medium
-
- * New release.
-
- -- Sipwise Jenkins Builder Mon, 25 Feb 2019 22:58:44 +0100
-
-ngcp-prosody-modules (7.2.0.0+0~mr7.2.0.0) unstable; urgency=medium
-
- * New release.
-
- -- Sipwise Jenkins Builder Wed, 09 Jan 2019 22:03:22 +0100
-
-ngcp-prosody-modules (7.1.0.0+0~mr7.1.0.0) unstable; urgency=medium
-
- [ Michael Prokop ]
- * [039086e] TT#44917 Ship certain modules only on Debian/stretch
-
- [ Sipwise Jenkins Builder ]
-
- -- Sipwise Jenkins Builder Mon, 12 Nov 2018 22:39:48 +0100
-
-ngcp-prosody-modules (7.0.0.0+0~mr7.0.0.0) unstable; urgency=medium
-
- * New release.
-
- -- Sipwise Jenkins Builder Mon, 24 Sep 2018 18:58:25 +0200
-
-ngcp-prosody-modules (6.5.0.0+0~mr6.5.0.0) unstable; urgency=medium
-
- * New release.
-
- -- Sipwise Jenkins Builder Tue, 24 Jul 2018 13:40:44 +0200
-
-ngcp-prosody-modules (6.4.0.0+0~mr6.4.0.0) unstable; urgency=medium
-
- [ Andreas Granig ]
- * [a0418df] TT#35053 Fix MAM by retrieving also inbound msg
- * [fde6324] TT#35604 Implement silent flag for push
-
- [ Guillem Jover ]
- * [c17d295] TT#26264 Remove maintainer script directly calling init scripts
-
- [ Sipwise Jenkins Builder ]
-
- -- Sipwise Jenkins Builder Tue, 29 May 2018 10:09:00 +0200
-
-ngcp-prosody-modules (6.3.0.0+0~mr6.3.0.0) unstable; urgency=medium
-
- [ Victor Seva ]
- * [b84c7b3] [util/table] fix implode function
- * [dd76b25] TT#33379 Merge https://github.com/sipwise/prosody/pull/2
-
- [ Andreas Granig ]
- * [5784273] TT#27653 Strip trailing trash from uuid
-
- [ Richard Fuchs ]
- * [bffc901] TT#34705 merge https://github.com/sipwise/prosody/pull/3
-
- [ Sipwise Jenkins Builder ]
-
- -- Sipwise Jenkins Builder Tue, 03 Apr 2018 22:32:05 +0200
-
-ngcp-prosody-modules (6.2.0.0+0~mr6.2.0.0) unstable; urgency=medium
-
- [ Sergii Kipot ]
- * [cd32e76] TT#29456 Create prosody-jessie docker for luacheck
- * [48fbebb] TT#29456 Create prosody-stretch docker for luacheck
-
- [ Sipwise Jenkins Builder ]
-
- -- Sipwise Jenkins Builder Mon, 29 Jan 2018 21:56:46 +0100
-
-ngcp-prosody-modules (6.1.0.0+0~mr6.1.0.0) unstable; urgency=medium
-
- [ Guillem Jover ]
- * [a6fa2c1] TT#22072 Update packaging
-
- [ Sipwise Jenkins Builder ]
-
- -- Sipwise Jenkins Builder Wed, 06 Dec 2017 00:13:15 +0100
-
-ngcp-prosody-modules (6.0.0.0+0~mr6.0.0.0) unstable; urgency=medium
-
- * New release.
-
- -- Sipwise Jenkins Builder Mon, 02 Oct 2017 23:43:33 +0200
-
-ngcp-prosody-modules (5.5.0.0+0~mr5.5.0.0) unstable; urgency=medium
-
- [ Victor Seva ]
- * [1057e3a] TT#17657 mod_sipwise_pushd: send full_message field to pushd
-
- [ Sipwise Jenkins Builder ]
-
- -- Sipwise Jenkins Builder Mon, 17 Jul 2017 22:47:07 +0200
-
-ngcp-prosody-modules (5.4.0.0+0~mr5.4.0.0) unstable; urgency=medium
-
- [ Victor Seva ]
- * [fb99302] TT#13055 mod_sipwise_pushd: support muc_config
- * [5b993fc] TT#14103 update upstream modules to 2e1a4740adee
- * [0117591] TT#14278 mam: add all supported versions to disco
- * [d6f87e7] TT#14258 mod_sipwise_pushd: add nick attribute for MUC presence
- * [23e737f] TT#15822 mod_sipwise_pushd: detect attachment message
- * [ef87c7b] TT#14103 update upstream modules to 9b43b7fc3558
- * [a70b099] TT#16400 support exclude config muc rooms from sending push notifications
-
- [ Andreas Granig ]
- * [0521a03] TT#14099 Send muc room name in push
-
- [ Sipwise Jenkins Builder ]
-
- -- Sipwise Jenkins Builder Tue, 16 May 2017 21:43:06 +0200
-
-ngcp-prosody-modules (5.3.0.0+0~mr5.3.0.0) unstable; urgency=medium
-
- * New release.
-
- -- Sipwise Jenkins Builder Wed, 22 Mar 2017 11:00:59 +0100
-
-ngcp-prosody-modules (5.2.0.0+0~mr5.2.0.0) unstable; urgency=medium
-
- [ Victor Seva ]
- * [c52d6cd] MT#16059 mod_sipwise_vhosts_sql: fix debug output
- * [bb1360f] TT#7560 mod_sipwise_offline
- * [9e65f10] TT#7482 mod_man: support mam version 0.5 (urn:xmpp:mam:1)
- * [754fb5b] TT#8297 mam: implement archive to DB
- * [c2dec69] TT#8766 mod_sipwise_pushd: fix error when caller is not from local domain
- * [9e93fb6] TT#9303 mod_mam: sipwise_archive.lib fix crash at find()
- * [b17aec9] TT#9311 mod_mam/sipwise_archive.lib: full username
- * [7fc4d61] TT#9317 mod_mam: don't store bodyless chat messages
- * [0fbc57e] TT#9400 use quilt to maintain patches to upstream
- * [e191475] TT#9402 refresh modules from upstream
- * [11f3f0b] TT#9504 sipwise_offline: do not store bodyless chat messages
- * [d5491e2] TT#9503 sipwise_pushd: wrong offline badge
- * [937ff0f] TT#9314: support emojis for DB
- * [ff4c2e0] TT#9551 set charset on reconnection
-
- [ Sipwise Jenkins Builder ]
-
- -- Sipwise Jenkins Builder Fri, 03 Feb 2017 00:41:42 +0100
-
-ngcp-prosody-modules (5.1.0.0+0~mr5.1.0.0) unstable; urgency=medium
-
- [ Victor Seva ]
- * [e4a378a] MT#10921 add mod_man plugin
- * [5003261] TT#6605 mod_sipwise_pushd: fix MUC offline when sender has more than one session
- * [0d7e7ac] TT#6646 support XEP-0352: Client State Indication
-
- [ Sipwise Jenkins Builder ]
-
- -- Sipwise Jenkins Builder Thu, 17 Nov 2016 23:19:59 +0100
-
-ngcp-prosody-modules (5.0.0.0+0~mr5.0.0.0) unstable; urgency=medium
-
- [ Victor Seva ]
- * [b09f209] MT#19579 pushd: define get_muc_room() to filter possible errors
-
- -- Alexander Lutay Fri, 21 Oct 2016 15:18:37 +0200
-
-ngcp-prosody-modules (1.15.0.0+0~mr5.0.0.0) unstable; urgency=medium
-
- [ Victor Seva ]
- * [608b9d6] MT#20021 mod_sipwise_vjud: add some debug messages related to normalize_number and search_by_number
- * [bc610ad] Revert "MT#18055 mod_sipwise_vjud: use dataforms for searching e164 values"
- * [9b811d4] MT#18055 mod_sipwise_vjud: use dataforms for searching e164 values
- * [778b400] TT#1597 sipwise_vjud: add domain field
- * [12c628f] TT#1597 sipwise_vjud: fix dataform reply format
- * [c51c1f6] TT#1597 sipwise_vjud: fix dataform reply format
-
- [ Guillem Jover ]
- * [4a32147] MT#20479 Fix the package to be architecture all
-
- [ Sipwise Jenkins Builder ]
-
- -- Sipwise Jenkins Builder Mon, 01 Aug 2016 16:35:39 +0200
-
-ngcp-prosody-modules (1.14.0.0+0~mr4.5.0.0) unstable; urgency=medium
-
- [ Victor Seva ]
- * [5181464] MT#18725 mod_sipwise_groups: reconnect_check for lookup functions
- * [b83138c] MT#18905 support pushd_blocking
-
- [ Sipwise Jenkins Builder ]
-
- -- Sipwise Jenkins Builder Mon, 23 May 2016 19:00:19 +0200
-
-ngcp-prosody-modules (1.13.0.0+0~mr4.4.0.0) unstable; urgency=medium
-
- [ Victor Seva ]
- * [dad9a2c] MT#17545 upgrade community modules
- * [9474448] MT#17541 support XEP-191 mod_blocking
- * [c122e4e] MT#18041 mod_sipwise_vjud: fix discovery
- * [f6031ab] MT#18041 mod_sipwise_vjud: fix discovery, clean code
- * [32cbf31] MT#18055 mod_sipwise_vjud: use dataforms for searching e164 values
-
- [ Michael Prokop ]
- * [aa5ffbc] MT#17699 Bump Standards-Version to 3.9.7
-
- [ Sipwise Jenkins Builder ]
-
- -- Sipwise Jenkins Builder Mon, 21 Mar 2016 22:29:16 +0100
-
-ngcp-prosody-modules (1.12.0.0+0~mr4.3.0.0) unstable; urgency=medium
-
- [ Victor Seva ]
- * [ff763a7] MT#15837 mod_sipwise_vcard_cusax: add error when no info is retrive
- * [24c1cc4] MT#15893 mod_sipwise_vcard_cusax: return error if subscriber does not exist
- * [bbb6f0f] MT#15819 mod_sipwise_pushd: set apns_alert message on muc
- * [c5f0784] MT#6407 cluster using mod_shard* as base
- * [babc44f] MT#6407 support MUC on cluster
- * [320bdef] MT#16059 save MUC participants on redis too
- * [8243371] MT#16059 mod_sipwise_redis_mucs: fix unbind hook. fixes commit 320bdefb33c
- * [d346079] MT#16059 mod_sipwise_pushd: support groupchat messages
- * [a943438] MT#15893 mod_sipwise_vcard_cusax: fix error if domain has no users
- * [c579c89] MT#16059 mod_sipwise_vhosts_sql: fix load sipwise_vjud component at search domain
- * [7e27dba] MT#16059 mod_sipwise_vjud: fix load module
-
- [ Sipwise Jenkins Builder ]
-
- -- Sipwise Jenkins Builder Wed, 16 Dec 2015 09:30:28 +0100
-
-ngcp-prosody-modules (1.11.0.0+0~mr4.2.0.0) unstable; urgency=medium
-
- [ Victor Seva ]
- * [d20b62f] MT#14281 pushd: use formencode() in order to urlencode the http request
- * [ee207e5] MT#14293 luacheck project config
- * [be8663b] MT#14921 add uuid push_id parameter
- * [6abfd0f] MT#15119 sipwise_log_debug module
- * [f672d8e] MT#15303 add mod_limit_auth
-
- [ Sipwise Jenkins Builder ]
-
- -- Sipwise Jenkins Builder Mon, 12 Oct 2015 15:43:59 +0200
-
-ngcp-prosody-modules (1.10.0.0+0~mr4.1.0.0) unstable; urgency=low
-
- [ Victor Seva ]
- * [5a87e97] MT#8951 fix vcard format v2 and email
- * [d55bd7e] MT#11583 lastactivity: fix debug output and remove .save() .restore()
- * [d08c713] MT#12267 merge changes from 0.9.8
- * [8447071] MT#12267 add mod_carbons and sync mod_smacks
- * [e2ae328] MT#12267 add mod_carbons_* and sync mod_smacks ( license header )
- * [1e3496f] MT#12953 pushd: fix error when the user is not provided properly
-
- [ Sipwise Jenkins Builder ]
-
- -- Sipwise Jenkins Builder Mon, 13 Jul 2015 15:32:17 +0200
-
-ngcp-prosody-modules (1.9.0.0+0~mr4.0.0.0) unstable; urgency=low
-
- [ Victor Seva ]
- * [7780d3c] MT#10849: add mod_smacks plugin
- * [aae45da] MT#6411 redis_session: new key schema
- * [3f2c23f] MT#6407 prosody cluster
- * [5021516] MT#9763 pushd: add prefix with sender_name at apns_alert field
- * [73fde6c] MT#9763 pushd: set the push msg for muc invitations
- * [af940bf] MT#6407 mod_sipwise_cluster: remove unused host config
- * [f1631d6] MT#9763 pushd: fix get_caller_info()
- * [26c11e6] MT#11125 don't restart prosody after install if node is not proxy
- * [c4633d8] MT#11125 fix bashism at 26c11e6e3841a. Don't restart prosody after install if node is not proxy
- * [a017dbf] MT#11583 add our own lastactivity module
-
- [ Sipwise Jenkins Builder ]
-
- -- Sipwise Jenkins Builder Wed, 18 Mar 2015 14:12:57 +0100
-
-ngcp-prosody-modules (1.8.0.0+0~mr3.8.0.0) unstable; urgency=low
-
- [ Victor Seva ]
- * [f031997] MT#9985 vhosts: enable MUC for domains
- * [7945b1b] MT#9985 vhosts: rework enable MUC for domains
- * [6dfa707] MT#9763 pushd: extended gcm and aspn parameters.
- * [dd8da8e] MT#7141 upgrade package to 3.0 style
- * [a6e0655] MT#9763 Pushd: use new format for pushd query. Skip empty messages
- * [d4995ce] MT#9763 push: add data_sender_number and data_sender_sip
-
- [ Alexander Lutay ]
- * [5914b4f] MT#9763 Pushd: updated Apple sound files + typo fix s/aspn/apns/
-
- [ Sipwise Jenkins Builder ]
-
- -- Sipwise Jenkins Builder Wed, 07 Jan 2015 21:37:29 +0100
-
-ngcp-prosody-modules (1.7.0.0+0~mr3.7.0.0) unstable; urgency=low
-
- * New release.
-
- -- Sipwise Jenkins Builder Thu, 30 Oct 2014 17:29:07 +0100
-
-ngcp-prosody-modules (1.6.0.0+0~mr3.6.0.0) unstable; urgency=low
-
- [ Victor Seva ]
- * [0d82202] MT#8851 add mod_sipwise_pushd module
- * [c84cc7e] MT#7505 add .gitreview file
- * [2c0bf78] MT#8951 sipwise_vcard_cusax: add numbers, name and email
- * [31fb42e] MT#8951 fix vcard format and querys
- * [963cfd4] MT#8851 mod_sipwise_pushd: fix 0d8220219aeb2 domain query
- * [4201b42] MT#8851 mod_sipwise_pushd: fix 0d8220219aeb2 domain query call
- * [39e4b7d] MT#8851 pushd: use from info when available if origin is null
-
- [ Sipwise Jenkins Builder ]
-
- -- Sipwise Jenkins Builder Thu, 09 Oct 2014 16:52:28 +0200
-
-ngcp-prosody-modules (1.5.0.0+0~mr3.5.0.0) unstable; urgency=low
-
- [ Victor Seva ]
- * [f9d8739] MT#7545 Adapt queries to the new voip_pbx_groups schema.
- * [9a736c5] MT#6431 restart prosody service after instalation
- * [a02bb76] MT#7545 remove pilot from the list of subscribers
- * [fce80c1] MT#6431 fix postinst script if node is not active
-
- [ Sipwise Jenkins Builder ]
-
- -- Sipwise Jenkins Builder Thu, 10 Jul 2014 15:35:26 +0200
-
-ngcp-prosody-modules (1.4.0.0+0~mr3.4.0.0) unstable; urgency=low
-
- [ Victor Seva ]
- * [47678b7] MT#6285 add mod_log_auth plugin
-
- [ Sipwise Jenkins Builder ]
-
- -- Sipwise Jenkins Builder Mon, 26 May 2014 15:58:51 +0200
-
-ngcp-prosody-modules (1.3.0.0+0~mr3.3.0.0) unstable; urgency=low
-
- [ Victor Seva ]
- * [47f488b] Add licence note on source files
- * [8dd77ca] MT#6409 Create mod_sipwise_admin_telnet
- * [f4cef17] MT#6411 Add mod_sipwise_redis_sessions module.
- * [efb226e] MT#6285 add mod_log_auth plugin
- * [f53d100] MT#7053 add database name to the queries. Use auth_sql prosody config.
-
- [ Michael Prokop ]
- * [80bcd45] MT#6511 Bump Standards-Version to 3.9.5
-
- -- Alexander Lutay Wed, 14 May 2014 11:10:31 +0200
-
-ngcp-prosody-modules (1.2.0.0+0~mr3.3.0.0) unstable; urgency=low
-
- [ Andreas Granig ]
- * [68fb4e4] MT#5811 Add xmpp-over-websocket plugin.
-
- -- Alexander Lutay Fri, 21 Mar 2014 15:42:58 +0100
-
-ngcp-prosody-modules (1.1.0) unstable; urgency=low
-
- [ Victor Seva ]
- * [09d8ccd] Initial version of sipwise_groups module.
-
- [ Andreas Granig ]
- * [c71c975] Update copyright to cover all files.
-
- -- Victor Seva Tue, 29 Oct 2013 14:59:54 +0100
-
-ngcp-prosody-modules (1.0.1) unstable; urgency=low
-
- * [dea7650] Implicitely add domain with "search" prefix.
-
- -- Andreas Granig Thu, 08 Aug 2013 00:03:08 +0200
-
-ngcp-prosody-modules (1.0.0) unstable; urgency=low
-
- * [804143c] Remove authentication by number.
- * [28b5fc5] Implement XEP-0055 with implicit rewrite rules.
-
- -- Andreas Granig Wed, 07 Aug 2013 22:38:07 +0200
-
-ngcp-prosody-modules (0.0.1) unstable; urgency=low
-
- * Initial release
-
- -- Michael Prokop Tue, 02 Jul 2013 01:54:04 +0200
-
diff --git a/debian/clean b/debian/clean
deleted file mode 100644
index 2967547..0000000
--- a/debian/clean
+++ /dev/null
@@ -1 +0,0 @@
-debian/README_mod_*.*
diff --git a/debian/control b/debian/control
deleted file mode 100644
index c0f2daf..0000000
--- a/debian/control
+++ /dev/null
@@ -1,58 +0,0 @@
-Source: ngcp-prosody-modules
-Section: net
-Priority: optional
-Maintainer: Sipwise Development Team
-Homepage: https://www.sipwise.com/
-Standards-Version: 4.6.2
-Rules-Requires-Root: no
-Build-Depends:
- debhelper-compat (= 13),
- quilt,
-
-Package: ngcp-prosody-modules
-Architecture: all
-Depends:
- lua-bitop,
- lua-dbi-common,
- lua-dbi-mysql,
- lua-ngcp-kamailio,
- lua-redis,
- lua-rex-pcre | lua-rex-pcre2,
- lua-sec,
- lua5.2,
- prosody (>= 0.11~),
- ${misc:Depends},
-Description: ngcp modules for the prosody Jabber/XMPP server
- This package provides modules for the Prosody Jabber/XMPP server
- Prosody, especially for usage with the Sipwise Next Generation
- Communication Platform.
- .
- The following modules are available:
- .
- * mod_sipwise_admin_telnet: admin_telnet with inteface selection
- * mod_sipwise_groups.lua: inserts PBX groups to the user contact list
- * mod_sipwise_lastactivity: XEP-0012
- * mod_sipwise_pushd: push notification to mobile users
- * mod_sipwise_redis_sessions: keep server's session info on redis
- * mod_sipwise_vcard_cusax.lua: return a vcard containing SIP URIs for
- phone/video of the requested user
- * mod_sipwise_vhosts_sql.lua: load vhosts from DB on startup of Prosody
- XMPP server
- * mod_sipwise_vjud.lua: implement Jabber Search with implicit number
- normalization using the NGCP rewrite rules
- .
- The following modules are from prosody-modules:
- .
- * mod_auth_sql.lua: simple SQL Authentication module
- * mod_blocking: allows the client to manage a simple
- list of blocked JIDs
- * mod_carbons_adhoc
- * mod_carbons_copies
- * mod_filter_chatstates: will filter Chat State Notifications out
- while the session is considered inactive
- * mod_limit_auth: lets you put a per-IP limit on the number of
- failed authentication attempts
- * mod_log_auth: logs IP address in a failed authentication attempt
- * mod_smacks: XEP-0198: Stream Management
- * mod_throttle_presence: cuts down on presence traffic when clients
- indicate they are inactive
diff --git a/debian/copyright b/debian/copyright
deleted file mode 100644
index 48222d3..0000000
--- a/debian/copyright
+++ /dev/null
@@ -1,98 +0,0 @@
-Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
-Source: https://www.sipwise.com/
-Upstream-Name: ngcp-prosody
-Upstream-Contact: Sipwise GmbH
-
-Files:
- *
-Copyright:
- 2013-2023 Sipwise GmbH, Austria
-License: MIT
-
-Files:
- plugins/mod_auth_sql.lua
-Copyright:
- 2011 Tomasz Sterna
- 2011 Waqas Hussain
- 2013-2023 Sipwise GmbH, Austria
-License: MIT
-
-Files:
- plugins/mod_sipwise_vcard_cusax.lua
-Copyright:
- 2008-2010 Matthew Wild
- 2008-2010 Waqas Hussain
- 2013-2023 Sipwise GmbH, Austria
-License: MIT
-
-Files:
- plugins/mod_carbons.lua
-Copyright:
- 2011 Kim Alvefur
-License: MIT
-
-Files:
- plugins/mod_carbons_copies.lua
-Copyright:
- 2012 Michael Holzt
- 2014 Florian Zeitz
-License: MIT
-
-Files:
- plugins/mod_carbons_adhoc.lua
-Copyright:
- 2012 Michael Holzt
- 2014 Florian Zeitz
-License: MIT
-
-Files:
- plugins/mod_sipwise_lastactivity.lua
-Copyright:
- 2008-2010 Matthew Wild
- 2008-2010 Waqas Hussain
- 2015-2023 Sipwise GmbH, Austria
-License: MIT
-
-Files:
- plugins/mod_log_auth.lua
-Copyright:
- 2013 Kim Alvefur
-License: MIT
-
-Files:
- plugins/mod_smacks.lua
-Copyright:
- 2010-2015 Matthew Wild
- 2010 Waqas Hussain
- 2012-2015 Kim Alvefur
- 2012 Thijs Alkemade
- 2014 Florian Zeitz
-License: MIT
-
-Files:
- plugins/mod_blocking.lua
-Copyright:
- 2010-2015 Kim Alvefur
- 2013 Tobias Markmann
- 2011 Paul Aurich
- 2010 Waqas Hussain
- 2010 Matthew Wild
-License: MIT
-
-License: MIT
- Permission is hereby granted, free of charge, to any person obtaining a copy of
- this software and associated documentation files (the "Software"), to deal in
- the Software without restriction, including without limitation the rights to
- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
- the Software, and to permit persons to whom the Software is furnished to do so,
- subject to the following conditions:
- .
- The above copyright notice and this permission notice shall be included in all
- copies or substantial portions of the Software.
- .
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
- FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
- COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
- IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/debian/ngcp-prosody-modules.docs b/debian/ngcp-prosody-modules.docs
deleted file mode 100644
index 547bb7b..0000000
--- a/debian/ngcp-prosody-modules.docs
+++ /dev/null
@@ -1 +0,0 @@
-debian/README_mod*
diff --git a/debian/ngcp-prosody-modules.install b/debian/ngcp-prosody-modules.install
deleted file mode 100644
index 0c09054..0000000
--- a/debian/ngcp-prosody-modules.install
+++ /dev/null
@@ -1,2 +0,0 @@
-plugins/* /usr/lib/prosody/modules/
-util/* /usr/lib/prosody/util/
diff --git a/debian/patches/fix_mod_carbons_adhoc_call_to_adhoc.patch b/debian/patches/fix_mod_carbons_adhoc_call_to_adhoc.patch
deleted file mode 100644
index a229855..0000000
--- a/debian/patches/fix_mod_carbons_adhoc_call_to_adhoc.patch
+++ /dev/null
@@ -1,21 +0,0 @@
-From: Sipwise Development Team
-Date: Wed, 11 Oct 2023 09:52:08 +0200
-Subject: fix_mod_carbons_adhoc_call_to_adhoc
-
-Change-Id: If2ce4cf8dff63c3a8491845d76f0b33b472dc8b6
----
- plugins/mod_carbons_adhoc/mod_carbons_adhoc.lua | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/plugins/mod_carbons_adhoc/mod_carbons_adhoc.lua b/plugins/mod_carbons_adhoc/mod_carbons_adhoc.lua
-index 406a0b0..e8af962 100644
---- a/plugins/mod_carbons_adhoc/mod_carbons_adhoc.lua
-+++ b/plugins/mod_carbons_adhoc/mod_carbons_adhoc.lua
-@@ -35,6 +35,6 @@ local function adhoc_status(self, data, state)
- end
-
- local status_desc = adhoc_new("Carbons: Get Status",
-- "mod_carbons_adhoc#status", adhoc_status);
-+ "mod_carbons_adhoc#status", adhoc_status, 'any');
-
- module:add_item("adhoc", status_desc);
diff --git a/debian/patches/mod_auth_sql.path b/debian/patches/mod_auth_sql.path
deleted file mode 100644
index 6c0241a..0000000
--- a/debian/patches/mod_auth_sql.path
+++ /dev/null
@@ -1,37 +0,0 @@
-From: Andreas Granig
-Date: Wed, 7 Aug 2013 22:34:34 +0200
-Subject: sipwise custom auth
-
----
- plugins/mod_auth_sql/mod_auth_sql.lua | 9 ++++++---
- 1 file changed, 6 insertions(+), 3 deletions(-)
-
-diff --git a/plugins/mod_auth_sql/mod_auth_sql.lua b/plugins/mod_auth_sql/mod_auth_sql.lua
-index 2947a91..631768d 100644
---- a/plugins/mod_auth_sql/mod_auth_sql.lua
-+++ b/plugins/mod_auth_sql/mod_auth_sql.lua
-@@ -71,10 +71,13 @@ local function getsql(sql, ...)
- end
-
- local function get_password(username)
-- local stmt, err = getsql("SELECT `password` FROM `authreg` WHERE `username`=? AND `realm`=?", username, module.host);
-+ local stmt, err = getsql("SELECT `s`.`password`, COALESCE(`p`.`value`, 0) AS `locklevel` FROM `subscriber` AS `s` LEFT JOIN usr_preferences AS `p` ON `p`.`username` = `s`.`username` AND `p`.`domain` = `s`.`domain` AND `p`.`attribute` = 'lock' WHERE `s`.`username`= ? AND lower(`s`.`domain`)= ?", username, module.host);
- if stmt then
- for row in stmt:rows(true) do
-- return row.password;
-+ local locklevel = 0 + row.locklevel;
-+ if locklevel < 4 then
-+ return row.password;
-+ end
- end
- end
- end
-@@ -109,7 +112,7 @@ function provider.get_sasl_handler()
- end
-
- function provider.users()
-- local stmt, err = getsql("SELECT `username` FROM `authreg` WHERE `realm`=?", module.host);
-+ local stmt, err = getsql("SELECT `username` FROM `subscriber` WHERE lower(`domain`)=?", module.host);
- if stmt then
- local next, state = stmt:rows(true)
- return function()
diff --git a/debian/patches/series b/debian/patches/series
deleted file mode 100644
index 6e245b8..0000000
--- a/debian/patches/series
+++ /dev/null
@@ -1,2 +0,0 @@
-mod_auth_sql.path
-fix_mod_carbons_adhoc_call_to_adhoc.patch
diff --git a/debian/rules b/debian/rules
deleted file mode 100755
index 50f855e..0000000
--- a/debian/rules
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/usr/bin/make -f
-# Uncomment this to turn on verbose mode.
-# export DH_VERBOSE=1
-
-%:
- dh $@ --with quilt
-
-override_dh_auto_configure:
- while read module ; do \
- if [ -f plugins/$$module/README.markdown ] ; then \
- cp plugins/$$module/README.markdown debian/README_$$module.markdown; \
- fi; \
- done < prosody-modules.list; \
-
-execute_after_dh_install:
- find debian/ngcp-prosody-modules/ -name README.\* -delete
diff --git a/debian/source/format b/debian/source/format
deleted file mode 100644
index 89ae9db..0000000
--- a/debian/source/format
+++ /dev/null
@@ -1 +0,0 @@
-3.0 (native)
diff --git a/plugins/mod_auth_sql/README.markdown b/plugins/mod_auth_sql/README.markdown
deleted file mode 100644
index 31485d1..0000000
--- a/plugins/mod_auth_sql/README.markdown
+++ /dev/null
@@ -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
- ----- -------
diff --git a/plugins/mod_auth_sql/mod_auth_sql.lua b/plugins/mod_auth_sql/mod_auth_sql.lua
deleted file mode 100644
index 2947a91..0000000
--- a/plugins/mod_auth_sql/mod_auth_sql.lua
+++ /dev/null
@@ -1,125 +0,0 @@
--- Simple SQL Authentication module for Prosody IM
--- Copyright (C) 2011 Tomasz Sterna
--- Copyright (C) 2011 Waqas Hussain
---
-
-local log = require "util.logger".init("auth_sql");
-local new_sasl = require "util.sasl".new;
-local DBI = require "DBI"
-
-local connection;
-local params = module:get_option("auth_sql", module:get_option("sql"));
-
-local resolve_relative_path = require "core.configmanager".resolve_relative_path;
-
-local function test_connection()
- if not connection then return nil; end
- if connection:ping() then
- return true;
- else
- module:log("debug", "Database connection closed");
- connection = nil;
- end
-end
-local function connect()
- if not test_connection() then
- prosody.unlock_globals();
- local dbh, err = DBI.Connect(
- params.driver, params.database,
- params.username, params.password,
- params.host, params.port
- );
- prosody.lock_globals();
- if not dbh then
- module:log("debug", "Database connection failed: %s", tostring(err));
- return nil, err;
- end
- module:log("debug", "Successfully connected to database");
- dbh:autocommit(true); -- don't run in transaction
- connection = dbh;
- return connection;
- end
-end
-
-do -- process options to get a db connection
- params = params or { driver = "SQLite3" };
-
- if params.driver == "SQLite3" then
- params.database = resolve_relative_path(prosody.paths.data or ".", params.database or "prosody.sqlite");
- end
-
- assert(params.driver and params.database, "Both the SQL driver and the database need to be specified");
-
- assert(connect());
-end
-
-local function getsql(sql, ...)
- if params.driver == "PostgreSQL" then
- sql = sql:gsub("`", "\"");
- end
- if not test_connection() then connect(); end
- -- do prepared statement stuff
- local stmt, err = connection:prepare(sql);
- if not stmt and not test_connection() then error("connection failed"); end
- if not stmt then module:log("error", "QUERY FAILED: %s %s", err, debug.traceback()); return nil, err; end
- -- run query
- local ok, err = stmt:execute(...);
- if not ok and not test_connection() then error("connection failed"); end
- if not ok then return nil, err; end
-
- return stmt;
-end
-
-local function get_password(username)
- local stmt, err = getsql("SELECT `password` FROM `authreg` WHERE `username`=? AND `realm`=?", username, module.host);
- if stmt then
- for row in stmt:rows(true) do
- return row.password;
- end
- end
-end
-
-
-provider = {};
-
-function provider.test_password(username, password)
- return password and get_password(username) == password;
-end
-function provider.get_password(username)
- return get_password(username);
-end
-function provider.set_password(username, password)
- return nil, "Setting password is not supported.";
-end
-function provider.user_exists(username)
- return get_password(username) and true;
-end
-function provider.create_user(username, password)
- return nil, "Account creation/modification not supported.";
-end
-function provider.get_sasl_handler()
- local profile = {
- plain = function(sasl, username, realm)
- local password = get_password(username);
- if not password then return "", nil; end
- return password, true;
- end
- };
- return new_sasl(module.host, profile);
-end
-
-function provider.users()
- local stmt, err = getsql("SELECT `username` FROM `authreg` WHERE `realm`=?", module.host);
- if stmt then
- local next, state = stmt:rows(true)
- return function()
- for row in next, state do
- return row.username;
- end
- end
- end
- return stmt, err;
-end
-
-
-module:provides("auth", provider);
diff --git a/plugins/mod_blocking/README.markdown b/plugins/mod_blocking/README.markdown
deleted file mode 100644
index 21b2a26..0000000
--- a/plugins/mod_blocking/README.markdown
+++ /dev/null
@@ -1,54 +0,0 @@
----
-labels:
-- 'Stage-Alpha'
-summary: 'XEP-0191: Simple Communications Blocking support'
-...
-
-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 (or [mod\_privacy\_lists] in 0.10+) and
-mod\_blocking are loaded in your modules\_enabled list:
-
- modules_enabled = {
- -- ...
- "privacy", -- or privacy_lists in Prosody 0.10+
- "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
- ------ ---------------------------------------------
diff --git a/plugins/mod_blocking/mod_blocking.lua b/plugins/mod_blocking/mod_blocking.lua
deleted file mode 100644
index 092de6f..0000000
--- a/plugins/mod_blocking/mod_blocking.lua
+++ /dev/null
@@ -1,173 +0,0 @@
-local jid_split = require "util.jid".split;
-local st = require "util.stanza";
-local datamanager = require"util.datamanager";
-
-local xmlns_blocking = "urn:xmpp:blocking";
-
-module:add_feature("urn:xmpp:blocking");
-
--- Add JID to default privacy list
-function add_blocked_jid(username, host, jid)
- local privacy_lists = datamanager.load(username, host, "privacy") or {lists = {}};
- local default_list_name = privacy_lists.default;
- if not privacy_lists.lists then
- privacy_lists.lists = {}
- end
- if not default_list_name then
- default_list_name = "blocklist";
- privacy_lists.default = default_list_name;
- end
- local default_list = privacy_lists.lists[default_list_name];
- if not default_list then
- default_list = { name = default_list_name, items = {} };
- privacy_lists.lists[default_list_name] = default_list;
- end
- local items = default_list.items;
- local order = items[1] and items[1].order or 0; -- Must come first
- for i=1,#items do -- order must be unique
- local item = items[i];
- item.order = item.order + 1;
- if item.type == "jid" and item.action == "deny" and item.value == jid then
- return false;
- end
- end
- table.insert(items, 1, { type = "jid"
- , action = "deny"
- , value = jid
- , message = false
- , ["presence-out"] = false
- , ["presence-in"] = false
- , iq = false
- , order = order
- });
- datamanager.store(username, host, "privacy", privacy_lists);
- return true;
-end
-
--- Remove JID from default privacy list
-function remove_blocked_jid(username, host, jid)
- local privacy_lists = datamanager.load(username, host, "privacy") or {};
- local default_list_name = privacy_lists.default;
- if not default_list_name then return; end
- local default_list = privacy_lists.lists[default_list_name];
- if not default_list then return; end
- local items = default_list.items;
- local item, removed = nil, false;
- for i=1,#items do -- order must be unique
- item = items[i];
- if item.type == "jid" and item.action == "deny" and item.value == jid then
- table.remove(items, i);
- removed = true;
- break;
- end
- end
- if removed then
- datamanager.store(username, host, "privacy", privacy_lists);
- end
- return removed;
-end
-
-function remove_all_blocked_jids(username, host)
- local privacy_lists = datamanager.load(username, host, "privacy") or {};
- local default_list_name = privacy_lists.default;
- if not default_list_name then return; end
- local default_list = privacy_lists.lists[default_list_name];
- if not default_list then return; end
- local items = default_list.items;
- local item;
- for i=#items,1,-1 do -- order must be unique
- item = items[i];
- if item.type == "jid" and item.action == "deny" then
- table.remove(items, i);
- end
- end
- datamanager.store(username, host, "privacy", privacy_lists);
- return true;
-end
-
-function get_blocked_jids(username, host)
- -- Return array of blocked JIDs in default privacy list
- local privacy_lists = datamanager.load(username, host, "privacy") or {};
- local default_list_name = privacy_lists.default;
- if not default_list_name then return {}; end
- local default_list = privacy_lists.lists[default_list_name];
- if not default_list then return {}; end
- local items = default_list.items;
- local item;
- local jid_list = {};
- for i=1,#items do -- order must be unique
- item = items[i];
- if item.type == "jid" and item.action == "deny" then
- jid_list[#jid_list+1] = item.value;
- end
- end
- return jid_list;
-end
-
-local function send_push_iqs(username, host, command_type, jids)
- local bare_jid = username.."@"..host;
-
- local stanza_content = st.stanza(command_type, { xmlns = xmlns_blocking });
- for _, jid in ipairs(jids) do
- stanza_content:tag("item", { jid = jid }):up();
- end
-
- for resource, session in pairs(prosody.bare_sessions[bare_jid].sessions) do
- local iq_push_stanza = st.iq({ type = "set", to = bare_jid.."/"..resource, id = "blocking-push" });
- iq_push_stanza:add_child(stanza_content);
- session.send(iq_push_stanza);
- end
-end
-
-function handle_blocking_command(event)
- local session, stanza = event.origin, event.stanza;
-
- local username, host = jid_split(stanza.attr.from);
- if stanza.attr.type == "set" then
- if stanza.tags[1].name == "block" then
- local block = stanza.tags[1];
- local block_jid_list = {};
- for item in block:childtags() do
- block_jid_list[#block_jid_list+1] = item.attr.jid;
- end
- if #block_jid_list == 0 then
- session.send(st.error_reply(stanza, "modify", "bad-request"));
- else
- for _, jid in ipairs(block_jid_list) do
- add_blocked_jid(username, host, jid);
- end
- session.send(st.reply(stanza));
- send_push_iqs(username, host, "block", block_jid_list);
- end
- return true;
- elseif stanza.tags[1].name == "unblock" then
- local unblock = stanza.tags[1];
- local unblock_jid_list = {};
- for item in unblock:childtags() do
- unblock_jid_list[#unblock_jid_list+1] = item.attr.jid;
- end
- if #unblock_jid_list == 0 then
- remove_all_blocked_jids(username, host);
- else
- for _, jid_to_unblock in ipairs(unblock_jid_list) do
- remove_blocked_jid(username, host, jid_to_unblock);
- end
- end
- session.send(st.reply(stanza));
- send_push_iqs(username, host, "unblock", unblock_jid_list);
- return true;
- end
- elseif stanza.attr.type == "get" and stanza.tags[1].name == "blocklist" then
- local reply = st.reply(stanza):tag("blocklist", { xmlns = xmlns_blocking });
- local blocked_jids = get_blocked_jids(username, host);
- for _, jid in ipairs(blocked_jids) do
- reply:tag("item", { jid = jid }):up();
- end
- session.send(reply);
- return true;
- end
-end
-
-module:hook("iq/self/urn:xmpp:blocking:blocklist", handle_blocking_command);
-module:hook("iq/self/urn:xmpp:blocking:block", handle_blocking_command);
-module:hook("iq/self/urn:xmpp:blocking:unblock", handle_blocking_command);
diff --git a/plugins/mod_carbons_adhoc/mod_carbons_adhoc.lua b/plugins/mod_carbons_adhoc/mod_carbons_adhoc.lua
deleted file mode 100644
index 406a0b0..0000000
--- a/plugins/mod_carbons_adhoc/mod_carbons_adhoc.lua
+++ /dev/null
@@ -1,40 +0,0 @@
--- Implement a Adhoc command which will show a user
--- the status of carbons generation in regard to his clients
---
--- Copyright (C) 2012 Michael Holzt
---
--- This file is MIT/X11 licensed.
-
-local st = require "util.stanza";
-local jid_bare = require "util.jid".bare;
-local adhoc_new = module:require "adhoc".new;
-local xmlns_carbons_v2 = "urn:xmpp:carbons:2";
-local xmlns_carbons_v1 = "urn:xmpp:carbons:1";
-local xmlns_carbons_v0 = "urn:xmpp:carbons:0";
-
-local bare_sessions = bare_sessions;
-
-local function adhoc_status(self, data, state)
- local bare_jid = jid_bare(data.from);
- local user_sessions = bare_sessions[bare_jid];
-
- local result = "";
-
- user_sessions = user_sessions and user_sessions.sessions;
- for _, session in pairs(user_sessions) do
- if session.full_jid then
- result = result .. session.full_jid .. ": " ..
- ( (session.want_carbons == xmlns_carbons_v2 and "v2" ) or
- (session.want_carbons == xmlns_carbons_v1 and "v1" ) or
- (session.want_carbons == xmlns_carbons_v0 and "v0" ) or
- "none" ) .. "\n";
- end
- end
-
- return { info = result, status = "completed" };
-end
-
-local status_desc = adhoc_new("Carbons: Get Status",
- "mod_carbons_adhoc#status", adhoc_status);
-
-module:add_item("adhoc", status_desc);
diff --git a/plugins/mod_carbons_copies/mod_carbons_copies.lua b/plugins/mod_carbons_copies/mod_carbons_copies.lua
deleted file mode 100644
index a798263..0000000
--- a/plugins/mod_carbons_copies/mod_carbons_copies.lua
+++ /dev/null
@@ -1,63 +0,0 @@
--- Send carbons v0 style copies of incoming messages to clients which
--- are not (yet) capable of Message Carbons (XEP-0280).
---
--- This extension integrates with the mod_carbons plugin in such a way
--- that a client capable of Message Carbons will not get a v0 copy.
---
--- This extension can be enabled for all users by default by setting
--- carbons_copies_default = true.
---
--- Alternatively or additionally setting carbons_copies_adhoc = true
--- will allow the user to enable or disable copies through Adhoc
--- commands.
---
--- Copyright (C) 2012 Michael Holzt
---
--- This file is MIT/X11 licensed.
-
-local jid_split = require "util.jid".split;
-local dm_load = require "util.datamanager".load;
-local dm_store = require "util.datamanager".store;
-local adhoc_new = module:require "adhoc".new;
-local xmlns_carbons_v0 = "urn:xmpp:carbons:0";
-local storename = "mod_carbons_copies";
-
-local function toggle_copies(data, on)
- local username, hostname, resource = jid_split(data.from);
- dm_store(username, hostname, storename, { enabled = on });
-end
-
-local function adhoc_enable_copies(self, data, state)
- toggle_copies(data, true);
- return { info = "Copies are enabled for you now.\nPlease restart/reconnect clients.", status = "completed" };
-end
-
-local function adhoc_disable_copies(self, data, state)
- toggle_copies(data, false);
- return { info = "Copies are disabled for you now.\nPlease restart/reconnect clients.", status = "completed" };
-end
-
-module:hook("resource-bind", function(event)
- local session = event.session;
- local username, hostname, resource = jid_split(session.full_jid);
-
- local store = dm_load(username, hostname, storename) or
- { enabled =
- module:get_option_boolean("carbons_copies_default") };
-
- if store.enabled then
- session.want_carbons = xmlns_carbons_v0;
- module:log("debug", "%s enabling copies", session.full_jid);
- end
-end);
-
--- Adhoc-Support
-if module:get_option_boolean("carbons_copies_adhoc") then
- local enable_desc = adhoc_new("Carbons: Enable Copies",
- "mod_carbons_copies#enable", adhoc_enable_copies);
- local disable_desc = adhoc_new("Carbons: Disable Copies",
- "mod_carbons_copies#disable", adhoc_disable_copies);
-
- module:add_item("adhoc", enable_desc);
- module:add_item("adhoc", disable_desc);
-end
diff --git a/plugins/mod_filter_chatstates/README.markdown b/plugins/mod_filter_chatstates/README.markdown
deleted file mode 100644
index 66d8898..0000000
--- a/plugins/mod_filter_chatstates/README.markdown
+++ /dev/null
@@ -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
- ----- -------
diff --git a/plugins/mod_filter_chatstates/mod_filter_chatstates.lua b/plugins/mod_filter_chatstates/mod_filter_chatstates.lua
deleted file mode 100644
index ea21578..0000000
--- a/plugins/mod_filter_chatstates/mod_filter_chatstates.lua
+++ /dev/null
@@ -1,31 +0,0 @@
-local filters = require "util.filters";
-local st = require "util.stanza";
-
-module:depends("csi");
-
-local function chatstate_tag_filter(tag)
- if tag.attr.xmlns ~= "http://jabber.org/protocol/chatstates" then
- return tag;
- end
-end
-
-local function filter_chatstates(stanza)
- if stanza.name == "message" then
- stanza = st.clone(stanza);
- stanza:maptags(chatstate_tag_filter);
- if #stanza.tags == 0 then
- return nil;
- end
- end
- return stanza;
-end
-
-module:hook("csi-client-inactive", function (event)
- local session = event.origin;
- filters.add_filter(session, "stanzas/out", filter_chatstates);
-end);
-
-module:hook("csi-client-active", function (event)
- local session = event.origin;
- filters.remove_filter(session, "stanzas/out", filter_chatstates);
-end);
diff --git a/plugins/mod_limit_auth/README.markdown b/plugins/mod_limit_auth/README.markdown
deleted file mode 100644
index 4cf3143..0000000
--- a/plugins/mod_limit_auth/README.markdown
+++ /dev/null
@@ -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.
diff --git a/plugins/mod_limit_auth/mod_limit_auth.lua b/plugins/mod_limit_auth/mod_limit_auth.lua
deleted file mode 100644
index 5dbc8aa..0000000
--- a/plugins/mod_limit_auth/mod_limit_auth.lua
+++ /dev/null
@@ -1,58 +0,0 @@
--- mod_limit_auth
-
-local st = require"util.stanza";
-local new_throttle = require "util.throttle".create;
-
-local period = math.max(module:get_option_number(module.name.."_period", 30), 0);
-local max = math.max(module:get_option_number(module.name.."_max", 5), 1);
-
-local tarpit_delay = module:get_option_number(module.name.."_tarpit_delay", nil);
-if tarpit_delay then
- local waiter = require "util.async".waiter;
- local delay = tarpit_delay;
- function tarpit_delay()
- local wait, done = waiter();
- module:add_timer(delay, done);
- wait();
- end
-else
- function tarpit_delay() end
-end
-
-local throttles = module:shared"throttles";
-
-local reply = st.stanza("failure", { xmlns = "urn:ietf:params:xml:ns:xmpp-sasl" }):tag("temporary-auth-failure");
-
-local function get_throttle(ip)
- local throttle = throttles[ip];
- if not throttle then
- throttle = new_throttle(max, period);
- throttles[ip] = throttle;
- end
- return throttle;
-end
-
-module:hook("stanza/urn:ietf:params:xml:ns:xmpp-sasl:auth", function (event)
- local origin = event.origin;
- if origin.type ~= "c2s_unauthed" then return end
- if not get_throttle(origin.ip):peek(1) then
- origin.log("warn", "Too many authentication attepmts for ip %s", origin.ip);
- tarpit_delay();
- origin.send(reply);
- return true;
- end
-end, 10);
-
-module:hook("authentication-failure", function (event)
- get_throttle(event.session.ip):poll(1);
-end);
-
-module:add_timer(14400, function (now)
- local old = now - 86400;
- for ip, throttle in pairs(throttles) do
- if throttle.t < old then
- throttles[ip] = nil;
- end
- end
-end);
-
diff --git a/plugins/mod_log_auth/README.markdown b/plugins/mod_log_auth/README.markdown
deleted file mode 100644
index 6e74d01..0000000
--- a/plugins/mod_log_auth/README.markdown
+++ /dev/null
@@ -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:
- 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
- ------- --------------
diff --git a/plugins/mod_log_auth/mod_log_auth.lua b/plugins/mod_log_auth/mod_log_auth.lua
deleted file mode 100644
index ae32f0e..0000000
--- a/plugins/mod_log_auth/mod_log_auth.lua
+++ /dev/null
@@ -1,19 +0,0 @@
-local mode = module:get_option_string("log_auth_ips", "failure");
-assert(({ all = true, failure = true, success = true })[mode],
- "Unknown log mode: "..tostring(mode).." - valid modes are 'all', 'failure', 'success'");
-
-if mode == "failure" or mode == "all" then
- module:hook("authentication-failure", function (event)
- local session = event.session;
- local username = session.username or session.sasl_handler and session.sasl_handler.username or "?";
- session.log("info", "Failed authentication attempt (%s) for user %s@%s from IP: %s",
- event.condition or "unknown-condition", username, module.host, session.ip or "?");
- end);
-end
-
-if mode == "success" or mode == "all" then
- module:hook("authentication-success", function (event)
- local session = event.session;
- session.log("info", "Successful authentication as %s@%s from IP: %s", session.username, module.host, session.ip or "?");
- end);
-end
diff --git a/plugins/mod_shard.lua b/plugins/mod_shard.lua
deleted file mode 100644
index 37775b5..0000000
--- a/plugins/mod_shard.lua
+++ /dev/null
@@ -1,196 +0,0 @@
--- Prosody IM
--- Copyright (C) 2015 Robert Norris
--- Copyright (C) 2015 Sipwise GmbH
---
--- This project is MIT/X11 licensed. Please see the
--- COPYING file in the source package for more information.
---
-module:depends("sipwise_redis_sessions");
-module:depends("sipwise_redis_mucs");
-
-local redis_sessions = module:shared("/*/sipwise_redis_sessions/redis_sessions");
-local redis_mucs = module:shared("/*/sipwise_redis_mucs/redis_mucs");
-local hosts = prosody.hosts;
-local jid_split = require "util.jid".split;
-local jid_bare = require "util.jid".bare;
-local fire_event = prosody.events.fire_event;
-local st = require "util.stanza";
-local set = require "util.set";
-
-local shard_name = module:get_option("shard_name", nil);
-if not shard_name then
- error("shard_name not configured", 0);
-end
-local persistent_rooms = module:open_store("persistent", "map");
-module:log("info", "%s added to shard %s", module.host, shard_name);
-
-local function build_query_result(rooms, stanza)
- local xmlns = 'http://jabber.org/protocol/disco#items';
- local s = stanza:query(xmlns);
- for room in rooms do
- s:tag("item", {jid=room}):up();
- end
- return stanza;
-end
-
-local function get_local_rooms(host)
- local rooms = set.new();
- for room in hosts[host].modules.muc.live_rooms() do
- rooms:add(room.jid);
- end
- return rooms;
-end
-
-local function is_local_room(room_jid)
- local node, host, _ = jid_split(room_jid);
- if not node then return false; end
-
- if not hosts[host] or not hosts[host].modules.muc then
- return false;
- end
- for room in hosts[host].modules.muc.live_rooms() do
- if room.jid == room_jid then return true; end
- end
- return false;
-end
-
-local function is_persistent_room(jid)
- local room_jid = jid_bare(jid);
- local res = persistent_rooms:get(nil, room_jid);
- module:log("debug", "[%s] is_persistent:%s", room_jid, tostring(res));
- if res then return true else return false end;
-end
-
-local function check_redis_info(room_jid)
- local rhost = redis_mucs.get_room_host(room_jid);
-
- if not rhost then return; end
- if rhost ~= shard_name then
- module:log("info", "clean wrong shard info for room[%s]", room_jid);
- redis_mucs.clean_room_host(room_jid, rhost);
- redis_mucs.set_room_host(room_jid, shard_name);
- end
-end
-
-local function handle_room_event(event)
- local to = event.stanza.attr.to;
- local node, host, _ = jid_split(to);
- local rhost, room_jid;
-
- if node then
- room_jid = jid_bare(to);
- if is_local_room(room_jid) then
- module:log("debug", "room[%s] is hosted here. Nothing to do", room_jid);
- check_redis_info(room_jid);
- return nil;
- end
- module:log("debug", "looking up target room shard for %s", to);
- rhost = redis_mucs.get_room_host(to);
- else
- -- TODO: remove me this is just for check if there are missing rooms
- local rooms = set.union(get_local_rooms(host),
- redis_mucs.get_rooms(host));
- module:log("debug", "rooms: %s", tostring(rooms));
- local stanza = build_query_result(rooms, st.reply(event.stanza));
- module:log("debug", "reply[%s]", tostring(stanza));
- event.origin.send(stanza);
- return true;
- end
-
- if not rhost then
- assert(room_jid);
- if is_persistent_room(room_jid) then
- module:log("info",
- "restore missing info for persistent room[%s]", room_jid);
- redis_mucs.set_room_host(room_jid, shard_name);
- else
- module:log("debug", "room[%s] not found, nothing to do", room_jid);
- end
- return nil;
- end
-
- if rhost == shard_name then
- module:log("debug", "room[%s] is hosted here, nothing to do", room_jid);
- return nil
- end
-
- fire_event("shard/send", { shard = rhost, stanza = event.stanza });
- return true;
-end
-
-local function handle_event (event)
- local to = event.stanza.attr.to;
- local node, host, resource = jid_split(to);
- local stop_process_local;
-
- if not host then
- module:log("debug", "no host, nothing to do here");
- return nil
- end
-
- if hosts[host].modules.muc then
- module:log("debug", "to MUC %s detected", host);
- return handle_room_event(event);
- end
-
- if resource and prosody.full_sessions[to] then
- module:log("debug", "%s has a session here, nothing to do", to);
- return nil
- end
-
- if not node then
- module:log("debug", "no node, nothing to do here");
- return nil
- end
-
- module:log("debug", "looking up target shard for %s", to);
-
- local rhosts = redis_sessions.get_hosts(to);
- for shard,resources in pairs(rhosts) do
- if shard and shard ~= shard_name then
- for _,r in pairs(resources) do
- local stanza_c = st.clone(event.stanza);
- stanza_c.attr.to = node..'@'..host..'/'..r;
- module:log("debug", "target shard for %s is %s",
- stanza_c.attr.to ,shard);
- fire_event("shard/send", { shard = shard, stanza = stanza_c });
- stop_process_local = true;
- end
- end
- end
-
- if prosody.bare_sessions[jid_bare(to)] then
- module:log("debug", "%s has a bare session here."..
- " stanza will be processed here too", to);
- return nil;
- end
-
- return stop_process_local;
-end
-
-local function handle_shard_error(event)
- local server_id = event.shard
- local stanza = event.stanza
- local jid = stanza.attr.to;
- local _, host, _ = jid_split(jid);
-
- if hosts[host].modules.muc then
- module:log("debug",
- "to MUC %s detected, clean conference %s", host, jid);
- redis_mucs.clean_room_host(jid, server_id)
- else
- redis_sessions.clean_host(jid, server_id)
- end
-end
-
-module:hook("iq/bare", handle_event, 1000);
-module:hook("iq/full", handle_event, 1000);
-module:hook("iq/host", handle_event, 1000);
-module:hook("message/bare", handle_event, 1000);
-module:hook("message/full", handle_event, 1000);
-module:hook("message/host", handle_event, 1000);
-module:hook("presence/bare", handle_event, 1000);
-module:hook("presence/full", handle_event, 1000);
-module:hook("presence/host", handle_event, 1000);
-module:hook_global("shard/error", handle_shard_error);
-module:log("debug", "hooked at %s", module:get_host());
diff --git a/plugins/mod_shard_client.lua b/plugins/mod_shard_client.lua
deleted file mode 100644
index f124e75..0000000
--- a/plugins/mod_shard_client.lua
+++ /dev/null
@@ -1,230 +0,0 @@
--- Prosody IM
--- Copyright (C) 2015 Robert Norris
--- Copyright (C) 2015 Sipwise GmbH
---
--- This project is MIT/X11 licensed. Please see the
--- COPYING file in the source package for more information.
---
-module:set_global();
-local socket = require "socket"
-local ut = require "ngcp.utils";
-local logger = require "util.logger";
-local st = require "util.stanza";
-local new_xmpp_stream = require "util.xmppstream".new;
-local wrapclient = require "net.server".wrapclient;
-local log = module._log;
-local shard_name = module:get_option("shard_name", nil);
-if not shard_name then
- error("shard_name not configured", 0);
-end
-local opt_keepalives = module:get_option_boolean("shard_tcp_keepalives", module:get_option_boolean("tcp_keepalives", true));
-
-local conns = {};
-local queue = {};
-
-local listener = {};
-
-local sessions = module:shared("sessions");
-
-local xmlns_shard = 'prosody:shard';
-local stream_callbacks = { default_ns = xmlns_shard };
-
-local xmlns_xmpp_streams = "urn:ietf:params:xml:ns:xmpp-streams";
-
-function stream_callbacks.error(session, error, data, _)
- if session.destroyed then return; end
- module:log("warn", "Error processing shard stream: %s", tostring(error));
- if error == "no-stream" then
- session:close("invalid-namespace");
- elseif error == "parse-error" then
- session.log("warn", "External shard %s XML parse error: %s", tostring(session.host), tostring(data));
- session:close("not-well-formed");
- elseif error == "stream-error" then
- local condition, text = "undefined-condition";
- for child in data:children() do
- if child.attr.xmlns == xmlns_xmpp_streams then
- if child.name ~= "text" then
- condition = child.name;
- else
- text = child:get_text();
- end
- if condition ~= "undefined-condition" and text then
- break;
- end
- end
- end
- text = condition .. (text and (" ("..text..")") or "");
- session.log("info", "Session closed by remote with error: %s", text);
- session:close(nil, text);
- end
-end
-
-function stream_callbacks.streamclosed(session)
- session.log("debug", "Received ");
- session:close();
-end
-
-local stream_xmlns_attr = {xmlns='urn:ietf:params:xml:ns:xmpp-streams'};
-local default_stream_attr = { ["xmlns:stream"] = "http://etherx.jabber.org/streams", xmlns = stream_callbacks.default_ns, version = "1.0", id = "" };
-local function session_close(session, reason)
- if session.destroyed then return; end
- if session.conn then
- if session.notopen then
- session.send("");
- session.send(st.stanza("stream:stream", default_stream_attr):top_tag());
- end
- if reason then
- if type(reason) == "string" then -- assume stream error
- module:log("info", "Disconnecting component, is: %s", reason);
- session.send(st.stanza("stream:error"):tag(reason, {xmlns = 'urn:ietf:params:xml:ns:xmpp-streams' }));
- elseif type(reason) == "table" then
- if reason.condition then
- local stanza = st.stanza("stream:error"):tag(reason.condition, stream_xmlns_attr):up();
- if reason.text then
- stanza:tag("text", stream_xmlns_attr):text(reason.text):up();
- end
- if reason.extra then
- stanza:add_child(reason.extra);
- end
- module:log("info", "Disconnecting component, is: %s", tostring(stanza));
- session.send(stanza);
- elseif reason.name then -- a stanza
- module:log("info", "Disconnecting component, is: %s", tostring(reason));
- session.send(reason);
- end
- end
- end
- session.send("");
- session.conn:close();
- listener.ondisconnect(session.conn, "stream error");
- end
-end
-
-function listener.onconnect(conn)
- local shard = conn:ip();
-
- local session = { type = "shard", conn = conn, send = function (data) return conn:write(tostring(data)); end, shard = shard };
-
- -- Logging functions --
- local conn_name = "sc"..tostring(session):match("[a-f0-9]+$");
- session.log = logger.init(conn_name);
- session.close = session_close;
-
- if opt_keepalives then
- conn:setoption("keepalive", opt_keepalives);
- end
-
- session.log("info", "outgoing shard connection");
-
- local stream = new_xmpp_stream(session, stream_callbacks);
- session.stream = stream;
-
- function session.data(_, data)
- local ok, err = stream:feed(data);
- if ok then return; end
- module:log("debug", "Received invalid XML (%s) %d bytes: %s", tostring(err), #data, data:sub(1, 300):gsub("[\r\n]+", " "):gsub("[%z\1-\31]", "_"));
- session:close("not-well-formed");
- end
-
- session.dispatch_stanza = stream_callbacks.handlestanza;
-
- session.notopen = true;
- session.send(st.stanza("stream:stream", {
- to = conn:ip(),
- ["xmlns:stream"] = 'http://etherx.jabber.org/streams';
- xmlns = xmlns_shard;
- }):top_tag());
-
- local _queue = queue[shard];
- for _,s in pairs(_queue) do
- conn:write(tostring(s));
- end
- queue[shard] = nil;
-
- sessions[conn] = session;
-end
-function listener.onincoming(conn, data)
- local session = sessions[conn];
- session.data(conn, data);
-end
-function listener.ondisconnect(conn, err)
- local session = sessions[conn];
-
- if (session) then
- (session.log or log)("info", "shard disconnected: %s (%s)", tostring(session.shard), tostring(err));
- if session.on_destroy then session:on_destroy(err); end
- sessions[conn] = nil;
- conns[session.shard] = nil;
- for k in pairs(session) do
- if k ~= "log" and k ~= "close" then
- session[k] = nil;
- end
- end
- session.destroyed = true;
- end
-
- module:log("error", "connection lost");
- module:fire_event("shard/disconnected", { reason = err, shard = conn:ip() });
-end
-
-local function connect(shard)
- local conn = socket.tcp ( )
- conn:settimeout ( 10 )
- local ok, err = conn:connect (shard, 7473)
- if not ok and err ~= "timeout" then
- return nil, err;
- end
-
- local handler, _ = wrapclient ( conn , shard , 7473 , listener , "*a")
- return handler;
-end
-
-module:hook_global("server-stopping", function(event)
- local reason = event.reason;
- for _, session in pairs(sessions) do
- session:close{ condition = "system-shutdown", text = reason };
- end
-end, 1000);
-
-local function handle_send(event)
- local shard = event.shard;
- local stanza = event.stanza;
-
- module:log("debug", "got stanza for shard "..shard)
-
- local conn = conns[shard];
- if conn == nil then
- module:log("debug", "connecting to "..shard.." for delivery");
- local err;
- conn, err = connect(shard);
- if not conn then
- module:log("error", "couldn't connect to "..shard..": "..err);
- module:fire_event("shard/error", {shard = shard, stanza = stanza});
- return;
- end
- conns[shard] = conn;
- queue[shard] = {};
- end
- if stanza.attr.via then
- local via = ut.explode(';', stanza.attr.via);
- module:log("debug", "via:%s", ut.table.tostring(via));
- if ut.table.contains(via, shard_name) then
- module:log("error", "loop detected, stanza[%s]", stanza);
- return;
- end
- table.insert(via, shard_name);
- stanza.attr.via = ut.implode(';', via);
- else
- stanza.attr.via = shard_name;
- end
- module:log("debug", "new via:%s", stanza.attr.via);
-
- local session = sessions[conn]
- if session == nil then
- table.insert(queue[shard], stanza)
- else
- conn:write(tostring(stanza));
- end
-end
-
-module:hook_global("shard/send", handle_send, 1000);
diff --git a/plugins/mod_shard_server.lua b/plugins/mod_shard_server.lua
deleted file mode 100644
index a3e44e1..0000000
--- a/plugins/mod_shard_server.lua
+++ /dev/null
@@ -1,208 +0,0 @@
--- Prosody IM
--- Copyright (C) 2015 Robert Norris
--- Copyright (C) 2015 Sipwise GmbH
---
--- This project is MIT/X11 licensed. Please see the
--- COPYING file in the source package for more information.
---
-
-module:set_global();
-local logger = require "util.logger"
-local log = logger.init("shard_server");
-local st = require "util.stanza";
-local jid_split = require "util.jid".split;
-
-local new_xmpp_stream = require "util.xmppstream".new;
-local uuid_gen = require "util.uuid".generate;
-
-local core_process_stanza = prosody.core_process_stanza;
-local hosts = prosody.hosts;
-
-local traceback = debug.traceback;
-
-local shard_name = module:get_option("shard_name", nil);
-if not shard_name then
- error("shard_name not configured", 0);
-end
-
-local opt_keepalives = module:get_option_boolean("shard_tcp_keepalives", module:get_option_boolean("tcp_keepalives", true));
-
-local sessions = module:shared("sessions");
-
---- Network and stream part ---
-
-local xmlns_shard = 'prosody:shard';
-
-local listener = {};
-
---- Callbacks/data for xmppstream to handle streams for us ---
-
-local stream_callbacks = { default_ns = xmlns_shard };
-
-local xmlns_xmpp_streams = "urn:ietf:params:xml:ns:xmpp-streams";
-
-function stream_callbacks.error(session, error, data, _)
- if session.destroyed then return; end
- module:log("warn", "Error processing component stream: %s", tostring(error));
- if error == "no-stream" then
- session:close("invalid-namespace");
- elseif error == "parse-error" then
- session.log("warn", "External component %s XML parse error: %s", tostring(session.host), tostring(data));
- session:close("not-well-formed");
- elseif error == "stream-error" then
- local condition, text = "undefined-condition";
- for child in data:children() do
- if child.attr.xmlns == xmlns_xmpp_streams then
- if child.name ~= "text" then
- condition = child.name;
- else
- text = child:get_text();
- end
- if condition ~= "undefined-condition" and text then
- break;
- end
- end
- end
- text = condition .. (text and (" ("..text..")") or "");
- session.log("info", "Session closed by remote with error: %s", text);
- session:close(nil, text);
- end
-end
-
-function stream_callbacks.streamopened(session, attr)
- if attr.to ~= shard_name then
- session:close{ condition = "host-unknown", text = "unknown shard name "..tostring(attr.to) };
- return;
- end
- session.host = attr.to;
- session.streamid = uuid_gen();
- session.notopen = nil;
-end
-
-function stream_callbacks.streamclosed(session)
- session.log("debug", "Received ");
- session:close();
-end
-
-local function handleerr(err) log("error", "Traceback[component]: %s", traceback(tostring(err), 2)); end
-function stream_callbacks.handlestanza(_, stanza)
- local to = stanza.attr.to;
- local _, host = jid_split(to);
-
- if not host then return end
-
- local h = hosts[host];
- local nh = {};
- for k,v in pairs(h) do nh[k] = v; end
- nh.type = "component";
-
- return xpcall(function () return core_process_stanza(nh, stanza) end, handleerr);
-end
-
---- Closing a component connection
-local stream_xmlns_attr = {xmlns='urn:ietf:params:xml:ns:xmpp-streams'};
-local default_stream_attr = { ["xmlns:stream"] = "http://etherx.jabber.org/streams", xmlns = stream_callbacks.default_ns, version = "1.0", id = "" };
-local function session_close(session, reason)
- if session.destroyed then return; end
- if session.conn then
- if session.notopen then
- session.send("");
- session.send(st.stanza("stream:stream", default_stream_attr):top_tag());
- end
- if reason then
- if type(reason) == "string" then -- assume stream error
- module:log("info", "Disconnecting component, is: %s", reason);
- session.send(st.stanza("stream:error"):tag(reason, {xmlns = 'urn:ietf:params:xml:ns:xmpp-streams' }));
- elseif type(reason) == "table" then
- if reason.condition then
- local stanza = st.stanza("stream:error"):tag(reason.condition, stream_xmlns_attr):up();
- if reason.text then
- stanza:tag("text", stream_xmlns_attr):text(reason.text):up();
- end
- if reason.extra then
- stanza:add_child(reason.extra);
- end
- module:log("info", "Disconnecting component, is: %s", tostring(stanza));
- session.send(stanza);
- elseif reason.name then -- a stanza
- module:log("info", "Disconnecting component, is: %s", tostring(reason));
- session.send(reason);
- end
- end
- end
- session.send("");
- session.conn:close();
- listener.ondisconnect(session.conn, "stream error");
- end
-end
-
---- Component connlistener
-
-function listener.onconnect(conn)
- local _send = conn.write;
- local session = { type = "shard", conn = conn, send = function (data) return _send(conn, tostring(data)); end };
-
- -- Logging functions --
- local conn_name = "ss"..tostring(session):match("[a-f0-9]+$");
- session.log = logger.init(conn_name);
- session.close = session_close;
-
- if opt_keepalives then
- conn:setoption("keepalive", opt_keepalives);
- end
-
- session.log("info", "incoming shard connection");
-
- local stream = new_xmpp_stream(session, stream_callbacks);
- session.stream = stream;
-
- session.notopen = true;
-
- function session.reset_stream()
- session.notopen = true;
- session.stream:reset();
- end
-
- function session.data(_, data)
- local ok, err = stream:feed(data);
- if ok then return; end
- module:log("debug", "Received invalid XML (%s) %d bytes: %s", tostring(err), #data, data:sub(1, 300):gsub("[\r\n]+", " "):gsub("[%z\1-\31]", "_"));
- session:close("not-well-formed");
- end
-
- session.dispatch_stanza = stream_callbacks.handlestanza;
-
- sessions[conn] = session;
-end
-function listener.onincoming(conn, data)
- local session = sessions[conn];
- session.data(conn, data);
-end
-function listener.ondisconnect(conn, err)
- local session = sessions[conn];
- if session then
- (session.log or log)("info", "component disconnected: %s (%s)", tostring(session.host), tostring(err));
- if session.on_destroy then session:on_destroy(err); end
- sessions[conn] = nil;
- for k in pairs(session) do
- if k ~= "log" and k ~= "close" then
- session[k] = nil;
- end
- end
- session.destroyed = true;
- end
-end
-
-function listener.ondetach(conn)
- sessions[conn] = nil;
-end
-
-module:provides("net", {
- name = "shard";
- private = true;
- listener = listener;
- default_port = 7473;
- multiplex = {
- pattern = "^<.*:stream.*%sxmlns%s*=%s*(['\"])"..xmlns_shard.."%1.*>";
- };
-});
diff --git a/plugins/mod_sipwise_admin_telnet.lua b/plugins/mod_sipwise_admin_telnet.lua
deleted file mode 100644
index 359d236..0000000
--- a/plugins/mod_sipwise_admin_telnet.lua
+++ /dev/null
@@ -1,1123 +0,0 @@
--- Prosody IM
--- Copyright (C) 2008-2010 Matthew Wild
--- Copyright (C) 2008-2010 Waqas Hussain
--- Copyright (C) 2013 Sipwise GmbH
---
--- This project is MIT/X11 licensed. Please see the
--- COPYING file in the source package for more information.
---
-
-module:set_global();
-
-local hostmanager = require "core.hostmanager";
-local modulemanager = require "core.modulemanager";
-local s2smanager = require "core.s2smanager";
-local portmanager = require "core.portmanager";
-
-local _G = _G;
-
-local prosody = _G.prosody;
-local hosts = prosody.hosts;
-local incoming_s2s = prosody.incoming_s2s;
-
-local console_listener = {
- default_port = module:get_option_number("console_port", 5582);
- default_mode = "*a";
- interface = module:get_option_string("console_interface", "127.0.0.1");
-};
-
-local iterators = require "util.iterators";
-local keys, values = iterators.keys, iterators.values;
-local jid_bare, jid_split = import("util.jid", "bare", "prepped_split");
-local set, array = require "util.set", require "util.array";
-local cert_verify_identity = require "util.x509".verify_identity;
-local envload = require "util.envload".envload;
-local envloadfile = require "util.envload".envloadfile;
-
-local commands = module:shared("commands")
-local def_env = module:shared("env");
-local default_env_mt = { __index = def_env };
-local core_post_stanza = prosody.core_post_stanza;
-
-local function redirect_output(_G, session)
- local env = setmetatable({ print = session.print }, { __index = function (t, k) return rawget(_G, k); end });
- env.dofile = function(name)
- local f, err = envloadfile(name, env);
- if not f then return f, err; end
- return f();
- end;
- return env;
-end
-
-console = {};
-
-function console:new_session(conn)
- local w = function(s) conn:write(s:gsub("\n", "\r\n")); end;
- local session = { conn = conn;
- send = function (t) w(tostring(t)); end;
- print = function (...)
- local t = {};
- for i=1,select("#", ...) do
- t[i] = tostring(select(i, ...));
- end
- w("| "..table.concat(t, "\t").."\n");
- end;
- disconnect = function () conn:close(); end;
- };
- session.env = setmetatable({}, default_env_mt);
-
- -- Load up environment with helper objects
- for name, t in pairs(def_env) do
- if type(t) == "table" then
- session.env[name] = setmetatable({ session = session }, { __index = t });
- end
- end
-
- return session;
-end
-
-function console:process_line(session, line)
- local useglobalenv;
-
- if line:match("^>") then
- line = line:gsub("^>", "");
- useglobalenv = true;
- elseif line == "\004" then
- commands["bye"](session, line);
- return;
- else
- local command = line:match("^%w+") or line:match("%p");
- if commands[command] then
- commands[command](session, line);
- return;
- end
- end
-
- session.env._ = line;
-
- local chunkname = "=console";
- local env = (useglobalenv and redirect_output(_G, session)) or session.env or nil
- local chunk, err = envload("return "..line, chunkname, env);
- if not chunk then
- chunk, err = envload(line, chunkname, env);
- if not chunk then
- err = err:gsub("^%[string .-%]:%d+: ", "");
- err = err:gsub("^:%d+: ", "");
- err = err:gsub("''", "the end of the line");
- session.print("Sorry, I couldn't understand that... "..err);
- return;
- end
- end
-
- local ranok, taskok, message = pcall(chunk);
-
- if not (ranok or message or useglobalenv) and commands[line:lower()] then
- commands[line:lower()](session, line);
- return;
- end
-
- if not ranok then
- session.print("Fatal error while running command, it did not complete");
- session.print("Error: "..taskok);
- return;
- end
-
- if not message then
- session.print("Result: "..tostring(taskok));
- return;
- elseif (not taskok) and message then
- session.print("Command completed with a problem");
- session.print("Message: "..tostring(message));
- return;
- end
-
- session.print("OK: "..tostring(message));
-end
-
-local sessions = {};
-
-function console_listener.onconnect(conn)
- -- Handle new connection
- local session = console:new_session(conn);
- sessions[conn] = session;
- printbanner(session);
- session.send(string.char(0));
-end
-
-function console_listener.onincoming(conn, data)
- local session = sessions[conn];
-
- local partial = session.partial_data;
- if partial then
- data = partial..data;
- end
-
- for line in data:gmatch("[^\n]*[\n\004]") do
- if session.closed then return end
- console:process_line(session, line);
- session.send(string.char(0));
- end
- session.partial_data = data:match("[^\n]+$");
-end
-
-function console_listener.ondisconnect(conn, err)
- local session = sessions[conn];
- if session then
- session.disconnect();
- sessions[conn] = nil;
- end
-end
-
-function console_listener.ondetach(conn)
- sessions[conn] = nil;
-end
-
--- Console commands --
--- These are simple commands, not valid standalone in Lua
-
-function commands.bye(session)
- session.print("See you! :)");
- session.closed = true;
- session.disconnect();
-end
-commands.quit, commands.exit = commands.bye, commands.bye;
-
-commands["!"] = function (session, data)
- if data:match("^!!") and session.env._ then
- session.print("!> "..session.env._);
- return console_listener.onincoming(session.conn, session.env._);
- end
- local old, new = data:match("^!(.-[^\\])!(.-)!$");
- if old and new then
- local ok, res = pcall(string.gsub, session.env._, old, new);
- if not ok then
- session.print(res)
- return;
- end
- session.print("!> "..res);
- return console_listener.onincoming(session.conn, res);
- end
- session.print("Sorry, not sure what you want");
-end
-
-
-function commands.help(session, data)
- local print = session.print;
- local section = data:match("^help (%w+)");
- if not section then
- print [[Commands are divided into multiple sections. For help on a particular section, ]]
- print [[type: help SECTION (for example, 'help c2s'). Sections are: ]]
- print [[]]
- print [[c2s - Commands to manage local client-to-server sessions]]
- print [[s2s - Commands to manage sessions between this server and others]]
- print [[module - Commands to load/reload/unload modules/plugins]]
- print [[host - Commands to activate, deactivate and list virtual hosts]]
- print [[user - Commands to create and delete users, and change their passwords]]
- print [[server - Uptime, version, shutting down, etc.]]
- print [[port - Commands to manage ports the server is listening on]]
- print [[dns - Commands to manage and inspect the internal DNS resolver]]
- print [[config - Reloading the configuration, etc.]]
- print [[console - Help regarding the console itself]]
- elseif section == "c2s" then
- print [[c2s:show(jid) - Show all client sessions with the specified JID (or all if no JID given)]]
- print [[c2s:show_insecure() - Show all unencrypted client connections]]
- print [[c2s:show_secure() - Show all encrypted client connections]]
- print [[c2s:close(jid) - Close all sessions for the specified JID]]
- elseif section == "s2s" then
- print [[s2s:show(domain) - Show all s2s connections for the given domain (or all if no domain given)]]
- print [[s2s:close(from, to) - Close a connection from one domain to another]]
- print [[s2s:closeall(host) - Close all the incoming/outgoing s2s sessions to specified host]]
- elseif section == "module" then
- print [[module:load(module, host) - Load the specified module on the specified host (or all hosts if none given)]]
- print [[module:reload(module, host) - The same, but unloads and loads the module (saving state if the module supports it)]]
- print [[module:unload(module, host) - The same, but just unloads the module from memory]]
- print [[module:list(host) - List the modules loaded on the specified host]]
- elseif section == "host" then
- print [[host:activate(hostname) - Activates the specified host]]
- print [[host:deactivate(hostname) - Disconnects all clients on this host and deactivates]]
- print [[host:list() - List the currently-activated hosts]]
- elseif section == "user" then
- print [[user:create(jid, password) - Create the specified user account]]
- print [[user:password(jid, password) - Set the password for the specified user account]]
- print [[user:delete(jid) - Permanently remove the specified user account]]
- print [[user:list(hostname, pattern) - List users on the specified host, optionally filtering with a pattern]]
- elseif section == "server" then
- print [[server:version() - Show the server's version number]]
- print [[server:uptime() - Show how long the server has been running]]
- print [[server:memory() - Show details about the server's memory usage]]
- print [[server:shutdown(reason) - Shut down the server, with an optional reason to be broadcast to all connections]]
- elseif section == "port" then
- print [[port:list() - Lists all network ports prosody currently listens on]]
- print [[port:close(port, interface) - Close a port]]
- elseif section == "dns" then
- print [[dns:lookup(name, type, class) - Do a DNS lookup]]
- print [[dns:addnameserver(nameserver) - Add a nameserver to the list]]
- print [[dns:setnameserver(nameserver) - Replace the list of name servers with the supplied one]]
- print [[dns:purge() - Clear the DNS cache]]
- print [[dns:cache() - Show cached records]]
- elseif section == "config" then
- print [[config:reload() - Reload the server configuration. Modules may need to be reloaded for changes to take effect.]]
- elseif section == "console" then
- print [[Hey! Welcome to Prosody's admin console.]]
- print [[First thing, if you're ever wondering how to get out, simply type 'quit'.]]
- print [[Secondly, note that we don't support the full telnet protocol yet (it's coming)]]
- print [[so you may have trouble using the arrow keys, etc. depending on your system.]]
- print [[]]
- print [[For now we offer a couple of handy shortcuts:]]
- print [[!! - Repeat the last command]]
- print [[!old!new! - repeat the last command, but with 'old' replaced by 'new']]
- print [[]]
- print [[For those well-versed in Prosody's internals, or taking instruction from those who are,]]
- print [[you can prefix a command with > to escape the console sandbox, and access everything in]]
- print [[the running server. Great fun, but be careful not to break anything :)]]
- end
- print [[]]
-end
-
--- Session environment --
--- Anything in def_env will be accessible within the session as a global variable
-
-def_env.server = {};
-
-function def_env.server:insane_reload()
- prosody.unlock_globals();
- dofile "prosody"
- prosody = _G.prosody;
- return true, "Server reloaded";
-end
-
-function def_env.server:version()
- return true, tostring(prosody.version or "unknown");
-end
-
-function def_env.server:uptime()
- local t = os.time()-prosody.start_time;
- local seconds = t%60;
- t = (t - seconds)/60;
- local minutes = t%60;
- t = (t - minutes)/60;
- local hours = t%24;
- t = (t - hours)/24;
- local days = t;
- return true, string.format("This server has been running for %d day%s, %d hour%s and %d minute%s (since %s)",
- days, (days ~= 1 and "s") or "", hours, (hours ~= 1 and "s") or "",
- minutes, (minutes ~= 1 and "s") or "", os.date("%c", prosody.start_time));
-end
-
-function def_env.server:shutdown(reason)
- prosody.shutdown(reason);
- return true, "Shutdown initiated";
-end
-
-local function human(kb)
- local unit = "K";
- if kb > 1024 then
- kb, unit = kb/1024, "M";
- end
- return ("%0.2f%sB"):format(kb, unit);
-end
-
-function def_env.server:memory()
- local pposix = require("util.pposix");
- if not pposix.meminfo then
- return true, "Lua is using "..collectgarbage("count");
- end
- local mem, lua_mem = pposix.meminfo(), collectgarbage("count");
- local print = self.session.print;
- print("Process: "..human((mem.allocated+mem.allocated_mmap)/1024));
- print(" Used: "..human(mem.used/1024).." ("..human(lua_mem).." by Lua)");
- print(" Free: "..human(mem.unused/1024).." ("..human(mem.returnable/1024).." returnable)");
- return true, "OK";
-end
-
-def_env.module = {};
-
-local function get_hosts_set(hosts, module)
- if type(hosts) == "table" then
- if hosts[1] then
- return set.new(hosts);
- elseif hosts._items then
- return hosts;
- end
- elseif type(hosts) == "string" then
- return set.new { hosts };
- elseif hosts == nil then
- local mm = require "modulemanager";
- local hosts_set = set.new(array.collect(keys(prosody.hosts)))
- / function (host) return (prosody.hosts[host].type == "local" or module and mm.is_loaded(host, module)) and host or nil; end;
- if module and mm.get_module("*", module) then
- hosts_set:add("*");
- end
- return hosts_set;
- end
-end
-
-function def_env.module:load(name, hosts, config)
- local mm = require "modulemanager";
-
- hosts = get_hosts_set(hosts);
-
- -- Load the module for each host
- local ok, err, count, mod = true, nil, 0, nil;
- for host in hosts do
- if (not mm.is_loaded(host, name)) then
- mod, err = mm.load(host, name, config);
- if not mod then
- ok = false;
- if err == "global-module-already-loaded" then
- if count > 0 then
- ok, err, count = true, nil, 1;
- end
- break;
- end
- self.session.print(err or "Unknown error loading module");
- else
- count = count + 1;
- self.session.print("Loaded for "..mod.module.host);
- end
- end
- end
-
- return ok, (ok and "Module loaded onto "..count.." host"..(count ~= 1 and "s" or "")) or ("Last error: "..tostring(err));
-end
-
-function def_env.module:unload(name, hosts)
- local mm = require "modulemanager";
-
- hosts = get_hosts_set(hosts, name);
-
- -- Unload the module for each host
- local ok, err, count = true, nil, 0;
- for host in hosts do
- if mm.is_loaded(host, name) then
- ok, err = mm.unload(host, name);
- if not ok then
- ok = false;
- self.session.print(err or "Unknown error unloading module");
- else
- count = count + 1;
- self.session.print("Unloaded from "..host);
- end
- end
- end
- return ok, (ok and "Module unloaded from "..count.." host"..(count ~= 1 and "s" or "")) or ("Last error: "..tostring(err));
-end
-
-function def_env.module:reload(name, hosts)
- local mm = require "modulemanager";
-
- hosts = array.collect(get_hosts_set(hosts, name)):sort(function (a, b)
- if a == "*" then return true
- elseif b == "*" then return false
- else return a < b; end
- end);
-
- -- Reload the module for each host
- local ok, err, count = true, nil, 0;
- for _, host in ipairs(hosts) do
- if mm.is_loaded(host, name) then
- ok, err = mm.reload(host, name);
- if not ok then
- ok = false;
- self.session.print(err or "Unknown error reloading module");
- else
- count = count + 1;
- if ok == nil then
- ok = true;
- end
- self.session.print("Reloaded on "..host);
- end
- end
- end
- return ok, (ok and "Module reloaded on "..count.." host"..(count ~= 1 and "s" or "")) or ("Last error: "..tostring(err));
-end
-
-function def_env.module:list(hosts)
- if hosts == nil then
- hosts = array.collect(keys(prosody.hosts));
- table.insert(hosts, 1, "*");
- end
- if type(hosts) == "string" then
- hosts = { hosts };
- end
- if type(hosts) ~= "table" then
- return false, "Please supply a host or a list of hosts you would like to see";
- end
-
- local print = self.session.print;
- for _, host in ipairs(hosts) do
- print((host == "*" and "Global" or host)..":");
- local modules = array.collect(keys(modulemanager.get_modules(host) or {})):sort();
- if #modules == 0 then
- if prosody.hosts[host] then
- print(" No modules loaded");
- else
- print(" Host not found");
- end
- else
- for _, name in ipairs(modules) do
- print(" "..name);
- end
- end
- end
-end
-
-def_env.config = {};
-function def_env.config:load(filename, format)
- local config_load = require "core.configmanager".load;
- local ok, err = config_load(filename, format);
- if not ok then
- return false, err or "Unknown error loading config";
- end
- return true, "Config loaded";
-end
-
-function def_env.config:get(host, section, key)
- local config_get = require "core.configmanager".get
- return true, tostring(config_get(host, section, key));
-end
-
-function def_env.config:reload()
- local ok, err = prosody.reload_config();
- return ok, (ok and "Config reloaded (you may need to reload modules to take effect)") or tostring(err);
-end
-
-def_env.hosts = {};
-function def_env.hosts:list()
- for host, host_session in pairs(hosts) do
- self.session.print(host);
- end
- return true, "Done";
-end
-
-function def_env.hosts:add(name)
-end
-
-def_env.c2s = {};
-
-local function show_c2s(callback)
- for hostname, host in pairs(hosts) do
- for username, user in pairs(host.sessions or {}) do
- for resource, session in pairs(user.sessions or {}) do
- local jid = username.."@"..hostname.."/"..resource;
- callback(jid, session);
- end
- end
- end
-end
-
-function def_env.c2s:count(match_jid)
- local count = 0;
- show_c2s(function (jid, session)
- if (not match_jid) or jid:match(match_jid) then
- count = count + 1;
- end
- end);
- return true, "Total: "..count.." clients";
-end
-
-function def_env.c2s:show(match_jid)
- local print, count = self.session.print, 0;
- local curr_host;
- show_c2s(function (jid, session)
- if curr_host ~= session.host then
- curr_host = session.host;
- print(curr_host);
- end
- if (not match_jid) or jid:match(match_jid) then
- count = count + 1;
- local status, priority = "unavailable", tostring(session.priority or "-");
- if session.presence then
- status = session.presence:child_with_name("show");
- if status then
- status = status:get_text() or "[invalid!]";
- else
- status = "available";
- end
- end
- print(" "..jid.." - "..status.."("..priority..")");
- end
- end);
- return true, "Total: "..count.." clients";
-end
-
-function def_env.c2s:show_insecure(match_jid)
- local print, count = self.session.print, 0;
- show_c2s(function (jid, session)
- if ((not match_jid) or jid:match(match_jid)) and not session.secure then
- count = count + 1;
- print(jid);
- end
- end);
- return true, "Total: "..count.." insecure client connections";
-end
-
-function def_env.c2s:show_secure(match_jid)
- local print, count = self.session.print, 0;
- show_c2s(function (jid, session)
- if ((not match_jid) or jid:match(match_jid)) and session.secure then
- count = count + 1;
- print(jid);
- end
- end);
- return true, "Total: "..count.." secure client connections";
-end
-
-function def_env.c2s:close(match_jid)
- local count = 0;
- show_c2s(function (jid, session)
- if jid == match_jid or jid_bare(jid) == match_jid then
- count = count + 1;
- session:close();
- end
- end);
- return true, "Total: "..count.." sessions closed";
-end
-
-local function session_flags(session, line)
- if session.cert_identity_status == "valid" then
- line[#line+1] = "(secure)";
- elseif session.secure then
- line[#line+1] = "(encrypted)";
- end
- if session.compressed then
- line[#line+1] = "(compressed)";
- end
- if session.smacks then
- line[#line+1] = "(sm)";
- end
- if session.conn and session.conn:ip():match(":") then
- line[#line+1] = "(IPv6)";
- end
- return table.concat(line, " ");
-end
-
-def_env.s2s = {};
-function def_env.s2s:show(match_jid)
- local _print = self.session.print;
- local print = self.session.print;
-
- local count_in, count_out = 0,0;
-
- for host, host_session in pairs(hosts) do
- print = function (...) _print(host); _print(...); print = _print; end
- for remotehost, session in pairs(host_session.s2sout) do
- if (not match_jid) or remotehost:match(match_jid) or host:match(match_jid) then
- count_out = count_out + 1;
- print(session_flags(session, {" ", host, "->", remotehost}));
- if session.sendq then
- print(" There are "..#session.sendq.." queued outgoing stanzas for this connection");
- end
- if session.type == "s2sout_unauthed" then
- if session.connecting then
- print(" Connection not yet established");
- if not session.srv_hosts then
- if not session.conn then
- print(" We do not yet have a DNS answer for this host's SRV records");
- else
- print(" This host has no SRV records, using A record instead");
- end
- elseif session.srv_choice then
- print(" We are on SRV record "..session.srv_choice.." of "..#session.srv_hosts);
- local srv_choice = session.srv_hosts[session.srv_choice];
- print(" Using "..(srv_choice.target or ".")..":"..(srv_choice.port or 5269));
- end
- elseif session.notopen then
- print(" The has not yet been opened");
- elseif not session.dialback_key then
- print(" Dialback has not been initiated yet");
- elseif session.dialback_key then
- print(" Dialback has been requested, but no result received");
- end
- end
- end
- end
- local subhost_filter = function (h)
- return (match_jid and h:match(match_jid));
- end
- for session in pairs(incoming_s2s) do
- if session.to_host == host and ((not match_jid) or host:match(match_jid)
- or (session.from_host and session.from_host:match(match_jid))
- -- Pft! is what I say to list comprehensions
- or (session.hosts and #array.collect(keys(session.hosts)):filter(subhost_filter)>0)) then
- count_in = count_in + 1;
- print(session_flags(session, {" ", host, "<-", session.from_host or "(unknown)"}));
- if session.type == "s2sin_unauthed" then
- print(" Connection not yet authenticated");
- end
- for name in pairs(session.hosts) do
- if name ~= session.from_host then
- print(" also hosts "..tostring(name));
- end
- end
- end
- end
-
- print = _print;
- end
-
- for session in pairs(incoming_s2s) do
- if not session.to_host and ((not match_jid) or session.from_host and session.from_host:match(match_jid)) then
- count_in = count_in + 1;
- print("Other incoming s2s connections");
- print(" (unknown) <- "..(session.from_host or "(unknown)"));
- end
- end
-
- return true, "Total: "..count_out.." outgoing, "..count_in.." incoming connections";
-end
-
-local function print_subject(print, subject)
- for _, entry in ipairs(subject) do
- print(
- (" %s: %q"):format(
- entry.name or entry.oid,
- entry.value:gsub("[\r\n%z%c]", " ")
- )
- );
- end
-end
-
--- As much as it pains me to use the 0-based depths that OpenSSL does,
--- I think there's going to be more confusion among operators if we
--- break from that.
-local function print_errors(print, errors)
- for depth, t in pairs(errors) do
- print(
- (" %d: %s"):format(
- depth-1,
- table.concat(t, "\n| ")
- )
- );
- end
-end
-
-function def_env.s2s:showcert(domain)
- local ser = require "util.serialization".serialize;
- local print = self.session.print;
- local domain_sessions = set.new(array.collect(keys(incoming_s2s)))
- /function(session) return session.from_host == domain and session or nil; end;
- for local_host in values(prosody.hosts) do
- local s2sout = local_host.s2sout;
- if s2sout and s2sout[domain] then
- domain_sessions:add(s2sout[domain]);
- end
- end
- local cert_set = {};
- for session in domain_sessions do
- local conn = session.conn;
- conn = conn and conn:socket();
- if not conn.getpeerchain then
- if conn.dohandshake then
- error("This version of LuaSec does not support certificate viewing");
- end
- else
- local cert = conn:getpeercertificate();
- if cert then
- local certs = conn:getpeerchain();
- local digest = cert:digest("sha1");
- if not cert_set[digest] then
- local chain_valid, chain_errors = conn:getpeerverification();
- cert_set[digest] = {
- {
- from = session.from_host,
- to = session.to_host,
- direction = session.direction
- };
- chain_valid = chain_valid;
- chain_errors = chain_errors;
- certs = certs;
- };
- else
- table.insert(cert_set[digest], {
- from = session.from_host,
- to = session.to_host,
- direction = session.direction
- });
- end
- end
- end
- end
- local domain_certs = array.collect(values(cert_set));
- -- Phew. We now have a array of unique certificates presented by domain.
- local n_certs = #domain_certs;
-
- if n_certs == 0 then
- return "No certificates found for "..domain;
- end
-
- local function _capitalize_and_colon(byte)
- return string.upper(byte)..":";
- end
- local function pretty_fingerprint(hash)
- return hash:gsub("..", _capitalize_and_colon):sub(1, -2);
- end
-
- for cert_info in values(domain_certs) do
- local certs = cert_info.certs;
- local cert = certs[1];
- print("---")
- print("Fingerprint (SHA1): "..pretty_fingerprint(cert:digest("sha1")));
- print("");
- local n_streams = #cert_info;
- print("Currently used on "..n_streams.." stream"..(n_streams==1 and "" or "s")..":");
- for _, stream in ipairs(cert_info) do
- if stream.direction == "incoming" then
- print(" "..stream.to.." <- "..stream.from);
- else
- print(" "..stream.from.." -> "..stream.to);
- end
- end
- print("");
- local chain_valid, errors = cert_info.chain_valid, cert_info.chain_errors;
- local valid_identity = cert_verify_identity(domain, "xmpp-server", cert);
- if chain_valid then
- print("Trusted certificate: Yes");
- else
- print("Trusted certificate: No");
- print_errors(print, errors);
- end
- print("");
- print("Issuer: ");
- print_subject(print, cert:issuer());
- print("");
- print("Valid for "..domain..": "..(valid_identity and "Yes" or "No"));
- print("Subject:");
- print_subject(print, cert:subject());
- end
- print("---");
- return ("Showing "..n_certs.." certificate"
- ..(n_certs==1 and "" or "s")
- .." presented by "..domain..".");
-end
-
-function def_env.s2s:close(from, to)
- local print, count = self.session.print, 0;
-
- if not (from and to) then
- return false, "Syntax: s2s:close('from', 'to') - Closes all s2s sessions from 'from' to 'to'";
- elseif from == to then
- return false, "Both from and to are the same... you can't do that :)";
- end
-
- if hosts[from] and not hosts[to] then
- -- Is an outgoing connection
- local session = hosts[from].s2sout[to];
- if not session then
- print("No outgoing connection from "..from.." to "..to)
- else
- (session.close or s2smanager.destroy_session)(session);
- count = count + 1;
- print("Closed outgoing session from "..from.." to "..to);
- end
- elseif hosts[to] and not hosts[from] then
- -- Is an incoming connection
- for session in pairs(incoming_s2s) do
- if session.to_host == to and session.from_host == from then
- (session.close or s2smanager.destroy_session)(session);
- count = count + 1;
- end
- end
-
- if count == 0 then
- print("No incoming connections from "..from.." to "..to);
- else
- print("Closed "..count.." incoming session"..((count == 1 and "") or "s").." from "..from.." to "..to);
- end
- elseif hosts[to] and hosts[from] then
- return false, "Both of the hostnames you specified are local, there are no s2s sessions to close";
- else
- return false, "Neither of the hostnames you specified are being used on this server";
- end
-
- return true, "Closed "..count.." s2s session"..((count == 1 and "") or "s");
-end
-
-function def_env.s2s:closeall(host)
- local count = 0;
-
- if not host or type(host) ~= "string" then return false, "wrong syntax: please use s2s:closeall('hostname.tld')"; end
- if hosts[host] then
- for session in pairs(incoming_s2s) do
- if session.to_host == host then
- (session.close or s2smanager.destroy_session)(session);
- count = count + 1;
- end
- end
- for _, session in pairs(hosts[host].s2sout) do
- (session.close or s2smanager.destroy_session)(session);
- count = count + 1;
- end
- else
- for session in pairs(incoming_s2s) do
- if session.from_host == host then
- (session.close or s2smanager.destroy_session)(session);
- count = count + 1;
- end
- end
- for _, h in pairs(hosts) do
- if h.s2sout[host] then
- (h.s2sout[host].close or s2smanager.destroy_session)(h.s2sout[host]);
- count = count + 1;
- end
- end
- end
-
- if count == 0 then return false, "No sessions to close.";
- else return true, "Closed "..count.." s2s session"..((count == 1 and "") or "s"); end
-end
-
-def_env.host = {}; def_env.hosts = def_env.host;
-
-function def_env.host:activate(hostname, config)
- return hostmanager.activate(hostname, config);
-end
-function def_env.host:deactivate(hostname, reason)
- return hostmanager.deactivate(hostname, reason);
-end
-
-function def_env.host:list()
- local print = self.session.print;
- local i = 0;
- for host in values(array.collect(keys(prosody.hosts)):sort()) do
- i = i + 1;
- print(host);
- end
- return true, i.." hosts";
-end
-
-def_env.port = {};
-
-function def_env.port:list()
- local print = self.session.print;
- local services = portmanager.get_active_services().data;
- local ordered_services, n_ports = {}, 0;
- for service, interfaces in pairs(services) do
- table.insert(ordered_services, service);
- end
- table.sort(ordered_services);
- for _, service in ipairs(ordered_services) do
- local ports_list = {};
- for interface, ports in pairs(services[service]) do
- for port in pairs(ports) do
- table.insert(ports_list, "["..interface.."]:"..port);
- end
- end
- n_ports = n_ports + #ports_list;
- print(service..": "..table.concat(ports_list, ", "));
- end
- return true, #ordered_services.." services listening on "..n_ports.." ports";
-end
-
-function def_env.port:close(close_port, close_interface)
- close_port = assert(tonumber(close_port), "Invalid port number");
- local n_closed = 0;
- local services = portmanager.get_active_services().data;
- for service, interfaces in pairs(services) do
- for interface, ports in pairs(interfaces) do
- if not close_interface or close_interface == interface then
- if ports[close_port] then
- self.session.print("Closing ["..interface.."]:"..close_port.."...");
- local ok, err = portmanager.close(interface, close_port)
- if not ok then
- self.session.print("Failed to close "..interface.." "..close_port..": "..err);
- else
- n_closed = n_closed + 1;
- end
- end
- end
- end
- end
- return true, "Closed "..n_closed.." ports";
-end
-
-def_env.muc = {};
-
-local console_room_mt = {
- __index = function (self, k) return self.room[k]; end;
- __tostring = function (self)
- return "MUC room <"..self.room.jid..">";
- end;
-};
-
-local function check_muc(jid)
- local room_name, host = jid_split(jid);
- if not hosts[host] then
- return nil, "No such host: "..host;
- elseif not hosts[host].modules.muc then
- return nil, "Host '"..host.."' is not a MUC service";
- end
- return room_name, host;
-end
-
-function def_env.muc:create(room_jid)
- local room, host = check_muc(room_jid);
- if not room_name then
- return room_name, host;
- end
- if not room then return nil, host end
- if hosts[host].modules.muc.rooms[room_jid] then return nil, "Room exists already" end
- return hosts[host].modules.muc.create_room(room_jid);
-end
-
-function def_env.muc:room(room_jid)
- local room_name, host = check_muc(room_jid);
- if not room_name then
- return room_name, host;
- end
- local room_obj = hosts[host].modules.muc.rooms[room_jid];
- if not room_obj then
- return nil, "No such room: "..room_jid;
- end
- return setmetatable({ room = room_obj }, console_room_mt);
-end
-
-local um = require"core.usermanager";
-
-def_env.user = {};
-function def_env.user:create(jid, password)
- local username, host = jid_split(jid);
- if not hosts[host] then
- return nil, "No such host: "..host;
- elseif um.user_exists(username, host) then
- return nil, "User exists";
- end
- local ok, err = um.create_user(username, password, host);
- if ok then
- return true, "User created";
- else
- return nil, "Could not create user: "..err;
- end
-end
-
-function def_env.user:delete(jid)
- local username, host = jid_split(jid);
- if not hosts[host] then
- return nil, "No such host: "..host;
- elseif not um.user_exists(username, host) then
- return nil, "No such user";
- end
- local ok, err = um.delete_user(username, host);
- if ok then
- return true, "User deleted";
- else
- return nil, "Could not delete user: "..err;
- end
-end
-
-function def_env.user:password(jid, password)
- local username, host = jid_split(jid);
- if not hosts[host] then
- return nil, "No such host: "..host;
- elseif not um.user_exists(username, host) then
- return nil, "No such user";
- end
- local ok, err = um.set_password(username, password, host);
- if ok then
- return true, "User password changed";
- else
- return nil, "Could not change password for user: "..err;
- end
-end
-
-function def_env.user:list(host, pat)
- if not host then
- return nil, "No host given";
- elseif not hosts[host] then
- return nil, "No such host";
- end
- local print = self.session.print;
- local total, matches = 0, 0;
- for user in um.users(host) do
- if not pat or user:match(pat) then
- print(user.."@"..host);
- matches = matches + 1;
- end
- total = total + 1;
- end
- return true, "Showing "..(pat and (matches.." of ") or "all " )..total.." users";
-end
-
-def_env.xmpp = {};
-
-local st = require "util.stanza";
-function def_env.xmpp:ping(localhost, remotehost)
- if hosts[localhost] then
- core_post_stanza(hosts[localhost],
- st.iq{ from=localhost, to=remotehost, type="get", id="ping" }
- :tag("ping", {xmlns="urn:xmpp:ping"}));
- return true, "Sent ping";
- else
- return nil, "No such host";
- end
-end
-
-def_env.dns = {};
-local adns = require"net.adns";
-local dns = require"net.dns";
-
-function def_env.dns:lookup(name, typ, class)
- local ret = "Query sent";
- local print = self.session.print;
- local function handler(...)
- ret = "Got response";
- print(...);
- end
- adns.lookup(handler, name, typ, class);
- return true, ret;
-end
-
-function def_env.dns:addnameserver(...)
- dns._resolver:addnameserver(...)
- return true
-end
-
-function def_env.dns:setnameserver(...)
- dns._resolver:setnameserver(...)
- return true
-end
-
-function def_env.dns:purge()
- dns.purge()
- return true
-end
-
-function def_env.dns:cache()
- return true, "Cache:\n"..tostring(dns.cache())
-end
-
--------------
-
-function printbanner(session)
- local option = module:get_option("console_banner");
- if option == nil or option == "full" or option == "graphic" then
- session.print [[
- ____ \ / _
- | _ \ _ __ ___ ___ _-_ __| |_ _
- | |_) | '__/ _ \/ __|/ _ \ / _` | | | |
- | __/| | | (_) \__ \ |_| | (_| | |_| |
- |_| |_| \___/|___/\___/ \__,_|\__, |
- A study in simplicity |___/
-
-]]
- end
- if option == nil or option == "short" or option == "full" then
- session.print("Welcome to the Prosody administration console. For a list of commands, type: help");
- session.print("You may find more help on using this console in our online documentation at ");
- session.print("http://prosody.im/doc/console\n");
- end
- if option and option ~= "short" and option ~= "full" and option ~= "graphic" then
- if type(option) == "string" then
- session.print(option)
- elseif type(option) == "function" then
- module:log("warn", "Using functions as value for the console_banner option is no longer supported");
- end
- end
-end
-
-module:provides("net", {
- name = "console";
- listener = console_listener;
- default_port = 5582;
- private = true;
-});
diff --git a/plugins/mod_sipwise_groups.lua b/plugins/mod_sipwise_groups.lua
deleted file mode 100644
index c4dde23..0000000
--- a/plugins/mod_sipwise_groups.lua
+++ /dev/null
@@ -1,287 +0,0 @@
--- Load PBX groups from DDBB
--- Copyright (C) 2013 Sipwise GmbH
---
--- This project is MIT/X11 licensed. Please see the
--- COPYING file in the source package for more information.
---
-
-local lookup_query = [[
-SELECT g.username, s.username, d.domain
-FROM provisioning.voip_subscribers AS s
-LEFT JOIN provisioning.voip_domains AS d ON s.domain_id = d.id
-LEFT JOIN provisioning.voip_pbx_groups AS ug ON s.id = ug.subscriber_id
-LEFT JOIN provisioning.voip_subscribers AS g ON g.id = ug.group_id
-LEFT JOIN provisioning.voip_usr_preferences as p ON p.subscriber_id = s.id
-WHERE s.account_id = ? AND s.is_pbx_group = 0 AND s.is_pbx_pilot = 0
-AND ug.group_id IS NOT NULL
-AND (p.attribute_id = ? AND p.value = '1')
-ORDER BY s.username;
-]];
-
-local lookup_with_dn_query = [[
-SELECT s.username, d.domain, p.value
-FROM provisioning.voip_subscribers AS s
-LEFT JOIN provisioning.voip_domains AS d ON s.domain_id = d.id
-LEFT JOIN provisioning.voip_usr_preferences as p ON p.subscriber_id = s.id
-LEFT JOIN provisioning.voip_pbx_groups AS ug ON s.id = ug.subscriber_id
-WHERE s.account_id = ? AND s.is_pbx_group = 0 AND s.is_pbx_pilot = 0
-AND ug.group_id IS NOT NULL
-AND p.attribute_id = ?
-ORDER BY s.username;
-]];
-
-local lookup_user_group_query = [[
-SELECT g.username
-FROM provisioning.voip_subscribers AS s
-LEFT JOIN provisioning.voip_domains AS d ON s.domain_id = d.id
-LEFT JOIN provisioning.voip_pbx_groups AS ug ON s.id = ug.subscriber_id
-LEFT JOIN provisioning.voip_subscribers AS g ON g.id = ug.group_id
-WHERE s.account_id = ? AND
-s.username = ? AND d.domain = ? AND
-s.is_pbx_group = 0 AND s.is_pbx_pilot = 0 AND ug.group_id IS NOT NULL;
-]];
-
-local lookup_users_by_groups_query = [[
-SELECT g.username, s.username, d.domain
-FROM provisioning.voip_subscribers AS s
-LEFT JOIN provisioning.voip_domains AS d ON s.domain_id = d.id
-LEFT JOIN provisioning.voip_pbx_groups AS ug ON s.id = ug.subscriber_id
-LEFT JOIN provisioning.voip_subscribers AS g ON g.id = ug.group_id
-LEFT JOIN provisioning.voip_usr_preferences as p ON p.subscriber_id = s.id
-WHERE s.account_id = ? AND s.is_pbx_group = 0 AND s.is_pbx_pilot = 0
-AND ug.group_id IS NOT NULL
-AND (p.attribute_id = ? AND p.value = '1') AND g.username in (?)
-ORDER BY s.username;
-]];
-
-local lookup_all_query = [[
-SELECT s.username, d.domain
-FROM provisioning.voip_subscribers AS s
-LEFT JOIN provisioning.voip_domains AS d ON s.domain_id = d.id
-LEFT JOIN provisioning.voip_usr_preferences as p ON p.subscriber_id = s.id
-WHERE s.account_id = ? AND s.is_pbx_group = 0 AND s.is_pbx_pilot = 0
-AND (p.attribute_id = ? AND p.value = '1')
-ORDER BY s.username;
-]];
-
-local account_id_query = [[
-SELECT account_id
-FROM provisioning.voip_subscribers
-WHERE username = ? AND
-domain_id = ( SELECT id FROM provisioning.voip_domains where domain = ?);
-]]
-
-local lookupt_preference_id_query = [[
-SELECT id
-FROM provisioning.voip_preferences
-WHERE attribute = ?;
-]]
-
--- from table to string
--- t = {'a','b'}
--- implode(",",t,"'")
--- "'a','b'"
--- implode("#",t)
--- "a#b"
-local function implode(delimiter, list, quoter)
- local len = #list
- if not delimiter then
- error("delimiter is nil")
- end
- if len == 0 then
- return nil
- end
- if not quoter then
- quoter = ""
- end
- local string = quoter .. list[1] .. quoter
- for i = 2, len do
- string = string .. delimiter .. quoter .. list[i] .. quoter
- end
- return string
-end
-
-local sql = require "util.sql";
-local default_params = module:get_option("sql");
-local engine;
-
--- Reconnect to DB if necessary
-local function reconect_check()
- if not engine.conn:ping() then
- engine.conn = nil;
- module:log("debug", "DDBB reconecting");
- engine:connect();
- engine:execute("SET NAMES 'utf8' COLLATE 'utf8_bin';");
- end
-end
-
--- returns the attribute_id of 'shared_buddylist_visiblility' preference
-local function lookup_buddy_id()
- reconect_check();
- local res = engine:select(lookupt_preference_id_query,
- 'shared_buddylist_visibility');
- for row in res do -- luacheck: ignore 512
- return row[1]
- end
- module:log("error", "no 'shared_buddylist_visiblility' preference found!");
-end
-local buddylist_preference_id;
-
--- returns the attribute_id of 'display_name' preference
-local function lookup_displayname_id()
- reconect_check();
- local res = engine:select(lookupt_preference_id_query,
- 'display_name');
- for row in res do -- luacheck: ignore 512
- return row[1]
- end
- module:log("error", "no 'display_name' preference found!");
-end
-local displayname_preference_id;
-
-
--- "roster-load" callback
-local function inject_roster_contacts(event)
- local username, host, roster = event.username, event.host, event.roster;
- module:log("debug", "Injecting group members to roster");
- local bare_jid = username.."@"..host;
- local account_id, groups, display_names;
-
- -- returns the account_id of username@host subscriber
- local function lookup_account_id()
- --module:log("debug", "lookup user '%s@%s'", username, host);
- reconect_check();
- for row in engine:select(account_id_query, username, host) do -- luacheck: ignore 512
- module:log("debug", "user '%s@%s' belongs to %d",
- username, host, row[1]);
- return row[1];
- end
- module:log("debug", "no account_id found!");
- end
-
- -- returns a table with the pbx groups the subscriber
- -- belongs to
- local function lookup_user_groups()
- local result = {};
- reconect_check();
- local res = engine:select(lookup_user_group_query, account_id,
- username, host, buddylist_preference_id);
- for row in res do
- module:log("debug", "found group:'%s'", row[1]);
- table.insert(result, row[1]);
- end
- return result;
- end
-
- -- returns a table with the subscribers display_name
- -- key is username@domain
- local function lookup_users_dn()
- local result = {};
- reconect_check();
- local res = engine:select(lookup_with_dn_query, account_id,
- displayname_preference_id);
- for row in res do
- result[row[1].."@"..row[2]] = row[3];
- end
- return result;
- end
-
- -- returns a dictionary with all the subscribers of the account
- -- key is the name of the pbx group
- -- if all is true a 'all' group will be added with all subscribers
- -- if all_groups is false only the groups that bare_jid belongs will be added
- local function lookup_groups(all, all_groups)
- local res;
- local result = {};
- if account_id then
- reconect_check();
-
- if all_groups then
- module:log("debug", "lookup_groups for account_id:%s",
- account_id);
- res = engine:select(lookup_query, account_id,
- buddylist_preference_id);
- else
- module:log("debug", "lookup_groups for account_id:%s jid:%s@%s",
- account_id, username, host);
- local user_groups = lookup_user_groups();
- res = engine:select(lookup_users_by_groups_query,
- account_id, buddylist_preference_id,
- implode(",",user_groups));
- end
- for row in res do
- if not result[row[1]] then
- result[row[1]] = {};
- end
- table.insert(result[row[1]], row[2].."@"..row[3]);
- end
- if all then
- result['all'] = {};
- for row in engine:select(lookup_all_query,
- account_id, buddylist_preference_id) do
- table.insert(result['all'], row[1].."@"..row[2]);
- end
- end
- end
- return result;
- end
-
- account_id = lookup_account_id();
- -- TODO: set this parameters from usr_preferences
- groups = lookup_groups(true, true);
- display_names = lookup_users_dn();
-
- local function import_jids_to_roster(group_name)
- for _,jid in pairs(groups[group_name]) do
- -- Add them to roster
- module:log("debug", "processing jid %s in group %s",
- tostring(jid), tostring(group_name));
- if jid ~= bare_jid then
- if not roster[jid] then roster[jid] = {}; end
- roster[jid].subscription = "both";
- -- If we have the subscriber display name
- if display_names[jid] then
- roster[jid].name = display_names[jid];
- end
- if not roster[jid].groups then
- roster[jid].groups = { [group_name] = true };
- end
- roster[jid].groups[group_name] = true;
- roster[jid].persist = false;
- end
- end
- end
-
- for group_name in pairs(groups) do
- module:log("debug", "Importing group %s", group_name);
- import_jids_to_roster(group_name);
- end
-
- if roster[false] then
- roster[false].version = true;
- end
-end
-
-local function normalize_params(params)
- assert(params.driver and params.database,
- "Configuration error: Both the SQL driver and the database need to be specified");
- return params;
-end
-
-function module.load()
- if prosody.prosodyctl then return; end
- local engines = module:shared("/*/sql/connections");
- local params = normalize_params(module:get_option("auth_sql", default_params));
- engine = engines[sql.db2uri(params)];
- if not engine then
- module:log("debug", "Creating new engine");
- engine = sql:create_engine(params);
- engines[sql.db2uri(params)] = engine;
- end
- engine:connect();
- buddylist_preference_id = lookup_buddy_id();
- displayname_preference_id = lookup_displayname_id();
-
- module:hook("roster-load", inject_roster_contacts);
- module:log("debug", "Groups loaded successfully");
-end
diff --git a/plugins/mod_sipwise_lastactivity.lua b/plugins/mod_sipwise_lastactivity.lua
deleted file mode 100644
index 0e4c938..0000000
--- a/plugins/mod_sipwise_lastactivity.lua
+++ /dev/null
@@ -1,59 +0,0 @@
--- Prosody IM
--- Copyright (C) 2008-2010 Matthew Wild
--- Copyright (C) 2008-2010 Waqas Hussain
--- Copyright (C) 2015 Sipwise GmbH
---
--- This project is MIT/X11 licensed. Please see the
--- COPYING file in the source package for more information.
---
-
--- TODO: permanent storage
-local st = require "util.stanza";
-local is_contact_subscribed = require "core.rostermanager".is_contact_subscribed;
-local jid_bare = require "util.jid".bare;
-local jid_split = require "util.jid".split;
-local sessions = module:shared("sessions");
-
-module:add_feature("jabber:iq:last");
-
-local map = {};
-
--- lastactivity: any change of presence
-module:hook("pre-presence/bare", function(event)
- local stanza = event.stanza;
- if not(stanza.attr.to) then
- local t = os.time();
- local s = stanza:get_child_text("status");
- map[event.origin.username] = {s = s, t = t};
- module:log("debug", string.format("change of presence:%s from:%s",
- tostring(s), tostring(event.origin.username)));
- end
-end, 10);
-
-module:hook("iq-get/bare/jabber:iq:last:query", function(event)
- local origin, stanza = event.origin, event.stanza;
- local username = jid_split(stanza.attr.to) or origin.username;
- if not stanza.attr.to or is_contact_subscribed(username, module.host, jid_bare(stanza.attr.from)) then
- local seconds, text = nil, "";
- if not sessions[origin.conn] and map[username] then
- seconds = tostring(os.difftime(os.time(), map[username].t));
- text = map[username].s;
- end
- origin.send(st.reply(stanza):tag('query', {xmlns='jabber:iq:last', seconds=seconds}):text(text));
- else
- origin.send(st.error_reply(stanza, 'auth', 'forbidden'));
- end
- return true;
-end);
-
-local function msg_handler(event)
- local origin, stanza = event.origin, event.stanza;
- local username = jid_split(stanza.attr.from) or origin.username;
- map[username] = {s = "online", t = os.time()};
- module:log("debug", string.format("%s from: %s",
- tostring(stanza.attr.type), tostring(username)));
-end
-
--- lastactivity: any message sent
-module:hook("pre-message/bare", msg_handler);
-module:hook("pre-message/full", msg_handler);
diff --git a/plugins/mod_sipwise_log_debug.lua b/plugins/mod_sipwise_log_debug.lua
deleted file mode 100644
index 25d009f..0000000
--- a/plugins/mod_sipwise_log_debug.lua
+++ /dev/null
@@ -1,78 +0,0 @@
--- Copyright (C) 2015 Sipwise GmbH
---
--- enable log stanzas/[in|out] by jid
---
--- This project is MIT/X11 licensed. Please see the
--- COPYING file in the source package for more information.
---
-module:set_global();
-local jid = require "util.jid";
-local add_filter = require "util.filters".add_filter;
-local ut = require "ngcp.utils";
-local log = module._log;
-
-local config_default = {
- level = "debug", -- log level
- prefix_out = "Sent",
- prefix_in = "Received",
- jids = {}, -- jids to log
- filter = {'*'} -- 'message', 'iq'
-};
-local config
-
-local function filter_stanza(stanza)
- if ut.table.contains(config.filter, '*') then
- return true
- end
- for _,v in pairs(config.filter) do
- if stanza.name == v then
- return true
- end
- if stanza:get_child(v) then
- return true
- end
- end
- return false
-end
-
-local function log_stanza(stanza, session, prefix)
- if filter_stanza(stanza) then
- session.log(config.level, "%s[%s]: %s", prefix, session.full_jid,
- tostring(stanza))
- return stanza
- end
- return stanza
-end
-
-local function log_in_stanza (stanza, session)
- return log_stanza(stanza, session,
- config.prefix_in or config_default.prefix_in)
-end
-
-local function log_out_stanza (stanza, session)
- return log_stanza(stanza, session,
- config.prefix_out or config_default.prefix_out)
-end
-
-
-local function resource_bind(event)
- local session = event.session;
- local node, domain, _ = jid.split(session.full_jid);
- local bare_jid = node.."@"..domain;
-
- if ut.table.contains(config.jids, bare_jid) then
- add_filter(session, "stanzas/out", log_out_stanza, 1000);
- add_filter(session, "stanzas/in", log_in_stanza, 1000);
- module:log("info", "log_debug activated for %s for filter %s",
- session.full_jid, ut.table.tostring(config.filter))
- end
-end
-
-function module.load()
- config = module:get_option("log_debug", config_default);
- log("info", "jids: %s", ut.table.tostring(config.jids))
-end
-
-function module.add_host(module)
- module:hook("resource-bind", resource_bind, 200);
-end
diff --git a/plugins/mod_sipwise_pushd.lua b/plugins/mod_sipwise_pushd.lua
deleted file mode 100644
index f1081b9..0000000
--- a/plugins/mod_sipwise_pushd.lua
+++ /dev/null
@@ -1,564 +0,0 @@
---
--- Copyright (C) 2014-2015 Sipwise GmbH
---
--- This project is MIT/X11 licensed. Please see the
--- COPYING file in the source package for more information.
---
-
-module:depends("sipwise_vcard_cusax");
-module:depends("sipwise_pushd_blocking");
-
-local datamanager = require "util.datamanager";
-local sql = require "util.sql";
-local format = string.format;
-local jid_split = require "util.jid".split;
-local jid_bare = require "util.jid".bare;
-local jid_host = require "util.jid".host;
-local jid_resource = require "util.jid".resource;
-local hosts = prosody.hosts;
-local http = require "net.http";
-local uuid = require "util.uuid";
-local ut = require "ngcp.utils";
-local set = require "util.set";
-local st = require "util.stanza";
-
-local sipwise_offline = module:shared("sipwise_offline/sipwise_offline");
-local pushd_blocking = module:shared("sipwise_pushd_blocking/pushd_blocking");
-
-local muc_config = {
- force_persistent = true,
- owner_on_join = true,
- exclude = {}
-};
-local pushd_config = {
- url = "https://127.0.0.1:8080/push",
- fcm = true,
- apns = true,
- call_sound = 'incoming_call.caf',
- msg_sound = 'incoming_message.caf',
- muc_config = muc_config
-};
-
-local push_usr_query = [[
-SELECT vp.attribute, vup.value FROM provisioning.voip_preferences vp
- LEFT JOIN provisioning.voip_usr_preferences vup ON vup.attribute_id = vp.id
- LEFT JOIN provisioning.voip_subscribers vs ON vs.id = vup.subscriber_id
- LEFT JOIN provisioning.voip_domains vd ON vd.id = vs.domain_id
-WHERE vp.attribute = 'mobile_push_enable'
- AND vs.username = ?
- AND vd.domain = ?;
-]];
-
-local push_dom_query = [[
-SELECT vp.attribute, vup.value FROM provisioning.voip_preferences vp
- LEFT JOIN provisioning.voip_dom_preferences vup ON vup.attribute_id = vp.id
- LEFT JOIN provisioning.voip_domains vd ON vd.id = vup.domain_id
-WHERE vp.attribute = 'mobile_push_enable'
- AND vd.domain = ?;
-]];
-
-local push_silent_query = [[
-SELECT vp.id FROM provisioning.voip_preferences vp
- LEFT JOIN provisioning.voip_usr_preferences vup ON vup.attribute_id = vp.id
- LEFT JOIN provisioning.voip_subscribers vs ON vs.id = vup.subscriber_id
- LEFT JOIN provisioning.voip_domains vd ON vd.id = vs.domain_id
-WHERE vp.attribute = 'mobile_push_silent_list'
- AND vs.username = ?
- AND vd.domain = ?
- AND vup.value = ?;
-]];
-
-local default_params = module:get_option("sql");
-local engine;
-
--- Reconnect to DB if necessary
-local function reconect_check()
- if not engine.conn:ping() then
- engine.conn = nil;
- engine:connect();
- end
-end
-
--- luacheck: ignore request
-local function process_response(response, code, request)
- if code >= 200 and code < 299 then
- module:log("debug", "pushd response OK[%s] %s",
- tostring(code), tostring(response));
- else
- module:log("error", "pushd response KO[%s] %s",
- tostring(code), tostring(response));
- end
-end
-
-local function push_silent(username, domain, other)
- reconect_check();
- for row in engine:select(push_silent_query, username, domain, other) do
- module:log("debug", "silent push preference mobile_push_silent_list matches");
- return true;
- end
- return false;
-end
-
-local function push_enable(username, domain)
- reconect_check();
- local flag
-
- for row in engine:select(push_usr_query, username, domain) do
- if row[2] == "never" then
- module:log("debug", "usr mobile_push_enable pref set to 'never'");
- return false;
- end
- module:log("debug", "usr pref %s", ut.table.tostring(row))
- flag = true
- end
- if flag then return true end
- for row in engine:select(push_dom_query, domain) do
- if row[2] == "never" then
- module:log("debug", "domain mobile_push_enable pref set to 'never'");
- return false;
- end
- module:log("debug", "dom pref %s", ut.table.tostring(row))
- end
- return true;
-end
-
-local function is_local_domain(dom)
- return ut.table.contains(ut.table.keys(hosts), dom);
-end
-
-local function get_caller_info(jid, caller_defaults)
- if not jid then
- return nil;
- end
- local node, host = jid_split(jid);
- if not is_local_domain(host) then
- module:log("debug", "caller[%s] not local", jid);
- return nil;
- end
- local vcard = module:shared(format("/%s/sipwise_vcard_cusax/vcard", host));
- if vcard and vcard.get_subscriber_info then
- local info = vcard.get_subscriber_info(node, host);
- module:log("debug", "caller_info of %s", jid);
- if not info.display_name then
- module:log("debug", "set display_name to %s", node);
- info.display_name = node;
- end
- if not info.aliases then
- info.aliases = caller_defaults.aliases;
- end
- return info;
- end
-end
-
-local function get_members(muc_room)
- local res = set.new();
- if muc_room then
- for o_jid, _, _ in muc_room:each_affiliation() do
- res:add(o_jid);
- end
- end
- return res;
-end
-
-local function get_occupants(muc_room)
- local res = set.new();
- for _, occupant in muc_room:each_occupant() do
- res:add(jid_bare(occupant.jid));
- end
- return res;
-end
-
-local function get_muc_room(room_jid)
- local host = jid_host(room_jid);
-
- if not hosts[host] then
- module:log("warn", "host [%s] not defined", tostring(host));
- return nil
- end
- if not hosts[host].modules.muc then
- module:log("warn", "muc not enabled here [%s]", tostring(host));
- return nil
- end
- return hosts[host].modules.muc.get_room_from_jid(jid_bare(room_jid));
-end
-
-local function get_muc_caller(room_jid)
- local nick = jid_resource(room_jid);
-
- local room = get_muc_room(room_jid);
- if not room then
- module:log("warn", "room %s not here", jid_bare(room_jid));
- return nil;
- end
- return room:get_occupant_by_nick(nick);
-end
-
-local function get_callee_badge(jid)
- local node, host = jid_split(jid);
- local result = 0;
- if sipwise_offline.get_num then
- result = sipwise_offline.get_num(node, host);
- else
- local offline = datamanager.list_load(node, host, "offline");
- if offline then
- result = #offline;
- end
- end
- -- plus the one in process
- result = result + 1;
- module:log("debug", "%d offline messages for %s", result, jid);
- return result;
-end
-
-local function is_invite(stanza)
- if stanza:get_child('x', 'http://jabber.org/protocol/muc#user') then
- return true
- end
-end
-
-local function is_attachment(stanza)
- if stanza:get_child('x', 'jabber:x:oob') then
- return true
- end
-end
-
-local function get_message(stanza)
- local body = stanza:get_child('body');
- if body then
- return body:get_text();
- end
-end
-
-local function get_muc_info(stanza, caller_info)
- local from = stanza.attr.from;
- local muc_stanza = stanza:get_child('x', 'jabber:x:conference');
- local muc = {};
- local room;
-
- if muc_stanza then
- muc.jid = muc_stanza.attr.jid;
- else
- muc.jid = jid_bare(from);
- end
- muc.name, muc.domain = jid_split(muc.jid);
- if not is_local_domain(muc.domain) then
- module:log("debug", "not from local host[%s]", tostring(muc.domain));
- return nil;
- end
- local room_host = hosts[muc.domain].modules.muc;
- if not room_host then
- module:log("debug", "not from MUC host[%s]", muc.domain);
- return nil;
- end
- room = room_host.get_room_from_jid(muc.jid);
- if room then
- muc.room = room:get_description() or muc.name;
- if muc_stanza then
- muc.invite = format("Group chat invitation to '%s' from %s",
- muc.room, caller_info.display_name);
- end
- return muc;
- end
-end
-
-local function is_pushd_blocked(from, to)
- local node_to, host_to = jid_split(to);
- local blocked_list = pushd_blocking.get_blocked_jids(node_to, host_to);
-
- module:log("debug", "pushd_blockedlist: %s for [%s]", ut.table.tostring(blocked_list), to);
-
- local _, host_from, resource_from = jid_split(from);
- if ut.table.contains(blocked_list, from)
- or ut.table.contains(blocked_list, jid_bare(from))
- or ut.table.contains(blocked_list, host_from) then
- return true;
- end
- if resource_from and ut.table.contains(blocked_list, host_from.."/"..resource_from) then
- return true;
- end
- return false;
-end
-
-local function handle_offline(event)
- module:log("debug", "handle_offline");
- local origin, stanza = event.origin, event.stanza;
- local to = stanza.attr.to;
- local from = stanza.attr.from;
- local node, host;
- local caller = { username = 'unknow', host = 'unknown.local' };
- local caller_defaults = {display_name = '', aliases = {''}};
- local caller_info;
- -- defaults to "application/x-www-form-urlencoded"
- local http_options = {
- method = "POST",
- body = "",
- }
- local message;
-
- local function build_push_common_query(msg, muc)
- if muc then
- msg.data_room_jid = muc['jid'];
- msg.data_room_description = muc['room'];
- if msg.data_type == 'invite' then
- msg.data_message = muc['invite'];
- end
- end
- msg.data_sender_number = tostring(caller_info.aliases[1]);
- msg.data_sender_name = tostring(caller_info.display_name);
- return msg;
- end
- local function build_push_apns_query(msg, muc, silent)
- if silent then
- msg['apns_content-available'] = '1';
- else
- if msg.data_type ~= 'invite' then
- msg.apns_alert = string.format("Message from %s:\n",
- caller_info.display_name) .. msg.data_message;
- else
- msg.apns_alert = muc['invite'];
- end
- msg.apns_sound = pushd_config.msg_sound or '';
- msg.apns_badge = tostring(get_callee_badge(to));
- end
- return msg;
- end
- local function build_push_query(msg)
- local muc;
- local caller_jid;
- local silent;
- if stanza.attr.type == 'groupchat' then
- msg.data_type = 'groupchat'
- caller_jid = get_muc_caller(stanza.attr.from);
- module:log("debug", "from:%s -> caller_jid:%s",
- tostring(stanza.attr.from), tostring(caller_jid));
- caller_info = get_caller_info(caller_jid, caller_defaults) or
- caller_defaults;
- muc = get_muc_info(stanza, caller_info);
- silent = push_silent(msg.callee, msg.domain, muc['jid']);
- else
- msg.data_type = 'message';
- caller_jid = format("%s@%s",
- origin.username or caller.username,
- origin.host or caller.host);
- if is_invite(stanza) then
- msg.data_type = 'invite';
- caller_jid = jid_bare(stanza:get_child('invite').attr.from) or
- caller_jid;
- elseif is_attachment(stanza) then
- msg.data_message = "new attachment";
- end
- caller_info = get_caller_info(caller_jid, caller_defaults) or
- caller_defaults;
- muc = get_muc_info(stanza, caller_info);
- silent = push_silent(msg.callee, msg.domain, caller_jid);
- end
- if silent then
- msg.data_silent = '1';
- else
- msg.data_silent = '0';
- end
- msg.data_sender_jid = caller_jid;
- msg.data_sender_sip = jid_bare(caller_jid);
- msg.push_id = uuid.generate();
- msg = build_push_common_query(msg, muc);
- if pushd_config.apns then
- msg = build_push_apns_query(msg, muc, silent);
- end
- return msg;
- end
-
- module:log("debug", "stanza[%s]", tostring(stanza));
-
- if from then
- caller.username, caller.host = jid_split(from);
- end
-
- if to then
- node, host = jid_split(to);
- if from and is_pushd_blocked(from, to) then
- module:log("debug", "skip pushd message from blocked jid [%s]", from);
- return nil;
- end
- message = {
- data_message = get_message(stanza),
- data_full_message = tostring(stanza),
- callee = node,
- domain = host,
- };
- if message.data_message then
- module:log("debug", "message OK");
- if push_enable(node, host) then
- message = build_push_query(message);
- module:log("debug", "message: %s", ut.table.tostring(message));
- http_options.body = http.formencode(message);
- if http_options.body then
- module:log("debug", "Sending http pushd request: %s data: %s",
- pushd_config.url, http_options.body);
- http.request(pushd_config.url, http_options, process_response);
- end
- else
- module:log("debug", "no mobile_push_enable pref set for %s", to);
- end
- else
- module:log("debug", "no message body");
- end
- end
-end
-
-local function fire_offline_message(event, muc_room, off_jid)
- local stanza_c = st.clone(event.stanza);
- local orig_from = event.stanza.attr.from;
- stanza_c.attr.to = off_jid;
- stanza_c.attr.from = muc_room:get_occupant_jid(orig_from);
-
- module:log("debug", "stanza[%s] stanza_c[%s]",
- tostring(event.stanza), tostring(stanza_c));
- if not stanza_c.attr.from then
- module:log("error", "original from[%s] not found at muc_room",
- tostring(orig_from));
- return nil;
- end
- if is_pushd_blocked(stanza_c.attr.from, stanza_c.attr.to) then
- module:log("debug", "skip pushd message from blocked jid [%s]",
- stanza_c.attr.from);
- elseif is_pushd_blocked(orig_from, stanza_c.attr.to) then
- module:log("debug", "skip pushd message from blocked jid [%s]",
- orig_from);
- else
- module:fire_event('message/offline/handle', {
- origin = event.origin,
- stanza = stanza_c,
- });
- end
-end
-
-local function handle_muc_offline(event, room_jid)
- local _, host = jid_split(room_jid);
- local muc = hosts[host].modules.muc;
-
- if muc then
- local muc_room = muc.get_room_from_jid(room_jid);
- if not muc_room then
- module:log("debug", "muc room[%s] not here. Nothing to do",
- room_jid);
- return nil;
- elseif ut.table.contains(pushd_config.muc_config.exclude, room_jid) then
- module:log("debug", "muc room[%s] excluded from pushd", room_jid);
- return nil;
- end
-
- local muc_members = get_members(muc_room);
- local muc_occ_online = get_occupants(muc_room);
- local muc_occ_offline = set.difference(muc_members,muc_occ_online);
- module:log("debug", "muc_members[%s]", tostring(muc_members));
- module:log("debug", "muc_occ_online[%s]", tostring(muc_occ_online));
- module:log("debug", "muc_occ_offline[%s]", tostring(muc_occ_offline));
- for off_jid in muc_occ_offline do
- module:log("debug", "fire_offline_message[%s]", off_jid);
- fire_offline_message(event, muc_room, off_jid);
- end
- end
-end
-
-local function handle_msg(event)
- module:log("debug", "handle_msg");
- local stanza = event.stanza;
- local room_jid = stanza.attr.to;
-
- if stanza.attr.type ~= 'groupchat' then
- module:log("debug",
- "message[%s] not of type groupchat. Nothing to do here",
- tostring(stanza));
- return nil;
- end
- module:log("debug", "handle_msg room_jid[%s]", tostring(room_jid));
- return handle_muc_offline(event, room_jid);
-end
-
-local function handle_muc_created(event)
- local room = event.room;
- if pushd_config.muc_config.force_persistent then
- room:set_persistent(true);
- module:log("debug", "persistent room[%s] forced on creation",
- room:get_name());
- end
-end
-
-local function handle_muc_config(event)
- local room, fields = event.room, event.fields;
- local name = fields['muc#roomconfig_roomname'] or room:get_name();
- local persistent = fields['muc#roomconfig_persistentroom'];
- if pushd_config.muc_config.force_persistent and not persistent then
- fields['muc#roomconfig_persistentroom'] = true;
- event.changed = true;
- module:log("debug", "persistent room[%s] forced", name);
- end
-end
-
-local function handle_muc_presence(event)
- if not pushd_config.muc_config.owner_on_join then return; end
- local stanza = event.stanza;
- if stanza.attr.type == "unavailable" then return; end
-
- local room = get_muc_room(stanza.attr.to);
- if not room then return; end
- local from_jid = jid_bare(stanza.attr.from);
- local affiliation = room:get_affiliation(from_jid);
- if affiliation ~= "owner" then
- room:set_affiliation(true, from_jid, "owner");
- module:log("debug", "[%s] set affiliation to 'owner' for [%s]",
- room:get_name(), from_jid);
- end
-end
-
-local function handle_muc_presence_out(event)
- local stanza = event.stanza;
- local muc_stanza = stanza:get_child('x',
- 'http://jabber.org/protocol/muc#user');
-
- if muc_stanza then
- local item = muc_stanza:get_child('item');
- if not item.nick and stanza.attr.from then
- local nick = select(3, jid_split(stanza.attr.from));
- if nick then
- item.attr.nick = nick;
- module:log("debug", "added nick[%s] tag", nick);
- end
- end
- end
-end
-
-if module:get_host_type() == "component" then
- module:hook("muc-room-created", handle_muc_created, 20);
- module:hook("muc-config-submitted", handle_muc_config, 20);
- module:hook("presence/full", handle_muc_presence, 501);
-else
- module:hook("presence/full", handle_muc_presence_out, 501);
-end
-module:hook("message/bare", handle_msg, 20);
-module:hook("message/offline/handle", handle_offline, 20);
-
-local function normalize_params(params)
- assert(params.driver and params.database,
- "Configuration error: Both the SQL driver and the database need to be specified");
- return params;
-end
-
-function module.load()
- if prosody.prosodyctl then return; end
- local engines = module:shared("/*/sql/connections");
- local params = normalize_params(module:get_option("auth_sql", default_params));
- engine = engines[sql.db2uri(params)];
- if not engine then
- module:log("debug", "Creating new engine");
- engine = sql:create_engine(params);
- engines[sql.db2uri(params)] = engine;
- end
- engine:connect();
- pushd_config = module:get_option("pushd_config", pushd_config);
- if not pushd_config.muc_config then
- pushd_config.muc_config = muc_config
- end
-
- module:log("debug", "load OK");
-end
diff --git a/plugins/mod_sipwise_pushd_blocking.lua b/plugins/mod_sipwise_pushd_blocking.lua
deleted file mode 100644
index fbfb4fd..0000000
--- a/plugins/mod_sipwise_pushd_blocking.lua
+++ /dev/null
@@ -1,175 +0,0 @@
-local jid_split = require "util.jid".split;
-local st = require "util.stanza";
-local datamanager = require"util.datamanager";
-
-local xmlns_blocking = "urn:xmpp:pushd_blocking";
-local datastore = "pushd_privacy";
-
-module:add_feature("urn:xmpp:pushd_blocking");
-local pushd_blocking = module:shared("pushd_blocking");
-
--- Add JID to default privacy list
-local function add_blocked_jid(username, host, jid)
- local privacy_lists = datamanager.load(username, host, datastore) or {lists = {}};
- local default_list_name = privacy_lists.default;
- if not privacy_lists.lists then
- privacy_lists.lists = {}
- end
- if not default_list_name then
- default_list_name = "blocklist";
- privacy_lists.default = default_list_name;
- end
- local default_list = privacy_lists.lists[default_list_name];
- if not default_list then
- default_list = { name = default_list_name, items = {} };
- privacy_lists.lists[default_list_name] = default_list;
- end
- local items = default_list.items;
- local order = items[1] and items[1].order or 0; -- Must come first
- for i=1,#items do -- order must be unique
- local item = items[i];
- item.order = item.order + 1;
- if item.type == "jid" and item.action == "deny" and item.value == jid then
- return false;
- end
- end
- table.insert(items, 1, { type = "jid"
- , action = "deny"
- , value = jid
- , message = false
- , ["presence-out"] = false
- , ["presence-in"] = false
- , iq = false
- , order = order
- });
- datamanager.store(username, host, datastore, privacy_lists);
- return true;
-end
-
--- Remove JID from default privacy list
-local function remove_blocked_jid(username, host, jid)
- local privacy_lists = datamanager.load(username, host, datastore) or {};
- local default_list_name = privacy_lists.default;
- if not default_list_name then return; end
- local default_list = privacy_lists.lists[default_list_name];
- if not default_list then return; end
- local items = default_list.items;
- local item, removed = nil, false;
- for i=1,#items do -- order must be unique
- item = items[i];
- if item.type == "jid" and item.action == "deny" and item.value == jid then
- table.remove(items, i);
- removed = true;
- break;
- end
- end
- if removed then
- datamanager.store(username, host, datastore, privacy_lists);
- end
- return removed;
-end
-
-local function remove_all_blocked_jids(username, host)
- local privacy_lists = datamanager.load(username, host, datastore) or {};
- local default_list_name = privacy_lists.default;
- if not default_list_name then return; end
- local default_list = privacy_lists.lists[default_list_name];
- if not default_list then return; end
- local items = default_list.items;
- local item;
- for i=#items,1,-1 do -- order must be unique
- item = items[i];
- if item.type == "jid" and item.action == "deny" then
- table.remove(items, i);
- end
- end
- datamanager.store(username, host, datastore, privacy_lists);
- return true;
-end
-
-function pushd_blocking.get_blocked_jids(username, host)
- -- Return array of blocked JIDs in default privacy list
- local privacy_lists = datamanager.load(username, host, datastore) or {};
- local default_list_name = privacy_lists.default;
- if not default_list_name then return {}; end
- local default_list = privacy_lists.lists[default_list_name];
- if not default_list then return {}; end
- local items = default_list.items;
- local item;
- local jid_list = {};
- for i=1,#items do -- order must be unique
- item = items[i];
- if item.type == "jid" and item.action == "deny" then
- jid_list[#jid_list+1] = item.value;
- end
- end
- return jid_list;
-end
-
-local function send_push_iqs(username, host, command_type, jids)
- local bare_jid = username.."@"..host;
-
- local stanza_content = st.stanza(command_type, { xmlns = xmlns_blocking });
- for _, jid in ipairs(jids) do
- stanza_content:tag("item", { jid = jid }):up();
- end
-
- for resource, session in pairs(prosody.bare_sessions[bare_jid].sessions) do
- local iq_push_stanza = st.iq({ type = "set", to = bare_jid.."/"..resource });
- iq_push_stanza:add_child(stanza_content);
- session.send(iq_push_stanza);
- end
-end
-
-local function handle_blocking_command(event)
- local session, stanza = event.origin, event.stanza;
-
- local username, host = jid_split(stanza.attr.from);
- if stanza.attr.type == "set" then
- if stanza.tags[1].name == "block" then
- local block = stanza.tags[1];
- local block_jid_list = {};
- for item in block:childtags() do
- block_jid_list[#block_jid_list+1] = item.attr.jid;
- end
- if #block_jid_list == 0 then
- session.send(st.error_reply(stanza, "modify", "bad-request"));
- else
- for _, jid in ipairs(block_jid_list) do
- add_blocked_jid(username, host, jid);
- end
- session.send(st.reply(stanza));
- send_push_iqs(username, host, "block", block_jid_list);
- end
- return true;
- elseif stanza.tags[1].name == "unblock" then
- local unblock = stanza.tags[1];
- local unblock_jid_list = {};
- for item in unblock:childtags() do
- unblock_jid_list[#unblock_jid_list+1] = item.attr.jid;
- end
- if #unblock_jid_list == 0 then
- remove_all_blocked_jids(username, host);
- else
- for _, jid_to_unblock in ipairs(unblock_jid_list) do
- remove_blocked_jid(username, host, jid_to_unblock);
- end
- end
- session.send(st.reply(stanza));
- send_push_iqs(username, host, "unblock", unblock_jid_list);
- return true;
- end
- elseif stanza.attr.type == "get" and stanza.tags[1].name == "blocklist" then
- local reply = st.reply(stanza):tag("blocklist", { xmlns = xmlns_blocking });
- local blocked_jids = pushd_blocking.get_blocked_jids(username, host);
- for _, jid in ipairs(blocked_jids) do
- reply:tag("item", { jid = jid }):up();
- end
- session.send(reply);
- return true;
- end
-end
-
-module:hook("iq/self/urn:xmpp:pushd_blocking:blocklist", handle_blocking_command);
-module:hook("iq/self/urn:xmpp:pushd_blocking:block", handle_blocking_command);
-module:hook("iq/self/urn:xmpp:pushd_blocking:unblock", handle_blocking_command);
diff --git a/plugins/mod_sipwise_redis_mucs.lua b/plugins/mod_sipwise_redis_mucs.lua
deleted file mode 100644
index ebe7997..0000000
--- a/plugins/mod_sipwise_redis_mucs.lua
+++ /dev/null
@@ -1,154 +0,0 @@
--- Prosody IM
--- Copyright (C) 2014-2015 Sipwise GmbH
---
--- This project is MIT/X11 licensed. Please see the
--- COPYING file in the source package for more information.
---
-module:set_global();
-local set = require "util.set";
-local ut = require "ngcp.utils";
-local jid = require "util.jid";
-local redis = require 'redis';
-local redis_config = {
- port = 6739, host = "127.0.0.1",
- server_id = "0", redis_db = "2"
-};
-local redis_client;
-local redis_mucs = module:shared("redis_mucs");
-
-local function test_connection()
- if not redis_client then return nil; end
- local ok, _ = pcall(redis_client.ping, redis_client);
- if not ok then
- redis_client = nil;
- end
-end
-
-local function client_connect()
- redis_client = redis.connect(redis_config.host, redis_config.port);
- if redis_config.redis_db then
- redis_client:select(redis_config.redis_db);
- end
-end
-
-local function muc_created(event)
- local room = event.room;
- local rhost = redis_client:get(room.jid);
-
- if rhost then
- module:log("warn", "[%s] already at %s", room.jid, rhost);
- return nil;
- end
- redis_mucs.set_room_host(room.jid, redis_config.server_id);
- module:log("debug", "muc-room-created %s", room.jid);
-end
-
-local function muc_pre_restore(event)
- local room_jid = event.jid;
- local rhost = redis_client:get(room_jid);
-
- if rhost ~= redis_config.server_id then
- module:log("warn", "[%s] already at %s", room_jid, rhost);
- return false;
- end
- module:log("debug", "[%s] already here", room_jid);
- return true;
-end
-
-local function muc_restored(event)
- local room = event.room;
-
- redis_mucs.set_room_host(room.jid, redis_config.server_id);
- module:log("debug", "muc-room-restored %s", room.jid);
-end
-
-local function muc_destroyed(event)
- local room = event.room;
-
- redis_mucs.clean_room_host(room.jid, redis_config.server_id);
- module:log("debug", "muc-room-destroyed %s", room.jid);
-end
-
-local function split_key(key)
- local t = ut.explode(':', key);
- return t[1], t[2];
-end
-
-function redis_mucs.get_rooms(host)
- local res = set.new();
- local l, r;
-
- module:log("debug", "search rooms at host[%s]", tostring(host));
- if not test_connection() then client_connect() end
- l = redis_client:smembers(host);
-
- for _,v in pairs(l) do
- _, r = split_key(v);
- res:add(r..'@'..host);
- end
- module:log("debug", "found [%s]", tostring(res));
- return res;
-end
-
-function redis_mucs.set_room_host(room_jid, server_id)
- local node, host, _ = jid.split(room_jid);
- local bare_jid = node.."@"..host;
-
- if not test_connection() then client_connect() end
- -- TODO: check that there is no other "bare_jid" value?
- if redis_client:set(bare_jid, server_id) then
- module:log("debug", "save [%s]=%s", bare_jid, server_id);
- end
- if redis_client:sadd(host, server_id..":"..node) > 0 then
- module:log("debug", "append [%s]=>%s:%s", host,
- server_id, node);
- end
-end
-
-function redis_mucs.get_room_host(room_jid)
- local node, domain, _ = jid.split(room_jid);
- local bare_jid = node.."@"..domain;
-
- module:log("debug", "search room:%s host", bare_jid);
- if not test_connection() then client_connect() end
- return redis_client:get(bare_jid);
-end
-
-function redis_mucs.clean_room_host(room_jid, server_id)
- local node, host, _ = jid.split(room_jid);
- local bare_jid = node.."@"..host;
-
- if not test_connection() then client_connect() end
- if redis_client:del(bare_jid) > 0 then
- module:log("debug", "remove [%s]=%s", bare_jid, server_id);
- end
- if redis_client:srem(host, server_id..":"..node) > 0 then
- module:log("debug", "remove [%s]=>%s:%s", host, server_id, node);
- end
-end
-
-function module.load()
- redis_config = module:get_option("redis_sessions_auth", redis_config);
-end
-
-function shutdown_component()
- local rooms = redis_mucs.get_rooms(module:get_host());
-
- for room_jid in rooms do
- redis_mucs.clean_room_host(room_jid, redis_config.server_id);
- end
-end
-module:hook_global("server-stopping", shutdown_component, -300);
-
-function module.add_host(module)
- local host = module:get_host();
- local _type = module:get_host_type();
-
- if _type == "component" then
- module:hook("muc-room-created", muc_created, 200);
- module:hook("muc-room-destroyed", muc_destroyed, 200);
- module:hook("muc-room-restored", muc_restored, 200);
- module:hook("muc-room-pre-restore", muc_pre_restore, 200);
- end
- module:log("debug", "hooked at %s as %s", host, _type);
-end
diff --git a/plugins/mod_sipwise_redis_sessions.lua b/plugins/mod_sipwise_redis_sessions.lua
deleted file mode 100644
index 1ad7079..0000000
--- a/plugins/mod_sipwise_redis_sessions.lua
+++ /dev/null
@@ -1,141 +0,0 @@
--- Prosody IM
--- Copyright (C) 2014-2015 Sipwise GmbH
---
--- This project is MIT/X11 licensed. Please see the
--- COPYING file in the source package for more information.
---
-
-module:set_global();
-local ut = require "ngcp.utils";
-local jid = require "util.jid";
-local array = require "util.array";
-local redis = require 'redis';
-local redis_config = {
- port = 6739, host = "127.0.0.1",
- server_id = "0", redis_db = "2",
- clean_local_sessions = true,
-};
-local redis_client;
-
-local redis_sessions = module:shared("redis_sessions");
-
-local function test_connection()
- if not redis_client then return nil end;
- local ok, _ = pcall(redis_client.ping, redis_client);
- if not ok then
- redis_client = nil;
- end
-end
-
-local function client_connect()
- redis_client = redis.connect(redis_config.host, redis_config.port);
- --module:log("debug", "connected to redis server %s:%d",
- --redis_config.host, redis_config.port);
- if redis_config.redis_db then
- redis_client:select(redis_config.redis_db);
- end
-end
-
-local function resource_bind(event)
- local session = event.session;
- local node, domain, resource = jid.split(session.full_jid);
- local full_jid, bare_jid = session.full_jid, node.."@"..domain;
-
- module:log("debug", "resource-bind from %s", session.host);
- module:log("debug", "save [%s]=%s", full_jid, redis_config.server_id);
- if not test_connection() then client_connect() end
- redis_client:set(full_jid, redis_config.server_id);
- module:log("debug", "append [%s]=>%s:%s", bare_jid, redis_config.server_id, resource);
- redis_client:sadd(bare_jid, redis_config.server_id..":"..resource);
-end
-
-local function remove_resource(full_jid)
- local node, domain, resource = jid.split(full_jid);
- local bare_jid = node.."@"..domain;
-
- if not test_connection() then client_connect() end
- redis_client:del(full_jid);
- module:log("debug", "remove [%s]=%s", full_jid, redis_config.server_id);
- redis_client:srem(bare_jid, redis_config.server_id..":"..resource);
- module:log("debug", "remove [%s]=>%s:%s", bare_jid,
- redis_config.server_id, resource);
-end
-
-local function resource_unbind(event)
- local session, _ = event.session, event.error;
-
- module:log("debug", "resource-unbind from %s", session.host);
- remove_resource(session.full_jid);
-end
-
-local function split_key(key)
- local t = ut.explode(':', key);
- return t[1], t[2];
-end
-
-local function clean_local_sessions(host)
- if not test_connection() then client_connect() end
- local l = redis_client:keys('*@'..host);
- for _, bare_jid in ipairs(l) do
- local resources = redis_client:smembers(bare_jid);
- module:log("debug", "clean previous sessions for %s on %s",
- bare_jid, redis_config.server_id);
- for _, resource in ipairs(resources) do
- local server, old_resource = split_key(resource);
- if server == redis_config.server_id then
- module:log("debug", "remove old resource %s", old_resource);
- remove_resource(bare_jid..'/'..old_resource);
- end
- end
- end
-end
-
-function redis_sessions.get_hosts(j)
- local node, domain = jid.split(j);
- local bare_jid = node.."@"..domain;
- local res = {};
- local l, h, r;
-
- module:log("debug", "search session:%s host", bare_jid);
- if not test_connection() then client_connect() end
- l = redis_client:smembers(bare_jid);
- --module:log("debug", "l:%s", ut.table.tostring(l));
- for _,v in pairs(l) do
- h, r = split_key(v);
- --module:log("debug", "h:%s r:%s", tostring(h), tostring(r));
- if not res[h] then res[h] = array() end
- res[h]:push(r);
- end
- return res;
-end
-
-function redis_sessions.clean_host(j, server_id)
- local bare_jid = jid.bare(j);
- module:log("debug", "clean jid %s from %s", bare_jid, server_id);
- if not test_connection() then client_connect() end
- local l = redis_client:smembers(bare_jid);
- for _,v in pairs(l) do
- local h, _ = split_key(v);
- if h == server_id then
- redis_client:srem(bare_jid, v);
- redis_client:del(v);
- module:log("debug", "removed %s from %s", v, bare_jid);
- end
- end
-end
-
-function module.load()
- redis_config = module:get_option("redis_sessions_auth", redis_config);
-end
-
-function module.add_host(module)
- local host = module:get_host();
- local _type = module:get_host_type();
-
- if _type ~= "component" then
- module:hook("resource-bind", resource_bind, 200);
- module:hook("resource-unbind", resource_unbind, 200);
- clean_local_sessions(host);
- end
- module:log("debug", "hooked at %s as %s", host, _type);
-end
diff --git a/plugins/mod_sipwise_vcard_cusax.lua b/plugins/mod_sipwise_vcard_cusax.lua
deleted file mode 100644
index f28ea21..0000000
--- a/plugins/mod_sipwise_vcard_cusax.lua
+++ /dev/null
@@ -1,195 +0,0 @@
--- Prosody IM
--- Copyright (C) 2008-2010 Matthew Wild
--- Copyright (C) 2008-2010 Waqas Hussain
--- Copyright (C) 2013-2015 Sipwise GmbH
---
--- This is a stripped down version of mod_vcard for returning
--- simply a vcard containing some info of
--- the requested user.
--- http://xmpp.org/extensions/xep-0054.html
---
--- This project is MIT/X11 licensed. Please see the
--- COPYING file in the source package for more information.
---
-
-local st = require "util.stanza";
-local jid_split = require "util.jid".split;
-local vcard = module:shared("vcard");
-local hosts = prosody.hosts;
-
-local email_query = [[
-SELECT bc.email, bccc.email FROM billing.voip_subscribers AS vs
- LEFT join billing.contacts AS bc ON vs.contact_id = bc.id
- LEFT join billing.contracts AS bcc ON vs.contract_id = bcc.id
- LEFT join billing.contacts AS bccc ON bcc.contact_id = bccc.id
- LEFT join billing.domains AS bd ON vs.domain_id = bd.id
-WHERE vs.username = ? AND bd.domain = ?;
-]]
-
-local display_usr_query = [[
-SELECT vp.attribute, vup.value FROM provisioning.voip_preferences vp
- LEFT JOIN provisioning.voip_usr_preferences vup ON vup.attribute_id = vp.id
- LEFT JOIN provisioning.voip_subscribers AS ps ON ps.id = vup.subscriber_id
- LEFT JOIN provisioning.voip_domains AS pd ON ps.domain_id = pd.id
-WHERE vp.attribute = 'display_name'
- AND ps.username = ? AND pd.domain = ?
-]];
-
-local aliases_query = [[
-SELECT pa.username, pa.is_primary FROM provisioning.voip_dbaliases AS pa
- LEFT JOIN provisioning.voip_subscribers AS ps ON ps.id = pa.subscriber_id
- LEFT JOIN provisioning.voip_domains AS pd ON ps.domain_id = pd.id
-WHERE ps.username = ? AND pd.domain = ? ORDER BY pa.is_primary DESC;
-]];
-
-local um_user_exists = require "core.usermanager".user_exists;
-local sql = require "util.sql";
-local default_params = module:get_option("sql");
-local engine;
-
-module:add_feature("vcard-temp");
-
-local function reconect_check()
- if not engine.conn:ping() then
- engine.conn = nil;
- engine:connect();
- end
-end
-
-function vcard.get_subscriber_info(user, host)
- if not hosts[host] or not hosts[host].users then
- return nil;
- end
- if not um_user_exists(user, host) then
- return nil;
- end
- local info = { user = user, domain = host, aliases = {} };
- reconect_check();
-
- for row in engine:select(display_usr_query, user, host) do
- info['display_name'] = row[2];
- module:log("debug", string.format("display_name:[%s]", row[2]));
- end
-
- for row in engine:select(aliases_query, user, host) do
- table.insert(info['aliases'], row[1]);
- module:log("debug", string.format("aliases:%s", row[1]));
- end
-
- for row in engine:select(email_query, user, host) do
- local email;
- -- NULL is not nil here, so checking length
- for i= 1, 2 do
- if row[i] and row[i]:len() > 0 then
- email = row[i];
- break;
- end
- end
- if email then
- info['email'] = email;
- module:log("debug", "email:", tostring(email));
- end
- end
- return info;
-end
-
-local function generate_vcard(info)
- local function add(t, name, value)
- local tmp = st.stanza( name,
- { xmlns = "vcard-temp" });
- if value then tmp:text(value) end
- if t then
- t:add_child(tmp);
- else
- return tmp;
- end
- end
- local vCard = st.stanza("vCard",
- {
- xmlns = "vcard-temp",
- prodid = "-//HandGen//NONSGML vGen v1.0//EN",
- version = "2.0"
- }
- );
- local uri = info["user"] .. '@' .. info["domain"];
-
- add(vCard, "JABBERID", uri);
- local t = add(nil, "TEL")
- add(t, "VIDEO");
- add(t, "NUMBER", "sip:" .. uri);
- vCard:add_child(t);
- for _,v in ipairs(info['aliases']) do
- t = add(nil, "TEL");
- add(t, "VOICE");
- add(t, "NUMBER", v);
- vCard:add_child(t);
- end
- if info['display_name'] then
- add(vCard, "FN", info['display_name']);
- end
- if info['email'] then
- t = add(nil, "EMAIL");
- add(t, "INTERNET");
- add(t, "PREF");
- add(t, "USERID", info['email']);
- vCard:add_child(t)
- end
- return vCard;
-end
-
-local function handle_vcard(event)
- module:log("debug", "handle_vcard");
- local session, stanza = event.origin, event.stanza;
- local to = stanza.attr.to;
- if stanza.attr.type == "get" then
- local user, host;
- if to then
- user, host = jid_split(to);
- else
- user = session.username;
- host = session.host;
- end
- if not (user and host) then
- module:log("error", "bad-request stanza: %s",
- tostring(stanza))
- session.send(st.error_reply(stanza, "modify", "jid-malformed"));
- end
- local info = vcard.get_subscriber_info(user, host);
- if info then
- local vCard = generate_vcard(info);
- local reply = st.reply(stanza):add_child(st.deserialize(vCard));
- session.send(reply);
- else
- module:log("error", "No info retrieve from stanza: %s",
- tostring(stanza))
- session.send(st.error_reply(stanza, "cancel", "item-not-found"));
- end
- else
- module:log("debug", "reject setting vcard");
- session.send(st.error_reply(stanza, "auth", "forbidden"));
- end
- return true;
-end
-
-module:hook("iq/bare/vcard-temp:vCard", handle_vcard);
-module:hook("iq/host/vcard-temp:vCard", handle_vcard);
-
-local function normalize_params(params)
- assert(params.driver and params.database,
- "Configuration error: Both the SQL driver and the database need to be specified");
- return params;
-end
-
-function module.load()
- if prosody.prosodyctl then return; end
- local engines = module:shared("/*/sql/connections");
- local params = normalize_params(module:get_option("auth_sql", default_params));
- engine = engines[sql.db2uri(params)];
- if not engine then
- module:log("debug", "Creating new engine");
- engine = sql:create_engine(params);
- engines[sql.db2uri(params)] = engine;
- end
- engine:connect();
- module:log("debug", "load OK");
-end
diff --git a/plugins/mod_sipwise_vhosts_sql.lua b/plugins/mod_sipwise_vhosts_sql.lua
deleted file mode 100644
index 3dd60b4..0000000
--- a/plugins/mod_sipwise_vhosts_sql.lua
+++ /dev/null
@@ -1,177 +0,0 @@
--- Load vhosts from DB on startup of Prosody XMPP server.
--- Copyright (C) 2013-2015 Sipwise GmbH
---
--- This project is MIT/X11 licensed. Please see the
--- COPYING file in the source package for more information.
---
-
-module:set_global();
-
-local DBI = require "DBI"
-local nameprep = require "util.encodings".stringprep.nameprep;
-local hostmanager = require "core.hostmanager";
-local configmanager = require "core.configmanager";
-local ut = require "ngcp.utils";
-local set = require "util.set";
-
-local connection;
-local params = module:get_option("auth_sql", module:get_option("sql"));
-local prosody = _G.prosody;
-local vhosts = set.new();
-
-local function test_connection()
- module:log("debug", "test_connection");
- if not connection then return nil; end
- if connection:ping() then
- module:log("debug", "test_connection ok");
- return true;
- else
- module:log("debug", "Database connection closed");
- connection = nil;
- end
-end
-local function connect()
- module:log("debug", "connect");
- if not test_connection() then
- prosody.unlock_globals();
- local dbh, err = DBI.Connect(
- params.driver, params.database,
- params.username, params.password,
- params.host, params.port
- );
- prosody.lock_globals();
- if not dbh then
- module:log("debug",
- "Database connection failed: %s", tostring(err));
- return nil, err;
- end
- module:log("debug", "Successfully connected to database");
- dbh:autocommit(true); -- don't run in transaction
- connection = dbh;
- return connection;
- end
-end
-
-do -- process options to get a db connection
- params = params or { driver = "SQLite3" };
-
- if params.driver == "SQLite3" then
- params.database = configmanager.resolve_relative_path(
- prosody.paths.data or ".",
- params.database or "prosody.sqlite");
- end
-
- assert(params.driver and params.database,
- "Both the SQL driver and the database need to be specified");
- assert(connect());
-end
-
-local function getsql(sql, ...)
- if params.driver == "PostgreSQL" then
- sql = sql:gsub("`", "\"");
- end
- if not test_connection() then connect(); end
- -- do prepared statement stuff
- local stmt, err = connection:prepare(sql);
- if not stmt and not test_connection() then error("connection failed"); end
- if not stmt then
- module:log("error", "QUERY FAILED: %s %s", err, debug.traceback());
- return nil, err;
- end
- -- run query
- local ok
- ok, err = stmt:execute(...);
- if not ok and not test_connection() then error("connection failed"); end
- if not ok then return nil, err; end
-
- return stmt;
-end
-
-local function activate_search(host)
- module:log("debug", "activate implicit search.%s", host);
- configmanager.set("search."..host, "component_module",
- "sipwise_vjud");
- local search_config = configmanager.getconfig()["search."..host];
- hostmanager.activate("search."..host, search_config);
-end
-
-local function activate_muc(host)
- local host_modules = configmanager.get(host, "modules_enabled");
- local conference_modules = {};
-
- module:log("debug", "modules_enabled[%s]: %s", host,
- ut.table.tostring(host_modules));
-
- configmanager.set("conference."..host, "component_module",
- "muc");
- if ut.table.contains(host_modules, "shard") then
- ut.table.add(conference_modules, "sipwise_redis_mucs");
- ut.table.add(conference_modules, "shard");
- end
- if ut.table.contains(host_modules, "sipwise_pushd") then
- ut.table.add(conference_modules, "sipwise_pushd");
- end
- if ut.table.contains(host_modules, "mam") then
- ut.table.add(conference_modules, "muc_mam");
- end
- module:log("debug", "conference_modules[%s]: %s",
- "conference."..host, ut.table.tostring(host_modules));
- configmanager.set("conference."..host, "modules_enabled",
- conference_modules);
- local conference_config = configmanager.getconfig()["conference."..host];
- conference_config['restrict_room_creation'] = 'local';
- hostmanager.activate("conference."..host, conference_config);
- module:log("debug", "modules_enabled[%s]: %s", "conference."..host,
- ut.table.tostring(conference_config['modules_enabled']));
-end
-
-local function add_vhost(host)
- local host_config = { enable = true };
-
- module:log("debug", "activate host %s", host);
- vhosts:add(host);
- hostmanager.activate(host, host_config);
-
- activate_search(host);
- activate_muc(host);
-end
-
-local function load_vhosts_from_db()
- local stmt, _ = getsql("SELECT `domain` FROM `domain`");
- if stmt then
- for row in stmt:rows(true) do
- local host = nameprep(row.domain);
- add_vhost(host);
- end
- end
-end
-
-local function check_vhosts(host)
- if ut.string.starts(host, 'search.') or
- ut.string.starts(host, 'conference.') or
- vhosts:contains(host) then
- module:log("debug", "%s activated, nothing to do", host);
- return
- end
- module:log("info", "%s activate subdomains", host);
- activate_search(host);
- activate_muc(host);
- vhosts:add(host);
-end
-
-local function deactivate_vhost(host)
- if ut.string.starts(host, 'search.') or
- ut.string.starts(host, 'conference.') or
- not vhosts:contains(host) then
- module:log("debug", "%s deactivated, nothing to do", host);
- return
- end
- module:log("info", "%s deactivate subdomains", host);
- hostmanager.deactivate("search."..host, 'main domain deactivated');
- hostmanager.deactivate("conference."..host, 'main domain deactivated');
- vhosts:remove(host);
-end
-
-module:hook("server-started", load_vhosts_from_db);
-module:hook("host-activated", check_vhosts);
-module:hook("host-deactivated", deactivate_vhost);
diff --git a/plugins/mod_sipwise_vjud.lua b/plugins/mod_sipwise_vjud.lua
deleted file mode 100644
index 85af47d..0000000
--- a/plugins/mod_sipwise_vjud.lua
+++ /dev/null
@@ -1,336 +0,0 @@
---
--- Copyright (C) 2013-2015 Sipwise GmbH
---
--- This project is MIT/X11 licensed. Please see the
--- COPYING file in the source package for more information.
---
-module:depends("disco");
-local softreq = require "util.dependencies".softreq;
-local ut_jid = require "util.jid";
-local sql = require "util.sql";
-local st = require "util.stanza";
-local template = require "util.template";
-local prosodyctl = require "util.prosodyctl"
-local dataforms_new = require "util.dataforms".new;
-local ut = require "ngcp.utils";
-local hosts = prosody.hosts;
-local rex = softreq "rex_pcre";
-if not rex then
- rex = require "rex_pcre2";
-end
-
-local form_layout = dataforms_new{
- title= 'User Directory Search';
- instructions = 'Please provide the following information to search for subscribers';
- {
- type = 'text-single',
- label = 'e164 Phone number',
- name = 'e164',
- required = false,
- },
- {
- type = 'text-single',
- label = 'domain',
- name = 'domain',
- required = false,
- },
-};
-
-local form_item = template[[
- -
-
- {value}
-
-
-]];
-
-local form_reply = {
- domain = template[[
-
-
-
- jabber:iq:search
-
-
-
-
-
-
-]],
- e164 = template[[
-
-
-
- jabber:iq:search
-
-
-
-
-
-
-]],
- form = template[[
-
- Use the enclosed form to search
-
- User Directory Search
- Please provide the following information to search for subscribers
-
- jabber:iq:search
-
-
-
-
-
-
-]]
-};
-
-local usr_replacements_query = [[
-SELECT vrr.match_pattern, vrr.replace_pattern FROM provisioning.voip_preferences vp
- LEFT JOIN provisioning.voip_usr_preferences vup ON vup.attribute_id = vp.id
- LEFT JOIN provisioning.voip_subscribers vs ON vs.id = vup.subscriber_id
- LEFT JOIN provisioning.voip_domains vd ON vd.id = vs.domain_id
- LEFT JOIN provisioning.voip_rewrite_rule_sets vrrs ON vrrs.callee_in_dpid = vup.value
- LEFT JOIN provisioning.voip_rewrite_rules vrr ON vrr.set_id = vrrs.id
- AND vrr.direction = 'in'
- AND vrr.field = 'callee'
-WHERE vp.attribute = 'rewrite_callee_in_dpid' AND vs.username = ? AND vd.domain = ?
- ORDER BY vrr.priority ASC;
-]];
-
-local dom_replacements_query = [[
-SELECT vrr.match_pattern, vrr.replace_pattern FROM provisioning.voip_preferences vp
- LEFT JOIN provisioning.voip_dom_preferences vdp ON vdp.attribute_id = vp.id
- LEFT JOIN provisioning.voip_domains vd ON vd.id = vdp.domain_id
- LEFT JOIN provisioning.voip_rewrite_rule_sets vrrs ON vrrs.callee_in_dpid = vdp.value
- LEFT JOIN provisioning.voip_rewrite_rules vrr ON vrr.set_id = vrrs.id
- AND vrr.direction = 'in' AND vrr.field = 'callee'
-WHERE vp.attribute = 'rewrite_callee_in_dpid' AND vd.domain = ?
- ORDER BY vrr.priority ASC;
-]];
-
-local locale_query = [[
-SELECT vp.attribute, vup.value FROM provisioning.voip_preferences vp
- LEFT JOIN provisioning.voip_usr_preferences vup ON vup.attribute_id = vp.id
- LEFT JOIN provisioning.voip_subscribers vs ON vs.id = vup.subscriber_id
- LEFT JOIN provisioning.voip_domains vd ON vd.id = vs.domain_id
-WHERE (vp.attribute = 'ac' or vp.attribute = 'cc')
- AND vs.username = ?
- AND vd.domain = ?;
-]];
-
-local lookup_query = [[
-SELECT username,domain FROM kamailio.dbaliases
-WHERE alias_username=?;
-]];
-
-local default_params = module:get_option("sql");
-local engine;
-
--- Reconnect to DB if necessary
-local function reconect_check()
- if not engine.conn:ping() then
- engine.conn = nil;
- engine:connect();
- end
-end
-
-local function normalize_number(user, host, number)
- local locale_info = {};
- reconect_check();
- for row in engine:select(locale_query, user, host) do
- locale_info["caller_"..row[1]] = row[2];
- end
-
- local replacement_regexes = {};
- local usr_pref = 0;
- for row in engine:select(usr_replacements_query, user, host) do
- usr_pref = 1;
- module:log("debug", "user rewrite_callee_in_dpid preference found");
- local patt, repl = row[1], row[2]
- :gsub("%$avp%(s:([a-zA-Z_]+)%)", locale_info)
- :gsub("\\", "%%"):gsub("%%%%", "\\");
- table.insert(replacement_regexes, { patt, repl });
- end
- if usr_pref == 0 then
- for row in engine:select(dom_replacements_query, host) do
- module:log("debug", "domain rewrite_callee_in_dpid preference found");
- local patt, repl = row[1], row[2]
- :gsub("%$avp%(s:([a-zA-Z_]+)%)", locale_info)
- :gsub("\\", "%%"):gsub("%%%%", "\\");
- table.insert(replacement_regexes, { patt, repl });
- end
- end
-
- for _, rule in ipairs(replacement_regexes) do
- local new_number, n_matches = rex.gsub(number, rule[1], rule[2]);
- if n_matches > 0 then
- module:log("debug", "rule [%s] matched [%s]->[%s]",
- tostring(rule[1]), tostring(number), tostring(new_number));
- number = new_number;
- break;
- end
- end
- return number;
-end
-
-local function search_by_number(number)
- local results = {};
- reconect_check();
- module:log("debug", "search jids with number:[%s]", tostring(number));
- for result in engine:select(lookup_query, number) do
- table.insert(results, result[1].."@"..result[2]);
- end
- return results;
-end
-
-local function get_dataform_items(vals, fieldname)
- local res = {};
-
- for _,v in ipairs(vals) do
- table.insert(res, form_item.apply({ name = fieldname, value = v }));
- end
- return res;
-end
-
-local function search_domains(dom)
- local hosts_keys = ut.table.keys(hosts);
- local res = {};
-
- module:log("debug", "search for domain: %s", tostring(dom));
- if dom == '*' then
- res = get_dataform_items(hosts_keys, 'domain');
- elseif ut.table.contains(hosts_keys, dom) then
- res = get_dataform_items({dom,}, 'domain');
- end
- return res;
-end
-
-function module.command(arg)
- local warn = prosodyctl.show_warning;
- local command = arg[1];
- if not command then
- warn("Valid subcommands: normalize");
- return 0;
- end
- table.remove(arg, 1);
- if command == "normalize" then
- if #arg ~= 2 then
- warn("Usage: normalize USER@HOST NUMBER");
- return 1;
- end
- local user_jid, number = arg[1], arg[2];
- local user, host = ut_jid.prepped_split(user_jid);
- if not (user and host) then
- warn("Invalid JID: "..user_jid);
- return 1;
- end
- print(normalize_number(user, host, number));
- elseif command == "query" then
- if #arg ~= 1 then
- warn("Usage: query NUMBER");
- warn(" NUMBER must be normalized (see 'normalize' command)");
- return 1;
- end
- local results = search_by_number(arg[1]);
- for _, jid in ipairs(results) do
- print("", jid);
- end
- end
- return 0;
-end
-
-local function append_x_items(form, values, reply)
- local x = form:get_child('x', 'jabber:x:data');
- for _, v in ipairs(values) do
- x:add_child(v):up();
- end
- reply:add_child(x);
-end
-
-if module:get_host_type() ~= "component" then
- error("Don't load mod_sipwise_vjud manually,"..
- " it should be for a component", 0);
-end
-
-module:add_feature("jabber:iq:search");
-module:add_feature("jabber:x:data"); -- dataforms
-module:hook("iq/host/jabber:iq:search:query", function(event)
- local origin, stanza = event.origin, event.stanza;
- module:log("debug", "stanza[%s]", tostring(stanza));
- if stanza.attr.type == "get" then
- return origin.send(st.reply(stanza):add_child(form_reply.form.apply({})));
- else
-
- local user, host = ut_jid.split(stanza.attr.from);
- local query = stanza:get_child("query",'jabber:iq:search');
- local form_stanza = query:get_child("x",'jabber:x:data');
- local reply;
- local search_number = stanza.tags[1]:get_child_text("nick");
- local search_domain
-
- if form_stanza then
- local form_data, form_errors = form_layout:data(form_stanza);
- if form_errors and form_errors['e164'] then
- reply = st.error_reply(stanza, "modify",
- "bad-request", "e164: "..form_errors['e164']);
- return origin.send(reply);
- else
- search_number = form_data['e164'] or search_number;
- search_domain = form_data['domain'];
- end
- end
-
- if search_number then
- local number = normalize_number(user, host, search_number);
- local data = search_by_number(number);
- reply = st.reply(stanza):query("jabber:iq:search");
- if form_stanza then
- append_x_items(form_reply.e164.apply({}),
- get_dataform_items(data, 'e164'), reply);
- else
- for _, jid in ipairs(data) do
- reply:tag("item", { jid = jid }):up();
- end
- end
- elseif search_domain then
- reply = st.reply(stanza):query("jabber:iq:search");
- append_x_items(form_reply.domain.apply({}),
- search_domains(search_domain), reply);
- else
- reply = st.error_reply(stanza, "modify",
- "bad-request", "no domain, nick or e164 field found");
- end
-
- return origin.send(reply);
- end
-end);
-
-local function normalize_params(params)
- assert(params.driver and params.database,
- "Configuration error: Both the SQL driver and the database need to be specified");
- return params;
-end
-
-function module.load()
- if prosody.prosodyctl then return; end
- local engines = module:shared("/*/sql/connections");
- local params = normalize_params(module:get_option("auth_sql", default_params));
- engine = engines[sql.db2uri(params)];
- if not engine then
- module:log("debug", "Creating new engine");
- engine = sql:create_engine(params);
- engines[sql.db2uri(params)] = engine;
- end
- engine:connect();
- module:log("debug", "load OK");
-end
diff --git a/plugins/mod_smacks/README.markdown b/plugins/mod_smacks/README.markdown
deleted file mode 100644
index 9326e6e..0000000
--- a/plugins/mod_smacks/README.markdown
+++ /dev/null
@@ -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
diff --git a/plugins/mod_smacks/mod_smacks.lua b/plugins/mod_smacks/mod_smacks.lua
deleted file mode 100644
index 96c4a18..0000000
--- a/plugins/mod_smacks/mod_smacks.lua
+++ /dev/null
@@ -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 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 (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 (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 (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
- (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 with the h of the 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 (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 (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);
diff --git a/plugins/mod_throttle_presence/README.markdown b/plugins/mod_throttle_presence/README.markdown
deleted file mode 100644
index c953ad6..0000000
--- a/plugins/mod_throttle_presence/README.markdown
+++ /dev/null
@@ -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
- ----- -------
diff --git a/plugins/mod_throttle_presence/mod_throttle_presence.lua b/plugins/mod_throttle_presence/mod_throttle_presence.lua
deleted file mode 100644
index 9792391..0000000
--- a/plugins/mod_throttle_presence/mod_throttle_presence.lua
+++ /dev/null
@@ -1,53 +0,0 @@
-local filters = require "util.filters";
-local st = require "util.stanza";
-
-module:depends("csi");
-
-local function presence_filter(stanza, session)
- if getmetatable(stanza) ~= st.stanza_mt then
- return stanza; -- Things we don't want to touch
- end
- if stanza._flush then
- stanza._flush = nil;
- return stanza;
- end
- local buffer = session.presence_buffer;
- local from = stanza.attr.from;
- if stanza.name == "presence" and (stanza.attr.type == nil or stanza.attr.type == "unavailable") then
- module:log("debug", "Buffering presence stanza from %s to %s", stanza.attr.from, session.full_jid);
- buffer[stanza.attr.from] = st.clone(stanza);
- return nil; -- Drop this stanza (we've stored it for later)
- else
- local cached_presence = buffer[stanza.attr.from];
- if cached_presence then
- module:log("debug", "Important stanza for %s from %s, flushing presence", session.full_jid, from);
- stanza._flush = true;
- cached_presence._flush = true;
- session.send(cached_presence);
- buffer[stanza.attr.from] = nil;
- end
- end
- return stanza;
-end
-
-local function throttle_session(event)
- local session = event.origin;
- if session.presence_buffer then return; end
- module:log("debug", "Suppressing presence updates to %s", session.full_jid);
- session.presence_buffer = {};
- filters.add_filter(session, "stanzas/out", presence_filter);
-end
-
-local function restore_session(event)
- local session = event.origin;
- if not session.presence_buffer then return; end
- filters.remove_filter(session, "stanzas/out", presence_filter);
- module:log("debug", "Flushing buffer for %s", session.full_jid);
- for jid, presence in pairs(session.presence_buffer) do
- session.send(presence);
- end
- session.presence_buffer = nil;
-end
-
-module:hook("csi-client-inactive", throttle_session);
-module:hook("csi-client-active", restore_session);
diff --git a/prosody-modules.list b/prosody-modules.list
deleted file mode 100644
index fc12045..0000000
--- a/prosody-modules.list
+++ /dev/null
@@ -1,9 +0,0 @@
-mod_auth_sql
-mod_blocking
-mod_carbons_adhoc
-mod_carbons_copies
-mod_filter_chatstates
-mod_limit_auth
-mod_log_auth
-mod_smacks
-mod_throttle_presence
diff --git a/prosody-modules.revision b/prosody-modules.revision
deleted file mode 100644
index 378bfa8..0000000
--- a/prosody-modules.revision
+++ /dev/null
@@ -1 +0,0 @@
-ecfd7aece33b
diff --git a/t/Dockerfile b/t/Dockerfile
deleted file mode 100644
index 64765de..0000000
--- a/t/Dockerfile
+++ /dev/null
@@ -1,33 +0,0 @@
-# DOCKER_NAME=prosody-bookworm
-FROM docker.mgm.sipwise.com/sipwise-bookworm:latest
-
-# Important! Update this no-op ENV variable when this Dockerfile
-# is updated with the current date. It will force refresh of all
-# of the base images and things like `apt-get update` won't be using
-# old cached versions when the Dockerfile is built.
-ENV REFRESHED_AT 2023-06-26
-
-RUN apt-get update && apt-get install --assume-yes lua-check
-
-RUN echo './t/testrunner' >>/root/.bash_history
-
-WORKDIR /code/
-
-################################################################################
-# Instructions for usage
-# ----------------------
-# When you want to build the base image from scratch
-# jump to the next section if you don't want to build yourself!:
-#
-# % docker build --tag="prosody-bookworm" -f t/Dockerfile .
-# % docker run --rm -i -t -v $(pwd):/code:rw prosody-bookworm:latest bash
-#
-# Use the existing docker image:
-# % docker pull docker.mgm.sipwise.com/prosody-bookworm
-# NOTE: run the following command from root folder of git repository:
-# % docker run --rm -i -t -v $(pwd):/code:rw docker.mgm.sipwise.com/prosody-bookworm:latest bash
-#
-# Inside docker (the command is in history, just press UP button):
-# ./t/testrunner
-#
-################################################################################
diff --git a/t/testrunner b/t/testrunner
deleted file mode 100755
index 44061f5..0000000
--- a/t/testrunner
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/bin/bash
-
-set -e
-set -u
-
-if [ -d /results ] ; then
- # Running from Jenkins (RW)
- RESULTS="/results"
-else
- # Running locally in Docker
- RESULTS="./results"
- mkdir -p "${RESULTS}"
-fi
-
-luacheck --formatter JUnit core plugins util > ${RESULTS}/prosody.luacheck
diff --git a/update_plugins.sh b/update_plugins.sh
deleted file mode 100755
index 8b5b6b8..0000000
--- a/update_plugins.sh
+++ /dev/null
@@ -1,44 +0,0 @@
-#!/bin/bash
-#
-set -e
-
-MOD_SRC=$1
-
-usage() {
- echo "$0 modules_src_dir"
- printf "\tmodules_src_dir:\tpath to prosody-modules mercurial source\n"
-}
-
-refresh_sources() {
- echo "refresh sources at $MOD_SRC"
- ( cd "$MOD_SRC" && hg pull && hg update )
-}
-
-copy_modules() {
- while read -r dir ; do
- if [ -d "$MOD_SRC/$dir" ] ; then
- cp -vr "$MOD_SRC/$dir" plugins
- else
- echo "$MOD_SRC/$dir no longer there"
- fi
- done < prosody-modules.list
-}
-
-get_revision_id() {
- (cd "$MOD_SRC/$dir" && hg id -i) > 'prosody-modules.revision'
-}
-
-if [ $# -ne 1 ] ; then
- echo "wrong number of parameters" >&2
- usage
- exit 1
-fi
-
-if ! [ -d "$MOD_SRC" ] ; then
- printf "modules_src_dir[%s] not found\n" "$MOD_SRC"
-fi
-
-refresh_sources
-copy_modules
-get_revision_id
-exit 0
diff --git a/util/prosody_sipwise_migrator b/util/prosody_sipwise_migrator
deleted file mode 100755
index a612b70..0000000
--- a/util/prosody_sipwise_migrator
+++ /dev/null
@@ -1,160 +0,0 @@
-#!/usr/bin/env lua5.2
-
-local CFG_SOURCEDIR='/usr/lib/prosody';
-local CFG_CONFIGDIR='/etc/prosody';
-
-local mam_query =[[
-select `id`, `username`,UuidFromBin(`key`),`stanza`,`epoch`,`with`
-from sipwise_mam as mam
-where UuidFromBin(`key`) not in (select `key` from prosodyarchive)
-order by username, id asc;
-]]
-
-local mam_del =[[
-delete from sipwise_mam where id = ?;
-]]
-
-local archive_insert =[[
-insert into `prosodyarchive` (`host`,`user`,`store`,`key`,`when`,`with`,`type`,`value`)
-values(?,?,'archive',?,?,?,'xml',?);
-]]
-
-local offline_query =[[
-select `id`, `domain`, `username`, `stanza`
-from sipwise_offline
-order by username, id asc;
-]]
-
-local offline_del = [[
-delete from sipwise_offline where id = ?;
-]]
-
-local offline_insert =[[
-insert into `prosodyarchive` (`host`,`user`,`store`,`key`,`when`,`with`,`type`,`value`)
-values(?,?,'offline',?,?,'','xml',?);
-]]
-
-local prosodyarchive_count =[[
-select count(sort_id) from prosodyarchive;
-]]
-
-local function is_relative(path)
- local path_sep = package.config:sub(1,1);
- return ((path_sep == "/" and path:sub(1,1) ~= "/")
- or (path_sep == "\\" and (path:sub(1,1) ~= "/" and path:sub(2,3) ~= ":\\")))
-end
-
--- Tell Lua where to find our libraries
-if CFG_SOURCEDIR then
- local function filter_relative_paths(path)
- if is_relative(path) then return ""; end
- end
- local function sanitise_paths(paths)
- return (paths:gsub("[^;]+;?", filter_relative_paths):gsub(";;+", ";"));
- end
- package.path = sanitise_paths(CFG_SOURCEDIR.."/?.lua;"..package.path);
- package.cpath = sanitise_paths(CFG_SOURCEDIR.."/?.so;"..package.cpath);
-end
-
-local configmanager = require "core.configmanager";
-local ok, _, err = configmanager.load(CFG_CONFIGDIR.."/prosody.cfg.lua", "lua")
-
-if not ok then
- print("Error: can't load the config file."..err);
- os.exit(1);
-end
-
-local sql_config = configmanager.get("*", "sql");
-
-if not sql_config then
- print("Error: sql not found in the config file.");
- os.exit(1);
-end
-
-local deserialize = require "util.serialization".deserialize;
-local st = require "util.stanza";
-local sql = require "util.sql";
-local jid_split = require "util.jid".split;
-local uuid = require "util.uuid".generate;
-local timestamp_parse = require "util.datetime".parse;
-
-local engine = sql:create_engine(sql_config);
-engine:connect();
-
--- from mod_storage_sql.lua of prosody 0.11.3
-local function create_table()
- local Table, Column, Index = sql.Table, sql.Column, sql.Index;
-
- local ProsodyArchiveTable = Table {
- name="prosodyarchive";
- Column { name="sort_id", type="INTEGER", primary_key=true, auto_increment=true };
- Column { name="host", type="TEXT", nullable=false };
- Column { name="user", type="TEXT", nullable=false };
- Column { name="store", type="TEXT", nullable=false };
- Column { name="key", type="TEXT", nullable=false }; -- item id
- Column { name="when", type="INTEGER", nullable=false }; -- timestamp
- Column { name="with", type="TEXT", nullable=false }; -- related id
- Column { name="type", type="TEXT", nullable=false };
- Column { name="value", type="MEDIUMTEXT", nullable=false };
- Index { name="prosodyarchive_index", unique = engine.params.driver ~= "MySQL", "host", "user", "store", "key" };
- Index { name="prosodyarchive_with_when", "host", "user", "store", "with", "when" };
- Index { name="prosodyarchive_when", "host", "user", "store", "when" };
- };
- engine:transaction(function()
- ProsodyArchiveTable:create(engine);
- end);
-end
-
-local function check_prosodyarchive()
- for row in engine:select(prosodyarchive_count) do --luacheck: ignore
- return row[1]
- end
-end
-
-local function migrate_mam()
- local i = 0
- for row in engine:select(mam_query) do
- local user, host, _ = jid_split(row[2]);
- -- print("id:"..row[1].." username:"..row[2].." uuid:"..row[3]);
- local stanza = st.deserialize(deserialize(row[4]));
- -- print("stanza:"..tostring(stanza));
- engine:insert(archive_insert, host, user, row[3], row[5], row[6], tostring(stanza));
- engine:delete(mam_del, row[1]);
- i = i + 1;
- end
- if i > 0 then
- engine.conn:commit();
- print("migrated "..i.." mam messages");
- end
-end
-
-local function migrate_offline()
- local i = 0
- for row in engine:select(offline_query) do
- -- print("id:"..row[1].." username:"..row[3].." domain:"..row[2]);
- local stanza = st.deserialize(deserialize(row[4]));
- local when = timestamp_parse(stanza.attr.stamp)
- stanza.attr.stamp, stanza.attr.stamp_legacy = nil, nil;
- engine:insert(offline_insert, row[2], row[3], uuid(), when, tostring(stanza));
- engine:delete(offline_del, row[1]);
- i = i + 1;
- end
- if i > 0 then
- engine.conn:commit();
- print("migrated "..i.." offline messages");
- end
-end
-
-if arg[1] == '--create' then
- if pcall(check_prosodyarchive) then
- print("prosodyarchive already there");
- else
- create_table();
- if check_prosodyarchive() then
- print("prosodyarchive created");
- end
- end
-else
- migrate_mam();
- migrate_offline();
-end