# 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` 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 `` 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 `` 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 (``) 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 `` 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.