- Add AGENTS.md with repo-specific conventions, build steps, and quirks - Add PATCHES_AND_MODULES.md documenting every upstream deviation - Add tests/ with pytest/slixmpp integration suite for core, MUC, vnctalk extensions, and infrastructure verification - Include pytest.ini and .gitignore Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
269 lines
14 KiB
Markdown
269 lines
14 KiB
Markdown
# VNCtalk Prosody Patches & Custom Modules Analysis
|
|
|
|
This document describes how the VNCtalk Prosody distribution deviates from upstream Prosody 0.11.6. The deviations are delivered as:
|
|
- **`patches/`** — direct replacements for upstream core files
|
|
- **`vnctalk/`** — additional custom modules
|
|
|
|
> **Note:** `patches.list` is stale and unused. The Dockerfile hard-copies each patch in a `RUN cp …` block.
|
|
|
|
---
|
|
|
|
## 1. Core Patches (`patches/`)
|
|
|
|
### 1.1 `mod_mam.lua` → `modules/mod_mam/mod_mam.lua`
|
|
**Purpose:** Message Archive Management (XEP-0313) for 1:1 chats.
|
|
|
|
**Key deviations from upstream:**
|
|
- **`vnc-rest-message` hook support:** Added `vnc_message_handler(event)` hooked on `vnc-rest-message` (priority 0). This allows the REST injection module (`mod_http_rest`) to archive messages that are injected via HTTP.
|
|
- **Store-user logic for REST:** When `vnc_rest` is true, `store_user` is derived from `orig_from` instead of `orig_to`, so injected stanzas are archived under the sender's archive.
|
|
- **`shall_store` always returns true:** The function `shall_store(user, who)` unconditionally returns `true`, bypassing any roster-based archive filtering.
|
|
- **Archives normal messages with body:** The condition `orig_type == "chat" or (orig_type == "normal" and stanza:get_child("body"))` is kept but the implementation now also processes `vnc-rest-message` events.
|
|
|
|
**Behavioural contract:**
|
|
- Any message injected via `/rest` (see `mod_http_rest`) must be archived under the sender's MAM archive.
|
|
- MAM queries must return `stanza-id` elements and support RSM pagination.
|
|
|
|
---
|
|
|
|
### 1.2 `mod_muc.lua` → `modules/muc/mod_muc.lua`
|
|
**Purpose:** MUC component loader and room lifecycle management.
|
|
|
|
**Key deviations from upstream:**
|
|
- **Custom unregister IQ hook:** Added `iq-set/bare/xmpp:vnctalk:unregister:query` → `handle_unregister_iq`. This exposes a VNCtalk-specific room-unregistration endpoint.
|
|
- **Debug helper:** Added `dumpTable(t, depth)` utility function.
|
|
- **Room defaults:** `muc_room_default_public` defaults to `false` (rooms are hidden by default).
|
|
|
|
---
|
|
|
|
### 1.3 `mod_muc_mam.lua` → `modules/mod_muc_mam.lua`
|
|
**Purpose:** MUC Message Archive Management.
|
|
|
|
**Key deviations from upstream:**
|
|
- **`with` filter hardcoded to `message<groupchat`:** The archive query and history loader both set `with = "message<groupchat"`, meaning only groupchat messages are stored/retrieved; presence stanzas are ignored unless `muc_log_presences` is enabled.
|
|
- **Presence archiving option:** If `muc_log_presences` is true, join/leave presences are archived with synthetic `with` values (`presence` / `presence<unavailable`).
|
|
- **MUC history from archive:** The `muc-get-history` hook loads history from the archive backend when the in-memory `_history` buffer is insufficient, respecting `max_history_messages`.
|
|
- **Stanza-id stripping before broadcast:** A `muc-broadcast-message` hook (priority 1) strips any `stanza-id` tags that claim to be from the room JID before the stanza is broadcast.
|
|
- **Occupant affiliation injection:** When `whois == "anyone"`, archived messages include an `<x xmlns="http://jabber.org/protocol/muc#user">` item with the sender's affiliation and role.
|
|
|
|
---
|
|
|
|
### 1.4 `mod_muc_unique.lua` → `modules/mod_muc_unique.lua`
|
|
**Purpose:** XEP-0307 Unique Room Names.
|
|
|
|
**Key deviations from upstream:**
|
|
- **Bare-JID request returns error:** Added `handle_iq_tobare` that replies with `item-not-found` for IQ-get requests sent to a bare JID, rather than generating a unique name. Unique names are only served for host-targeted requests.
|
|
|
|
---
|
|
|
|
### 1.5 `moduleapi.lua` → `core/moduleapi.lua`
|
|
**Purpose:** Module API base.
|
|
|
|
**Key deviations from upstream:**
|
|
- **Added `open_host_store`:** New method `api:open_host_store(host, name, store_type)` allows a module to open a data store on behalf of a different host. This is heavily used by MUC FCM modules to read user private data / vCards from the main virtual host while running inside the MUC component.
|
|
|
|
---
|
|
|
|
### 1.6 `muc.lib.lua` → `modules/muc/muc.lib.lua`
|
|
**Purpose:** MUC room implementation (the largest patch).
|
|
|
|
**Key deviations from upstream:**
|
|
- **Offline-affiliate broadcast (`broadcast` method):** After broadcasting to all online occupants, the method iterates over `_affiliations` and sends the stanza to any affiliated JIDs that are **not** currently in the room, **provided their domain is not hosted locally**. This enables federation/remote-user delivery.
|
|
- **`publicise_occupant_status` skip for unavailable:** When an occupant's role becomes `nil` (they left), the code logs `"skipping unavailable presence"` and does **not** route an unavailable presence to the leaving user themselves. This changes the standard XEP-0045 self-presence delivery.
|
|
- **Debug helpers:** Added `dumpTable` and `table_clone`.
|
|
|
|
---
|
|
|
|
### 1.7 `hidden.lib.lua` → `modules/muc/hidden.lib.lua`
|
|
**Purpose:** Room visibility (public/hidden).
|
|
|
|
**Key deviations from upstream:**
|
|
- **Restricted public rooms:** If `muc_room_allow_public` is `false` (default in VNCtalk config), the public-room config option is hidden from non-admins, and only admins may create public rooms.
|
|
|
|
---
|
|
|
|
### 1.8 `register.lib.lua` → `modules/muc/register.lib.lua`
|
|
**Purpose:** MUC room nickname registration.
|
|
|
|
**Key deviations from upstream:**
|
|
- **VNCtalk unregister handler (`handle_unregister_iq`):** Fires `vnc-muc-kick` event and unconditionally removes the user's affiliation from `_affiliations`, bypassing normal affiliation-change logic. This is used for VNCtalk-specific account deletion/room cleanup.
|
|
|
|
---
|
|
|
|
### 1.9 `mod_carbons.lua` → `modules/mod_carbons.lua`
|
|
**Purpose:** XEP-0280 Message Carbons.
|
|
|
|
**Key deviations from upstream:**
|
|
- **`vnc-rest-message` hook:** Added `module:hook("vnc-rest-message", c2s_message_handler, -0.5)`. Messages injected via the REST endpoint are carbon-copied to the user's other resources just like locally-sent messages.
|
|
|
|
---
|
|
|
|
### 1.10 `mod_admin_telnet.lua` → `modules/mod_admin_telnet.lua`
|
|
**Purpose:** Admin telnet console.
|
|
|
|
**Key deviations from upstream:**
|
|
- **Listens on all interfaces:** Changed `interface` from `"127.0.0.1"` to `"*"`. The healthcheck script (`healthcheck.sh`) relies on connecting to `127.0.0.1:5582`, but the console is now bound to `0.0.0.0`.
|
|
|
|
---
|
|
|
|
### 1.11 `portmanager.lua` → `core/portmanager.lua`
|
|
**Purpose:** Network port management.
|
|
|
|
**Key deviations from upstream:**
|
|
- **Respects `network_default_read_size` for socket mode:** `local default_mode = config.get("*", "network_default_read_size") or 4096;` instead of a hardcoded `4096`. The VNCtalk config sets this to `8192` to support larger stanzas (Jitsi/file transfers).
|
|
|
|
---
|
|
|
|
## 2. Custom Modules (`vnctalk/`)
|
|
|
|
### 2.1 Authentication
|
|
|
|
#### `mod_auth_http_async`
|
|
- **HTTP-based authentication.** Replaces Prosody's internal password database with an async HTTP call to `http_auth_url`.
|
|
- Sends a `Basic` auth header with `base64(username@host:password)`.
|
|
- If `util.async` is unavailable, falls back to synchronous `socket.http` / `ssl.https`.
|
|
- `user_exists` always returns `true`; `set_password` / `create_user` / `delete_user` are no-ops.
|
|
|
|
---
|
|
|
|
### 2.2 Push Notifications (FCM)
|
|
|
|
#### `mod_vnc_fcm`
|
|
- **FCM push for 1:1 messages.** Hooks on `message/bare`, `pre-message/bare/full`, and `vnc-rest-message`.
|
|
- Reads FCM tokens from private storage (`documents:stanza:io:json` → `fcm` element) and from a map store (`fcmtoken`).
|
|
- Supports iOS and Android tokens; iOS gets a notification payload with sound, Android gets data-only.
|
|
- Handles VNCtalk-specific extensions: `vncTalkConference`, `whiteboard`, `attachment`, `read` signals, Jitsi URL/room.
|
|
- Removes invalid FCM tokens (`NotRegistered`, `InvalidRegistration`, etc.) from user private data automatically.
|
|
- **Inactive-device tracking:** Uses `csi-client-active/inactive` hooks to avoid pushing to devices that are online but inactive.
|
|
|
|
#### `mod_vnc_fcm_hin`
|
|
- Variant of `mod_vnc_fcm` with HIN (German health network) specific modifications.
|
|
|
|
#### `mod_vnc_muc_fcm` / `mod_vnc_muc_fcm_hin`
|
|
- **FCM push for MUC messages.** Loaded on the MUC component.
|
|
- Opens stores against `storage_host` (the main virtual host) using `module:open_host_store`.
|
|
- On each groupchat message, iterates over room affiliations and pushes to offline/non-occupant members.
|
|
|
|
---
|
|
|
|
### 2.3 MUC Extensions
|
|
|
|
#### `mod_vnc_muc_automember`
|
|
- Automatically grants `member` affiliation to anyone who receives a MUC invite, if they were previously unaffiliated.
|
|
|
|
#### `mod_vnc_muc_hook`
|
|
- Sends a XMPP message notification (with `http://vnc.biz/xmpp/muc#hook` namespace) to online users who are affiliated with a room but not currently joined, when an "important" message (has non-empty body) is broadcast.
|
|
- Optionally sends a mediated invite instead of a plain notification (`muc_notification_invite`).
|
|
|
|
#### `mod_vnc_muc_data`
|
|
- Adds a `muc#roomconfig_vdata` config field and `muc#roominfo_vdata` disco info field.
|
|
- On config change, broadcasts a groupchat message with `<x xmlns='xmpp:vnctalk:update'/>` containing JSON-encoded affiliations and room data.
|
|
|
|
#### `mod_vnc_e2ehints`
|
|
- Adds a `muc#roomconfig_e2e` boolean field and `muc#roominfo_e2e` disco info field for end-to-end encryption hints.
|
|
|
|
#### `mod_vnc_remotemucstore`
|
|
- Archives groupchat messages from **remote** MUCs (federation) into a local archive (`muc_remote`) so users can query history even for rooms not hosted locally.
|
|
- Stores MUC presence metadata (real JID mapping, subject, nick) in a map store.
|
|
- Prevents further processing of messages addressed to bare JIDs from remote MUCs (so they don't create offline message spam).
|
|
|
|
#### `mod_vnc_remotemucinvite`
|
|
- Archives mediated MUC invitations sent to remote users in a store (`muc_remote_inv`).
|
|
|
|
#### `mod_vnc_track_kicks`
|
|
- Tracks kick/ban events in MUC rooms (lightweight event logger).
|
|
|
|
---
|
|
|
|
### 2.4 vCard & Avatar
|
|
|
|
#### `mod_vnc_vcard_avatar`
|
|
- On vCard update (IQ-set to self), extracts the PHOTO binval, decodes it, and uploads it via HTTP PUT to `avatar_upload_url`.
|
|
- Uses Basic auth if `avatar_upload_user` / `avatar_upload_pass` are configured.
|
|
- Stores an `avatarupdate` timestamp in a map store.
|
|
|
|
#### `mod_vnc_vcard_fallback`
|
|
- Intercepts vCard IQ-get requests.
|
|
- If the user has no vCard on file, auto-generates one from the username (dot-separated usernames become `FIRSTNAME LASTNAME`).
|
|
- Adds `ORGNAME` if `default_vcard_orgname` is configured.
|
|
|
|
---
|
|
|
|
### 2.5 Messaging Extensions
|
|
|
|
#### `mod_vnc_timestamp`
|
|
- Adds a custom `<stamp xmlns='xmpp:vnctalk:stamp'/>` tag to incoming chat/groupchat messages that have a body.
|
|
- Sends an IQ-result back to the sender containing the timestamp and original stanza ID.
|
|
|
|
#### `mod_vnc_receipts`
|
|
- Archives XEP-0184 delivery receipts (`<received xmlns='urn:xmpp:receipts'/>`) in an archive store (`receipts`).
|
|
|
|
#### `mod_vnc_lastactivity`
|
|
- Extends XEP-0012 last activity with avatar hash caching.
|
|
- Caches SHA1 of the user's vCard PHOTO in memory (`avt_hash_table`).
|
|
- Stores/retrieves remote user activity and avatar updates in map stores.
|
|
|
|
#### `mod_vnc_broadcast`
|
|
- Implements a `vnc_broadcast` component.
|
|
- Accepts messages with `<vncTalkBroadcast xmlns='xmpp:vnctalk'>` and fans them out to:
|
|
- Explicit `to` JIDs
|
|
- Roster groups (via `roster_manager.load_roster`)
|
|
- MUC room affiliations (if a `to` JID is a MUC)
|
|
|
|
---
|
|
|
|
### 2.6 HTTP / REST
|
|
|
|
#### `mod_http_rest`
|
|
- Exposes `POST /rest` on the HTTP server.
|
|
- Accepts `Content-Type: text/xml` bodies, parses them as XMPP stanzas, and injects them into the server via `module:fire_event("vnc-rest-message", …)`.
|
|
- Returns `201` on success, `415` for wrong content type, `422` for unparseable body.
|
|
|
|
#### `mod_http_upload_external`
|
|
- Delegates HTTP file upload to an external PHP endpoint (`share.php`).
|
|
- Generates signed URLs using `http_upload_external_secret`.
|
|
|
|
---
|
|
|
|
### 2.7 Utility Modules
|
|
|
|
| Module | Purpose |
|
|
|--------|---------|
|
|
| `mod_alias` | Allows users to have alias JIDs |
|
|
| `mod_roster_command` | Ad-hoc commands for roster management |
|
|
| `mod_s2s_keepalive` | Keepalive pings for S2S connections (Jitsi compatibility) |
|
|
| `mod_smacks` / `mod_smacks_offline` | Stream Management (XEP-0198) with offline queue support |
|
|
| `mod_carbons_copies` | Helper for carbons routing |
|
|
| `mod_log_slow_events` | Logs events exceeding `log_slow_events_threshold` |
|
|
| `mod_discoitems` | Manual override of disco items for a host |
|
|
| `mod_filter_chatstates` | Drops chat-state notifications under certain conditions |
|
|
| `mod_idlecompat` | Compatibility shim for idle detection |
|
|
| `mod_http_altconnect` | Alternative connection methods discovery |
|
|
| `mod_http_index` | Static HTTP index page |
|
|
| `mod_webpresence` | Publish presence as web images/icons |
|
|
| `mod_auto_accept_subscriptions` | Auto-accept presence subscriptions |
|
|
| `mod_vcard_muc` | vCard support for MUC rooms |
|
|
| `mod_vnc_delfile` | File deletion command via XMPP |
|
|
|
|
---
|
|
|
|
## 3. Configuration-Driven Behaviour
|
|
|
|
Several behaviours are not hard-coded in Lua but emerge from `config/prosody.cfg.lua.template`:
|
|
|
|
| Config Option | Effect |
|
|
|---------------|--------|
|
|
| `authentication = "http_async"` | All client auth goes to `hybridaAuthUrl` |
|
|
| `default_archive_policy = "roster"` | MAM archives only roster contacts by default |
|
|
| `muc_log_by_default = true` / `muc_log_all_rooms = true` | All MUC messages archived |
|
|
| `c2s_require_encryption = true` | Plaintext C2S rejected |
|
|
| `keepalive_servers = { "${DEFAULT_JITSI_CONFERENCE}" }` | S2S keepalive pings sent to Jitsi |
|
|
| `component_secret = "…"` | Fixed MUC component secret (not randomly generated) |
|
|
|
|
---
|
|
|
|
## 4. Healthcheck & Runtime Quirks
|
|
|
|
- `healthcheck.sh` exits **2** (not 1) when the TLS cert in `/etc/tls-update/tls.crt` differs from the loaded one. Orchestrators must treat exit code 2 as a restart signal.
|
|
- `startup-sidecar.sh` runs a Node `http-server` on port 8080 for static files; this is **not** the XMPP server.
|
|
- `startup.sh` generates fallback certs from base64 defaults if `prosodySSLkey/cert` env vars are missing.
|