# HG changeset patch # User tmolitor # Date 1586122748 -7200 # Node ID bf5d91769f9948b8fdeb94d92fc43f4755749c60 # Parent 0957ba6aeb9907cebdea08b6d5bbc53c11d0e03c# Parent 2b10e51d85a6fcd3aa8964057c80a9a92b5303bd Merge commit diff -r 0957ba6aeb99 -r bf5d91769f99 mod_auth_ldap/README.markdown --- a/mod_auth_ldap/README.markdown Sun Apr 05 23:37:17 2020 +0200 +++ b/mod_auth_ldap/README.markdown Sun Apr 05 23:39:08 2020 +0200 @@ -13,7 +13,7 @@ Dependecies =========== -This module depends on [LuaLDAP](http://www.keplerproject.org/lualdap/) +This module depends on [LuaLDAP](https://github.com/lualdap/lualdap) for connecting to an LDAP server. Configuration diff -r 0957ba6aeb99 -r bf5d91769f99 mod_conversejs/README.markdown --- a/mod_conversejs/README.markdown Sun Apr 05 23:37:17 2020 +0200 +++ b/mod_conversejs/README.markdown Sun Apr 05 23:39:08 2020 +0200 @@ -14,6 +14,8 @@ [Converse.js](https://conversejs.org/), configured to work with the VirtualHost that it is loaded onto. +It becomes available on an URL like `https://example.com:5281/conversejs` + Configuration ============= diff -r 0957ba6aeb99 -r bf5d91769f99 mod_http_upload/README.markdown --- a/mod_http_upload/README.markdown Sun Apr 05 23:37:17 2020 +0200 +++ b/mod_http_upload/README.markdown Sun Apr 05 23:39:08 2020 +0200 @@ -24,6 +24,19 @@ It should **not** be added to modules_enabled. +## Discoverability + +Prosody makes subdomains of your VirtualHosts easily discoverable by +clients. To make the component discoverable by other hosts, use +[`disco_items`][doc:modules:mod_disco#configuration]. + +``` {.lua} +VirtualHost "foo.example.org" +disco_items = { + { "upload.example.com" }, +} +``` + Limits ------ @@ -54,7 +67,7 @@ A command exists to invoke expiry: ``` -prosodyctl mod_http_upload expire [optional list of users] +prosodyctl mod_http_upload expire [list of users or hosts] ``` ### User quota diff -r 0957ba6aeb99 -r bf5d91769f99 mod_http_upload/mod_http_upload.lua --- a/mod_http_upload/mod_http_upload.lua Sun Apr 05 23:37:17 2020 +0200 +++ b/mod_http_upload/mod_http_upload.lua Sun Apr 05 23:39:08 2020 +0200 @@ -415,7 +415,7 @@ function module.command(args) datamanager = require "core.storagemanager".olddm; -- luacheck: ignore 421/user - if args[1] == "expire" then + if args[1] == "expire" and args[2] then local split = require "util.jid".prepped_split; for i = 2, #args do local user, host = split(args[i]); @@ -427,6 +427,10 @@ end end end + else + print("prosodyctl mod_http_upload expire [host or user@host]+") + print("\tProcess upload expiry for the given list of hosts and/or users"); + return 1; end end diff -r 0957ba6aeb99 -r bf5d91769f99 mod_http_upload_external/README.markdown --- a/mod_http_upload_external/README.markdown Sun Apr 05 23:37:17 2020 +0200 +++ b/mod_http_upload_external/README.markdown Sun Apr 05 23:39:08 2020 +0200 @@ -26,8 +26,16 @@ Configuration ============= -Add `"http_upload_external"` to modules_enabled in your global section, or under the host(s) you wish -to use it on. +The module can be added as a new Component definition: + +``` {.lua} +Component "upload.example.org" "http_upload_external" +http_upload_external_base_url = "https://your.example.com/upload/service" +http_upload_external_secret = "your shared secret" +``` + +It should **not** be added to modules_enabled. + External URL ------------ diff -r 0957ba6aeb99 -r bf5d91769f99 mod_muc_archive/README.markdown --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_muc_archive/README.markdown Sun Apr 05 23:39:08 2020 +0200 @@ -0,0 +1,43 @@ +--- +labels: +- 'Stage-Beta' +summary: Log MUC messages to disk +... + +# Introduction + +This module logs the conversations of chatrooms running on the server to Prosody's data store. + +This is a fork of [mod_muc_log](https://modules.prosody.im/mod_muc_log.html) which uses the newer storage API. +This allows you to also log messages to a SQL backend. + +## Changes between mod_muc_archive and mod_muc_log: + +- Use newer module storage API so that you can also store in SQL +- Adhere to config option `muc_log_all_rooms` (also used by mod_muc_mam) +- Add affiliation information in the logged stanza +- Remove code that set (and then removed) an "alreadyJoined" dummy element + +NOTE: The changes are unlikely to be entirely backwards compatible because the stanza +being logged is no longer wrapped with ``. + +Details +======= + +mod\_muc\_archive must be loaded individually for the components that need it. + +Assuming you have a MUC component already running on +conference.example.org then you can add muc\_archive to it like so: + + Component "conference.example.org" "muc" + modules_enabled = { + "muc_archive"; + } + + +Compatibility +============= + + ------ ----- + 0.11 Works + ------ ----- diff -r 0957ba6aeb99 -r bf5d91769f99 mod_muc_archive/mod_muc_archive.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_muc_archive/mod_muc_archive.lua Sun Apr 05 23:39:08 2020 +0200 @@ -0,0 +1,191 @@ +-- The MIT License (MIT) +-- +-- Copyright (C) 2009 Thilo Cestonaro +-- Copyright (C) 2009 Waqas Hussain +-- Copyright (C) 2009-2013 Matthew Wild +-- Copyright (C) 2013 Kim Alvefur +-- Copyright (C) 2013 Marco Cirillo +-- Copyright (c) 2020 JC Brand +-- +-- 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. +-- +local hosts = prosody.hosts; +local tostring = tostring; +local st = require "util.stanza"; +local split_jid = require "util.jid".split; +local jid_bare = require "util.jid".bare; +local time_now = os.time; + +local muc_form_config_option = "muc#roomconfig_enablelogging" + +local log_all_rooms = module:get_option_boolean("muc_log_all_rooms", false); +local log_by_default = module:get_option_boolean("muc_log_by_default", false); +local log_presences = module:get_option_boolean("muc_log_presences", true); + +local archive_store = "muc_logging_archive"; +local archive = module:open_store(archive_store, "archive"); + +local xmlns_muc_user = "http://jabber.org/protocol/muc#user"; + +if archive.name == "null" or not archive.find then + if not archive.find then + module:log("error", "Attempt to open archive storage returned a driver without archive API support"); + module:log("error", "mod_%s does not support archiving", + archive._provided_by or archive.name and "storage_"..archive.name.."(?)" or ""); + else + module:log("error", "Attempt to open archive storage returned null driver"); + end + module:log("info", "See https://prosody.im/doc/storage and https://prosody.im/doc/archiving for more information"); + return false; +end + + +-- Module Definitions + +local function get_room_from_jid(jid) + local node, host = split_jid(jid); + local component = hosts[host]; + if component then + local muc = component.modules.muc + if muc and rawget(muc,"rooms") then + -- We're running 0.9.x or 0.10 (old MUC API) + return muc.rooms[jid]; + elseif muc and rawget(muc,"get_room_from_jid") then + -- We're running >0.10 (new MUC API) + return muc.get_room_from_jid(jid); + else + return + end + end +end + +local function logging_enabled(room) + if log_all_rooms then + return true; + end + if room._data.hidden then -- do not log data of private rooms + return false; + end + local enabled = room._data.logging; + if enabled == nil then + return log_by_default; + end + return enabled; +end + + +local function log_if_needed(event) + local stanza = event.stanza; + + if (stanza.name == "presence") or + (stanza.name == "iq") or + (stanza.name == "message" and tostring(stanza.attr.type) == "groupchat") + then + local node, host = split_jid(stanza.attr.to); + if node and host then + local room = get_room_from_jid(node.."@"..host) + if not room then return end + -- Policy check + if not logging_enabled(room) then return end + + local muc_to = nil + local muc_from = nil; + + if stanza.name == "presence" and stanza.attr.type == nil then + muc_from = stanza.attr.to; + elseif stanza.name == "iq" and stanza.attr.type == "set" then + -- kick, to is the room, from is the admin, nick who is kicked is attr of iq->query->item + if stanza.tags[1] and stanza.tags[1].name == "query" then + local tmp = stanza.tags[1]; + if tmp.tags[1] ~= nil and tmp.tags[1].name == "item" and tmp.tags[1].attr.nick then + tmp = tmp.tags[1]; + for jid, nick in pairs(room._jid_nick) do + if nick == stanza.attr.to .. "/" .. tmp.attr.nick then + muc_to = nick; + break; + end + end + end + end + else + for jid, nick in pairs(room._jid_nick) do + if jid == stanza.attr.from then + muc_from = nick; + break; + end + end + end + + if (muc_from or muc_to) then + local stored_stanza = st.clone(stanza); + stored_stanza.attr.from = muc_from; + stored_stanza.attr.to = muc_to; + + if stanza.name == "message" then + local actor = jid_bare(room._occupants[muc_from].jid); + local affiliation = room:get_affiliation(actor) or "none"; + local role = room:get_role(actor) or room:get_default_role(affiliation); + stored_stanza:add_direct_child(st.stanza("x", { xmlns = xmlns_muc_user }) + :tag("item", { affiliation = affiliation; role = role; jid = actor })); + end + + local with = stanza.name + if stanza.attr.type then + with = with .. "<" .. stanza.attr.type + end + archive:append(node, nil, stored_stanza, time_now(), with); + end + end + end +end + +if not log_all_rooms then + module:hook("muc-config-form", function(event) + local room, form = event.room, event.form; + table.insert(form, + { + name = muc_form_config_option, + type = "boolean", + label = "Enable Logging?", + value = logging_enabled(room), + } + ); + end); + + module:hook("muc-config-submitted", function(event) + local room, fields, changed = event.room, event.fields, event.changed; + local new = fields[muc_form_config_option]; + if new ~= room._data.logging then + room._data.logging = new; + if type(changed) == "table" then + changed[muc_form_config_option] = true; + else + event.changed = true; + end + end + end); +end + +module:hook("message/bare", log_if_needed, 1); + +if log_presences then + module:hook("iq/bare", log_if_needed, 1); + module:hook("presence/full", log_if_needed, 1); +end + +module:log("debug", "module mod_muc_archive loaded!"); diff -r 0957ba6aeb99 -r bf5d91769f99 mod_muc_limits/mod_muc_limits.lua --- a/mod_muc_limits/mod_muc_limits.lua Sun Apr 05 23:37:17 2020 +0200 +++ b/mod_muc_limits/mod_muc_limits.lua Sun Apr 05 23:39:08 2020 +0200 @@ -13,6 +13,7 @@ local burst = math.max(module:get_option_number("muc_burst_factor", 6), 1); local max_nick_length = module:get_option_number("muc_max_nick_length", 23); -- Default chosen through scientific methods +local join_only = module:get_option_boolean("muc_limit_joins_only", false); local dropped_count = 0; local dropped_jids; @@ -74,17 +75,21 @@ if rooms then function module.unload() - for room_jid, room in pairs(rooms) do + for room_jid, room in pairs(rooms) do --luacheck: ignore 213/room_jid room.throttle = nil; end end - module:hook("message/bare", handle_stanza, 501); - module:hook("message/full", handle_stanza, 501); - module:hook("presence/bare", handle_stanza, 501); module:hook("presence/full", handle_stanza, 501); + if not join_only then + module:hook("message/bare", handle_stanza, 501); + module:hook("message/full", handle_stanza, 501); + module:hook("presence/bare", handle_stanza, 501); + end else module:hook("muc-occupant-pre-join", handle_stanza); - module:hook("muc-occupant-pre-change", handle_stanza); - module:hook("muc-occupant-groupchat", handle_stanza); + if not join_only then + module:hook("muc-occupant-pre-change", handle_stanza); + module:hook("muc-occupant-groupchat", handle_stanza); + end end diff -r 0957ba6aeb99 -r bf5d91769f99 mod_rest/README.markdown --- a/mod_rest/README.markdown Sun Apr 05 23:37:17 2020 +0200 +++ b/mod_rest/README.markdown Sun Apr 05 23:39:08 2020 +0200 @@ -375,6 +375,11 @@ items list query. The response contain an array of items like `{"jid":"xmpp.address.here","name":"Description of item"}`. +`extensions` +: Map of extended feature discovery (see [XEP-0128]) data with + `FORM_DATA` fields as the keys pointing at maps with the rest of the + data. + #### Ad-Hoc Commands Used to execute arbitrary commands on supporting entities. diff -r 0957ba6aeb99 -r bf5d91769f99 mod_rest/jsonmap.lib.lua --- a/mod_rest/jsonmap.lib.lua Sun Apr 05 23:37:17 2020 +0200 +++ b/mod_rest/jsonmap.lib.lua Sun Apr 05 23:39:08 2020 +0200 @@ -75,7 +75,7 @@ disco = { type = "func", xmlns = "http://jabber.org/protocol/disco#info", tagname = "query", st2json = function (s) --> array of features - local identities, features = array(), array(); + local identities, features, extensions = array(), array(), {}; for tag in s:childtags() do if tag.name == "identity" and tag.attr.category and tag.attr.type then identities:push({ category = tag.attr.category, type = tag.attr.type, name = tag.attr.name }); @@ -83,7 +83,16 @@ features:push(tag.attr.var); end end - return { node = s.attr.node, identities = identities, features = features, }; + for form in s:childtags("x", "jabber:x:data") do + local jform = field_mappings.formdata.st2json(form); + local form_type = jform["FORM_TYPE"]; + if jform then + jform["FORM_TYPE"] = nil; + extensions[form_type] = jform; + end + end + if next(extensions) == nil then extensions = nil; end + return { node = s.attr.node, identities = identities, features = features, extensions = extensions }; end; json2st = function (s) if type(s) == "table" and s ~= json.null then @@ -98,6 +107,12 @@ disco:tag("feature", { var = feature }):up(); end end + if s.extensions then + for form_type, extension in pairs(s.extensions) do + extension["FORM_TYPE"] = form_type; + disco:add_child(field_mappings.formdata.json2st(extension)); + end + end return disco; else return st.stanza("query", { xmlns = "http://jabber.org/protocol/disco#info", }); @@ -324,10 +339,28 @@ -- Simpler mapping of dataform from JSON map formdata = { type = "func", xmlns = "jabber:x:data", tagname = "", - st2json = function () - -- Tricky to do in a generic way without each form layout - -- In the future, some well-known layouts might be understood - return nil, "not-implemented"; + st2json = function (s) + local r = {}; + for field in s:childtags("field") do + if field.attr.var then + local values = array(); + for value in field:childtags("value") do + values:push(value:get_text()); + end + if field.attr.type == "list-single" or field.attr.type == "list-multi" then + r[field.attr.var] = values; + elseif field.attr.type == "text-multi" then + r[field.attr.var] = values:concat("\n"); + elseif field.attr.type == "boolean" then + r[field.attr.var] = values[1] == "1" or values[1] == "true"; + elseif field.attr.type then + r[field.attr.var] = values[1] or json.null; + else -- type is optional, no way to know if multiple or single value is expected + r[field.attr.var] = values; + end + end + end + return r; end, json2st = function (s, t) local form = st.stanza("x", { xmlns = "jabber:x:data", type = t }); diff -r 0957ba6aeb99 -r bf5d91769f99 mod_smacks/mod_smacks.lua --- a/mod_smacks/mod_smacks.lua Sun Apr 05 23:37:17 2020 +0200 +++ b/mod_smacks/mod_smacks.lua Sun Apr 05 23:39:08 2020 +0200 @@ -17,7 +17,7 @@ local uuid_generate = require "util.uuid".generate; local jid = require "util.jid"; -local t_insert, t_remove = table.insert, table.remove; +local t_remove = table.remove; local math_min = math.min; local math_max = math.max; local os_time = os.time; @@ -200,8 +200,15 @@ end local function outgoing_stanza_filter(stanza, session) - local is_stanza = stanza.attr and not stanza.attr.xmlns and not stanza.name:find":"; - if is_stanza and not stanza._cached then -- Stanza in default stream namespace + -- 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;