Mercurial > prosody-modules
changeset 1994:f263fcf1b0ed
Merge with Goffi
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Mon, 21 Dec 2015 12:24:21 +0100 |
parents | 6d7699eda594 (diff) 66aaf7c3cb29 (current diff) |
children | 0e008f36a91c |
files | |
diffstat | 26 files changed, 433 insertions(+), 192 deletions(-) [+] |
line wrap: on
line diff
--- a/README Mon Dec 21 11:31:10 2015 +0100 +++ b/README Mon Dec 21 12:24:21 2015 +0100 @@ -34,5 +34,5 @@ prosody-modules for 0.8 are now maintained separately at <http://0-8.prosody-modules.googlecode.com/hg/>. -[Prosody]: http://prosody.im/ -[mailing list]: http://prosody.im/discuss +[Prosody]: https://prosody.im/ +[mailing list]: https://prosody.im/discuss
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_alias/README.markdown Mon Dec 21 12:24:21 2015 +0100 @@ -0,0 +1,57 @@ +--- +summary: Point alias accounts or domains to correct XMPP user +... + +Introduction +============ + +This module allows you to set up aliases that alert people who try to +contact them or add them to their roster what your actual JID is. This +is useful for changing JIDs, or just in the case where you own both +example.com and example.net, and want people who contact you@example.com +to be alerted to contact you at you@example.net instead. + +This type of aliasing is well supported in the email world, but very hard +to handle with XMPP, this module sidesteps all the hard problems by just +sending the user a helpful message, requiring humans to decide what they +actually want to do. + +This doesn't require any special support on other clients or servers, +just the ability to recieve messages. + +Configuration +============= + +Add the module to the `modules_enabled` list. + + modules_enabled = { + ... + "alias"; + } + +Then set up your list of aliases, aliases can be full or bare JIDs, +or hosts: + + aliases = { + ["old@example.net"] = "new@example.net"; + ["you@example.com"] = "you@example.net"; + ["conference.example.com"] = "conference.example.net"; + } + +You can also set up a custom response, by default it is: + + alias_response = "User $alias can be contacted at $target"; + +A script named mod_alias_postfixadmin.sh is included in this directory to +generate the aliases array directly from a postfixadmin MySQL database. +Instructions for use are included in the script. + +Compatibility +============= + + ------- -------------- + trunk Works + 0.10 Works + 0.9 Unknown + 0.8 Unknown + ------- --------------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_alias/mod_alias.lua Mon Dec 21 12:24:21 2015 +0100 @@ -0,0 +1,43 @@ +-- Copyright (C) 2015 Travis Burtrum +-- This file is MIT/X11 licensed. + +-- set like so in prosody config, works on full or bare jids, or hosts: +--aliases = { +-- ["old@example.net"] = "new@example.net"; +-- ["you@example.com"] = "you@example.net"; +-- ["conference.example.com"] = "conference.example.net"; +--} + +local aliases = module:get_option("aliases", {}); +local alias_response = module:get_option("alias_response", "User $alias can be contacted at $target"); + +local st = require "util.stanza"; + +function handle_alias(event) + + if event.stanza.attr.type ~= "error" then + local alias = event.stanza.attr.to; + local target = aliases[alias]; + if target then + local replacements = { + alias = alias, + target = target + }; + local error_message = alias_response:gsub("%$([%w_]+)", function (v) + return replacements[v] or nil; + end); + local message = st.message{ type = "chat", from = alias, to = event.stanza.attr.from }:tag("body"):text(error_message); + module:send(message); + return event.origin.send(st.error_reply(event.stanza, "cancel", "gone", error_message)); + end + end + +end + +module:hook("message/bare", handle_alias, 300); +module:hook("message/full", handle_alias, 300); +module:hook("message/host", handle_alias, 300); + +module:hook("presence/bare", handle_alias, 300); +module:hook("presence/full", handle_alias, 300); +module:hook("presence/host", handle_alias, 300);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_alias/mod_alias_postfixadmin.sh Mon Dec 21 12:24:21 2015 +0100 @@ -0,0 +1,20 @@ +#!/bin/sh +# Copyright (C) 2015 Travis Burtrum +# This file is MIT/X11 licensed. + +# run like ./mod_alias_postfixadmin.sh "mysql -N -upostfixadmin -ppostfixadmin postfixadmin" > /etc/prosody/aliases.cfg.lua +# then put: +# Include "aliases.cfg.lua" +# in prosody.cfg.lua + +mysql="$1" + +echo "-- alias plugin, generated by mod_alias_postfixadmin.sh" +echo "aliases = {" + +echo "SELECT concat('["'"'"', address, '"'"'"] = "'"'"', goto, '"'"'";') FROM alias WHERE address != goto; +SELECT concat('["'"'"', address, '"'"'"] = "'"'"', goto, '"'"'";') FROM ( + select replace(address, concat('@', target_domain), concat('@', alias_domain)) as address, goto FROM alias JOIN alias_domain ON alias_domain.target_domain = SUBSTRING(alias.address, locate('@',alias.address) + 1, length(alias.address)) +) a WHERE a.address != a.goto;" | $mysql | sort | uniq + +echo "}"
--- a/mod_auth_ldap/README.markdown Mon Dec 21 11:31:10 2015 +0100 +++ b/mod_auth_ldap/README.markdown Mon Dec 21 12:24:21 2015 +0100 @@ -37,7 +37,7 @@ ldap\_rootdn The distinguished name to auth against `"" (anonymous)` ldap\_password Password for rootdn `""` ldap\_filter Search filter, with `$user` and `$host` substituded for user- and hostname `"(uid=$user)"` - ldap\_scope Search scope. other values: "base" and "subtree" `"onelevel"` + ldap\_scope Search scope. other values: "base" and "onelevel" `"subtree"` ldap\_tls Enable TLS (StartTLS) to connect to LDAP (can be true or false). The non-standard 'LDAPS' protocol is not supported. `false` ldap\_mode How passwords are validated. `"bind"`
--- a/mod_auth_ldap/mod_auth_ldap.lua Mon Dec 21 11:31:10 2015 +0100 +++ b/mod_auth_ldap/mod_auth_ldap.lua Mon Dec 21 12:24:21 2015 +0100 @@ -9,7 +9,7 @@ local ldap_rootdn = module:get_option_string("ldap_rootdn", ""); local ldap_password = module:get_option_string("ldap_password", ""); local ldap_tls = module:get_option_boolean("ldap_tls"); -local ldap_scope = module:get_option_string("ldap_scope", "onelevel"); +local ldap_scope = module:get_option_string("ldap_scope", "subtree"); local ldap_filter = module:get_option_string("ldap_filter", "(uid=$user)"):gsub("%%s", "$user", 1); local ldap_base = assert(module:get_option_string("ldap_base"), "ldap_base is a required option for ldap"); local ldap_mode = module:get_option_string("ldap_mode", "bind");
--- a/mod_http_muc_log/README.markdown Mon Dec 21 11:31:10 2015 +0100 +++ b/mod_http_muc_log/README.markdown Mon Dec 21 12:24:21 2015 +0100 @@ -8,41 +8,40 @@ ============ This module provides a built-in web interface to view chatroom logs -stored by [mod\_mam\_muc](mod_mam_muc.html). +stored by [mod\_mam\_muc]. Installation ============ -Just copy the folder muc\_log\_http as it is, into the modules folder of -your Prosody installation. +Same as any other module, be sure to include the HTML template +`http_muc_log.html` alongside `mod_http_muc_log.lua`. -Configuration Details -===================== +Configuration +============= -You need to add muc\_log\_http to your global modules\_enabled, and the -configuration options similarly must be put into your global -(server-wide) options section: +For example: - Component "conference.example.com" "muc" - modules_enabled = { - ..... - "mam_muc"; - "http_muc_log"; - ..... - } - storage = { - muc_log = "sql2"; -- for example - } +``` lua +Component "conference.example.com" "muc" +modules_enabled = { + "mam_muc"; + "http_muc_log"; +} +storage = { + muc_log = "sql"; -- for example +} +``` The web interface would then be reachable at the address: http://conference.example.com:5280/muc_log/ -See [the page about Prosodys HTTP server](http://prosody.im/doc/http) -for info about the address. +See [the page about Prosodys HTTP server][doc:http] for info about the +address. Compatibility ============= Requires Prosody 0.10 or above and a storage backend with support for -stanza archives. +stanza archives. See [mod\_storage\_muc\_log] for using legacy data from +[mod\_muc\_log].
--- a/mod_http_upload/README.markdown Mon Dec 21 11:31:10 2015 +0100 +++ b/mod_http_upload/README.markdown Mon Dec 21 12:24:21 2015 +0100 @@ -58,6 +58,16 @@ http_upload_file_size_limit = 10 * 1024 * 1024 -- this is 10MB in bytes ``` +Path +---- + +By default, uploaded files are put in a sub-directory of the default +Prosody storage path (usually `/var/lib/prosody`). This can be changed: + +``` {.lua} +http_upload_path = "/path/to/uploded/files" +``` + Compatibility =============
--- a/mod_http_upload/mod_http_upload.lua Mon Dec 21 11:31:10 2015 +0100 +++ b/mod_http_upload/mod_http_upload.lua Mon Dec 21 12:24:21 2015 +0100 @@ -11,6 +11,7 @@ local st = require"util.stanza"; local lfs = require"lfs"; local uuid = require"util.uuid".generate; +local urlencode = require"util.http".urlencode; local t_concat = table.concat; local t_insert = table.insert; local s_upper = string.upper; @@ -34,7 +35,7 @@ -- state local pending_slots = module:shared("upload_slots"); -local storage_path = join_path(prosody.paths.data, module.name); +local storage_path = module:get_option_string(module.name .. "_path", join_path(prosody.paths.data, module.name)); lfs.mkdir(storage_path); -- hooks @@ -66,7 +67,7 @@ reply:tag("slot", { xmlns = xmlns_http_upload }); local random = uuid(); pending_slots[random.."/"..filename] = origin.full_jid; - local url = module:http_url() .. "/" .. random .. "/" .. filename; + local url = module:http_url() .. "/" .. random .. "/" .. urlencode(filename); reply:tag("get"):text(url):up(); reply:tag("put"):text(url):up(); origin.send(reply);
--- a/mod_mam/README.markdown Mon Dec 21 11:31:10 2015 +0100 +++ b/mod_mam/README.markdown Mon Dec 21 12:24:21 2015 +0100 @@ -7,8 +7,7 @@ Introduction ============ -Implementation of [XEP-0313: Message Archive -Management](http://xmpp.org/extensions/xep-0313.html). +Implementation of [XEP-0313: Message Archive Management]. Details ======= @@ -37,15 +36,14 @@ Storage backend --------------- -mod\_mam uses the store "archive2"[^1]. See [Prosodys data storage -documentation](https://prosody.im/doc/storage) for information on how to -configure storage. +mod\_mam uses the store "archive2"[\^1]. See [Prosodys data storage +documentation][doc:storage] for information on how to configure storage. -For example, to use mod\_storage\_sql2[^2]: +For example, to use mod\_storage\_sql: ``` {.lua} storage = { - archive2 = "sql2"; + archive2 = "sql"; } ``` @@ -85,14 +83,12 @@ ------- --------------- trunk Works - 0.10 Works [^3] + 0.10 Works [^2] 0.9 Unsupported 0.8 Does not work ------- --------------- [^1]: Might be changed to "mam" at some point -[^2]: mod\_storage\_sql2 will replace mod\_storage\_sql at some point - -[^3]: requires a storage driver with archive support, eg - mod\_storage\_sql2 in 0.10 +[^2]: requires a storage driver with archive support, eg + mod\_storage\_sql in 0.10
--- a/mod_mam/mod_mam.lua Mon Dec 21 11:31:10 2015 +0100 +++ b/mod_mam/mod_mam.lua Mon Dec 21 12:24:21 2015 +0100 @@ -224,7 +224,7 @@ -- Stanza without 'to' are treated as if it was to their own bare jid -- We store chat messages or normal messages that have a body - if not(orig_type == "chat" or orig_type == "normal" and stanza:get_child("body") ) then + if not(orig_type == "chat" or (orig_type == "normal" and stanza:get_child("body")) ) then module:log("debug", "Not archiving stanza: %s (type)", stanza:top_tag()); return; end
--- a/mod_mam_muc/README.markdown Mon Dec 21 11:31:10 2015 +0100 +++ b/mod_mam_muc/README.markdown Mon Dec 21 12:24:21 2015 +0100 @@ -9,9 +9,8 @@ This module logs the conversation of chatrooms running on the server to Prosody's archive storage. To access them you will need a client with -support for [XEP-0313: Message Archive -Management](http://xmpp.org/extensions/xep-0313.html) or a module such -as [mod\_http\_muc\_log](mod_http_muc_log.html). +support for [XEP-0313: Message Archive Management] or a module such +as [mod\_http\_muc\_log]. Usage ===== @@ -25,16 +24,18 @@ modules_enabled = { "mam_muc", } +``` + +And configure it to use an archive-capable storage module: + +``` {.lua} storage = { - -- This makes mod_mam_muc use the sql2 storage backend (others will use internal) - -- which at the time of this writing is the only one supporting stanza archives - muc_log = "sql2"; + muc_log = "sql"; -- Requires 0.10 or later } ``` -See [Prosodys data storage -documentation](https://prosody.im/doc/storage) for more info on how to -configure storage for different plugins. +See [Prosodys data storage documentation][doc:storage] for more info on +how to configure storage for different plugins. Configuration ============= @@ -57,9 +58,13 @@ Compatibility ============= - ------- --------------- - trunk Works - 0.10 Works + ------- ----------------- + trunk Works best + 0.10 Works partially 0.9 Does not work 0.8 Does not work - ------- --------------- + ------- ----------------- + +Prosody trunk (after April 2014) has a major rewrite of the MUC module, +allowing easier integration. Without this (0.10), some features do not +work, such as correct advertising and join/part logging.
--- a/mod_mam_muc/mod_mam_muc.lua Mon Dec 21 11:31:10 2015 +0100 +++ b/mod_mam_muc/mod_mam_muc.lua Mon Dec 21 12:24:21 2015 +0100 @@ -3,6 +3,11 @@ -- -- This file is MIT/X11 licensed. +if module:get_host_type() ~= "component" then + module:log("error", "mod_%s should be loaded only on a MUC component, not normal hosts", module.name); + return; +end + local xmlns_mam = "urn:xmpp:mam:0"; local xmlns_delay = "urn:xmpp:delay"; local xmlns_forward = "urn:xmpp:forward:0";
--- a/mod_migrate/README.markdown Mon Dec 21 11:31:10 2015 +0100 +++ b/mod_migrate/README.markdown Mon Dec 21 12:24:21 2015 +0100 @@ -37,7 +37,7 @@ ======== for store in accounts roster private blocklist vcard archive2-archive; do - prosodyctl migrate example.com $store sql2 + prosodyctl migrate example.com $store sql done Compatibility
--- a/mod_migrate/mod_migrate.lua Mon Dec 21 11:31:10 2015 +0100 +++ b/mod_migrate/mod_migrate.lua Mon Dec 21 12:24:21 2015 +0100 @@ -5,55 +5,57 @@ local mm = require"core.modulemanager"; function module.command(arg) - local host, source_store, migrate_to, user = unpack(arg); + local host, source_stores, migrate_to, user = unpack(arg); if not migrate_to then return print("Usage: prosodyctl mod_migrate example.com <source-store>[-<store-type>] <target-driver> [users]*"); end sm.initialize_host(host); um.initialize_host(host); local module = module:context(host); - local store_type = source_store:match("%-(%a+)$"); - if store_type then - source_store = source_store:sub(1, -2-#store_type); - end - local storage = module:open_store(source_store, store_type); - local target = assert(sm.load_driver(host, migrate_to)); - target = assert(target:open(source_store, store_type)); + for source_store in source_stores:gmatch("[^,]+") do + local store_type = source_store:match("%-(%a+)$"); + if store_type then + source_store = source_store:sub(1, -2-#store_type); + end + local storage = module:open_store(source_store, store_type); + local target = assert(sm.load_driver(host, migrate_to)); + target = assert(target:open(source_store, store_type)); - local function migrate_user(username) - module:log("info", "Migrating data for %s", username); - local data, err = storage:get(username); - assert(data or err==nil, err); - assert(target:set(username, data)); - end + local function migrate_user(username) + module:log("info", "Migrating data for %s", username); + local data, err = storage:get(username); + assert(data or err==nil, err); + assert(target:set(username, data)); + end - if store_type == "archive" then - function migrate_user(username) - module:log("info", "Migrating archive items for %s", username); - local count, errs = 0, 0; - for id, item, when, with in storage:find(username) do - local ok, err = target:append(username, id, item, when, with); - if ok then - count = count + 1; - else - module:log("warn", "Error: %s", err); - errs = errs + 1; + if store_type == "archive" then + function migrate_user(username) + module:log("info", "Migrating archive items for %s", username); + local count, errs = 0, 0; + for id, item, when, with in storage:find(username) do + local ok, err = target:append(username, id, item, when, with); + if ok then + count = count + 1; + else + module:log("warn", "Error: %s", err); + errs = errs + 1; + end + if ( count + errs ) % 100 == 0 then + module:log("info", "%d items migrated, %d errors", count, errs); + end end - if ( count + errs ) % 100 == 0 then - module:log("info", "%d items migrated, %d errors", count, errs); - end + module:log("info", "%d items migrated, %d errors", count, errs); end - module:log("info", "%d items migrated, %d errors", count, errs); end - end - if arg[4] then - for i = 4, #arg do - migrate_user(arg[i]); - end - else - for user in um.users(host) do - migrate_user(user); + if arg[4] then + for i = 4, #arg do + migrate_user(arg[i]); + end + else + for user in um.users(host) do + migrate_user(user); + end end end end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_muc_access_control/mod_muc_access_control.lua Mon Dec 21 12:24:21 2015 +0100 @@ -0,0 +1,57 @@ +local st = require "util.stanza"; +local jid = require "util.jid"; +local nodeprep = require "util.encodings".stringprep.nodeprep; + +local unprepped_access_lists = module:get_option("muc_access_lists", {}); +local access_lists = {}; + +-- Make sure all input is prepped +for unprepped_room_name, unprepped_list in pairs(unprepped_access_lists) do + local prepped_room_name = nodeprep(unprepped_room_name); + if not prepped_room_name then + module:log("error", "Invalid room name: %s", unprepped_room_name); + else + local prepped_list = {}; + for _, unprepped_jid in ipairs(unprepped_list) do + local prepped_jid = jid.prep(jid); + if not prepped_jid then + module:log("error", "Invalid JID: %s", unprepped_jid); + else + table.insert(prepped_list, jid.pep(jid)); + end + end + end +end + +local function is_restricted(room, who) + local allowed = access_lists[room]; + + if allowed == nil or allowed[who] or allowed[select(2, jid.split(who))] then + return nil; + end + + return "forbidden"; +end + +module:hook("presence/full", function(event) + local stanza = event.stanza; + + if stanza.name == "presence" and stanza.attr.type == "unavailable" then -- Leaving events get discarded + return; + end + + -- Get the room + local room = jid.split(stanza.attr.to); + if not room then return; end + + -- Get who has tried to join it + local who = jid.bare(stanza.attr.from) + + -- Checking whether room is restricted + local check_restricted = is_restricted(room, who) + if check_restricted ~= nil then + event.allowed = false; + event.stanza.attr.type = 'error'; + return event.origin.send(st.error_reply(event.stanza, "cancel", "forbidden", "You're not allowed to enter this room: " .. check_restricted)); + end +end, 10);
--- a/mod_muc_log_http/muc_log_http/themes/prosody/day_body.html Mon Dec 21 11:31:10 2015 +0100 +++ b/mod_muc_log_http/muc_log_http/themes/prosody/day_body.html Mon Dec 21 12:24:21 2015 +0100 @@ -1,7 +1,7 @@ <div id="title"> <div id="date">###DATE###</div> <div id="roomjid">###JID###</div> - <div id="links">(join via <a class="component" href="xmpp:###JID###?join">client</a> / <a class="component" href="http://speeqe.com/room/###JID###/">speeqe</a>)</div> + <div id="links">(join via <a class="component" href="xmpp:###JID###?join">client</a>)</div> </div> <div id="calendar"> <div id="navigation">
--- a/mod_muc_log_http/muc_log_http/themes/prosody/days_body.html Mon Dec 21 11:31:10 2015 +0100 +++ b/mod_muc_log_http/muc_log_http/themes/prosody/days_body.html Mon Dec 21 12:24:21 2015 +0100 @@ -1,7 +1,7 @@ <div id="title"> <div id="date">###SINCE### - ###TO###</div> <div id="roomjid">###JID###</div> - <div id="links">(join via <a class="component" href="xmpp:###JID###?join">client</a> / <a class="component" href="http://speeqe.com/room/###JID###/">speeqe</a>)</div> + <div id="links">(join via <a class="component" href="xmpp:###JID###?join">client</a>)</div> </div> <div id="calendar"> <div id="navigation">
--- a/mod_presence_cache/README.markdown Mon Dec 21 11:31:10 2015 +0100 +++ b/mod_presence_cache/README.markdown Mon Dec 21 12:24:21 2015 +0100 @@ -14,7 +14,8 @@ replies to presence probes. Also see [mod\_throttle\_presence]. By default, only binary (online or offline) state is stored. It can -optionally store the full presence but this requires much more memory. +optionally store the full presence but this requires much more memory +and bandwidth. Configuration ============= @@ -29,10 +30,13 @@ Advanced configuration ====================== -To enable full stanza caching: +To enable *experimental* full stanza caching: presence_cache_full = false +This will usually double the presence data sent to clients, pending +support for deduplication. + TODO ====
--- a/mod_pubsub_hub/README.markdown Mon Dec 21 11:31:10 2015 +0100 +++ b/mod_pubsub_hub/README.markdown Mon Dec 21 12:24:21 2015 +0100 @@ -1,18 +1,19 @@ --- -summary: PubSubHubbub service +summary: PubSubHubbub hub ... Introduction ------------- +============ This module implements a -[PubSubHubbub](http://pubsubhubbub.googlecode.com/svn/trunk/pubsubhubbub-core-0.3.html)(PuSH) -hub, allowing PuSH clients to subscribe to local XMPP +[PubSubHubbub](http://pubsubhubbub.googlecode.com/svn/trunk/pubsubhubbub-core-0.3.html) +(PuSH) hub, allowing PuSH clients to subscribe to local XMPP [Publish-Subscribe](http://xmpp.org/extensions/xep-0060.html) nodes -stored by [mod\_pubsub](http://prosody.im/doc/modules/mod_pubsub). +stored by [mod\_pubsub](http://prosody.im/doc/modules/mod_pubsub) and +receive real time updates to feeds. Configuration -------------- +============= Component "pubsub.example.com" "pubsub" @@ -20,13 +21,16 @@ "pubsub_hub"; } -The hub is then available on {http://pubsub.example.com:5280/hub}. +The hub is then available on `http://pubsub.example.com:5280/hub`. Compatibility -------------- +============= ------- -------------- trunk Works + 0.10 Should work 0.9 Works 0.8 Doesn't work ------- -------------- + +
--- a/mod_s2s_auth_dane/README.markdown Mon Dec 21 11:31:10 2015 +0100 +++ b/mod_s2s_auth_dane/README.markdown Mon Dec 21 12:24:21 2015 +0100 @@ -26,8 +26,7 @@ Configuration ============= -After [installing the -module](https://prosody.im/doc/installing_modules), just add it to +After [installing the module][doc:installing\_modules], just add it to `modules_enabled`; modules_enabled = { @@ -78,8 +77,7 @@ Further reading =============== -- [DANE TLSA implementation and operational - guidance](http://tools.ietf.org/html/draft-ietf-dane-ops) +- [DANE Operational Guidance][rfc7671] Compatibility =============
--- a/mod_s2s_auth_dane/mod_s2s_auth_dane.lua Mon Dec 21 11:31:10 2015 +0100 +++ b/mod_s2s_auth_dane/mod_s2s_auth_dane.lua Mon Dec 21 12:24:21 2015 +0100 @@ -67,11 +67,12 @@ -- Takes a s2sin/out and a callback local function dane_lookup(host_session, cb) cb = cb or noop; + local log = host_session.log or module._log; if host_session.dane ~= nil then return end -- Has already done a lookup if host_session.direction == "incoming" then if not host_session.from_host then - module:log("debug", "Session doesn't have a 'from' host set"); + log("debug", "Session doesn't have a 'from' host set"); return; end -- We don't know what hostname or port to use for Incoming connections @@ -80,19 +81,24 @@ -- and incoming connections, so this should work well local name = host_session.from_host and idna_to_ascii(host_session.from_host); if not name then - module:log("warn", "Could not convert '%s' to ASCII for DNS lookup", tostring(host_session.from_host)); + log("warn", "Could not convert '%s' to ASCII for DNS lookup", tostring(host_session.from_host)); return; end + log("debug", "Querying SRV records from _xmpp-server._tcp.%s.", name); host_session.dane = dns_lookup(function (answer, err) host_session.dane = false; -- Mark that we already did the lookup if not answer then - module:log("debug", "Resolver error: %s", tostring(err)); + log("debug", "Resolver error: %s", tostring(err)); return cb(host_session); end - if not answer.secure then - module:log("debug", "Results are not secure"); + if answer.bogus then + log("warn", "Results are bogus!"); + -- Bad sign, probably not a good idea to do any fallback here + host_session.dane = answer; + elseif not answer.secure then + log("debug", "Results are not secure"); return cb(host_session); end @@ -111,7 +117,9 @@ local dane; for _, record in ipairs(answer) do t_insert(srv_hosts, record.srv); + log("debug", "Querying TLSA record for %s:%d", record.srv.target, record.srv.port); dns_lookup(function(dane_answer) + log("debug", "Got answer for %s:%d", record.srv.target, record.srv.port); n = n - 1; -- There are three kinds of answers -- Insecure, Secure and Bogus @@ -129,10 +137,13 @@ if (dane_answer.bogus or dane_answer.secure) and not dane then -- The first answer we care about -- For services with only one SRV record, this will be the only one + log("debug", "First secure (or bogus) TLSA") dane = dane_answer; elseif dane_answer.bogus then + log("debug", "Got additional bogus TLSA") dane.bogus = dane_answer.bogus; elseif dane_answer.secure then + log("debug", "Got additional secure TLSA") for _, dane_record in ipairs(dane_answer) do t_insert(dane, dane_record); end @@ -143,7 +154,7 @@ if #dane > 0 and dane.bogus then -- Got at least one non-bogus reply, -- This should trigger a failure if one of them did not match - host_session.log("warn", "Ignoring bogus replies"); + log("warn", "Ignoring bogus replies"); dane.bogus = nil; end if #dane == 0 and dane.bogus == nil then @@ -166,6 +177,7 @@ end -- Do TLSA lookup for currently selected SRV record local srv_choice = srv_hosts[host_session.srv_choice or 0] or { target = idna_to_ascii(host_session.to_host), port = 5269 }; + log("debug", "Querying TLSA record for %s:%d", srv_choice.target, srv_choice.port); host_session.dane = dns_lookup(function(answer) if answer and ((answer.secure and #answer > 0) or answer.bogus) then srv_choice.dane = answer; @@ -221,13 +233,12 @@ return false; end -- Cleanup - session.dane = nil; session.srv_hosts = nil; end); end -- Compare one TLSA record against a certificate -local function one_dane_check(tlsa, cert) +local function one_dane_check(tlsa, cert, log) local select, match, certdata = tlsa.select, tlsa.match; if select == 0 then @@ -235,7 +246,7 @@ elseif select == 1 and cert.pubkey then certdata = pem2der(cert:pubkey()); else - module:log("warn", "DANE selector %s is unsupported", tlsa:getSelector() or select); + log("warn", "DANE selector %s is unsupported", tlsa:getSelector() or select); return; end @@ -244,12 +255,12 @@ elseif match == 2 then certdata = hashes.sha512(certdata); elseif match ~= 0 then - module:log("warn", "DANE match rule %s is unsupported", tlsa:getMatchType() or match); + log("warn", "DANE match rule %s is unsupported", tlsa:getMatchType() or match); return; end if #certdata ~= #tlsa.data then - module:log("warn", "Length mismatch: Cert: %d, TLSA: %d", #certdata, #tlsa.data); + log("warn", "Length mismatch: Cert: %d, TLSA: %d", #certdata, #tlsa.data); end return certdata == tlsa.data; end @@ -263,14 +274,14 @@ local match_found, supported_found; for i = 1, #dane do local tlsa = dane[i].tlsa; - module:log("debug", "TLSA #%d: %s", i, tostring(tlsa)) + log("debug", "TLSA #%d: %s", i, tostring(tlsa)) local use = tlsa.use; if enabled_uses:contains(use) then -- DANE-EE or PKIX-EE if use == 3 or use == 1 then -- Should we check if the cert subject matches? - local is_match = one_dane_check(tlsa, cert); + local is_match = one_dane_check(tlsa, cert, log); if is_match ~= nil then supported_found = true; end @@ -286,6 +297,7 @@ session.cert_chain_status = "valid"; end match_found = true; + dane.matching = tlsa; break; end -- DANE-TA or PKIX-CA @@ -294,7 +306,7 @@ local chain = session.conn:socket():getpeerchain(); for c = 1, #chain do local cacert = chain[c]; - local is_match = one_dane_check(tlsa, cacert); + local is_match = one_dane_check(tlsa, cacert, log); if is_match ~= nil then supported_found = true; end @@ -315,6 +327,7 @@ end end match_found = true; + dane.matching = tlsa; break; end end @@ -350,3 +363,32 @@ end end); +-- Telnet command +if module:get_option_set("modules_enabled", {}):contains("admin_telnet") then + module:depends("admin_telnet"); -- Make sure the env is there + local def_env = module:shared("admin_telnet/env"); + + local sessions = module:shared("s2s/sessions"); + + local function annotate(session, line) + line = line or {}; + table.insert(line, "--"); + if session.dane == nil then + table.insert(line, "No DANE attempted, probably insecure SRV response"); + elseif session.dane == false then + table.insert(line, "DANE failed or response was insecure"); + elseif type(session.dane) ~= "table" then + table.insert(line, "Waiting for DANE records..."); + elseif session.dane.matching then + table.insert(line, "Matching DANE record:\n| " .. tostring(session.dane.matching)); + else + table.insert(line, "DANE records:\n| " .. tostring(session.dane)); + end + return table.concat(line, " "); + end + + function def_env.s2s:show_dane(...) + return self:show(..., annotate); + end +end +
--- a/mod_storage_gdbm/mod_storage_gdbm.lua Mon Dec 21 11:31:10 2015 +0100 +++ b/mod_storage_gdbm/mod_storage_gdbm.lua Mon Dec 21 12:24:21 2015 +0100 @@ -29,10 +29,6 @@ return getmetatable(s) == st.stanza_mt; end -local function t(c, a, b) - if c then return a; end return b; -end - local base_path = path.resolve_relative_path(prosody.paths.data, module.host); lfs.mkdir(base_path); @@ -106,26 +102,26 @@ query = query or empty; local meta = self:get(username) or empty; local prefix = (username or "@") .. "#"; - local r = query.reverse; - local d = t(r, -1, 1); - local s = meta[t(r, query.before, query.after)]; + local reverse = query.reverse; + local step = reverse and -1 or 1; + local first = meta[query.after]; + local last = meta[query.before]; + -- we don't want to include the actual 'after' or 'before' item + if first then first = first + 1; end + if last then last = last - 1; end + first, last = first or 1, last or #meta; + if reverse then first, last = last, first; end local limit = query.limit; - if s then - s = s + d; - else - s = t(r, #meta, 1) - end - local e = t(r, 1, #meta); - local c = 0; + local count = 0; return function () - if limit and c >= limit then return end + if limit and count >= limit then return end local item, value; - for i = s, e, d do + for i = first, last, step do item = meta[i]; if (not query.with or item.with == query.with) and (not query.start or item.when >= query.start) and (not query["end"] or item.when <= query["end"]) then - s = i + d; c = c + 1; + first = i + step; count = count + 1; value = self:get(prefix..item.key); return item.key, (deserialize_map[item.type] or id)(value), item.when, item.with; end
--- a/mod_storage_lmdb/mod_storage_lmdb.lua Mon Dec 21 11:31:10 2015 +0100 +++ b/mod_storage_lmdb/mod_storage_lmdb.lua Mon Dec 21 12:24:21 2015 +0100 @@ -8,6 +8,11 @@ -- -- luacheck: globals prosody open +local assert = assert; +local select = select; +local xpcall = xpcall; +local traceback = debug.traceback; + local lmdb = require"lightningmdb"; local lfs = require"lfs"; local path = require"util.paths"; @@ -15,6 +20,36 @@ local serialize = serialization.serialize; local deserialize = serialization.deserialize; +local function transaction(env, flag, func, ...) + local args, n_args = {...}, select("#", ...); + local t = env:txn_begin(nil, flag); + local function f() return func(t, unpack(args, 1, n_args)); end + local success, a, b, c = xpcall(f, traceback); + if not success then + t:abort(); + return success, a; + end + local ok, err = t:commit(); + if not ok then + return ok, err; + end + return success, a, b, c; +end + +local function keyvalue_set(t, db, key, value) + if value ~= nil then + return assert(t:put(db, key, value, 0)); + else + return t:del(db, key, value); + end +end + +local function keyvalue_get(t, db, key) + local data, err = t:get(db, key, 0); + assert(data or not err, err); + return data; +end + local drivers = {}; local provider = {}; @@ -22,35 +57,19 @@ local keyval_mt = { __index = keyval, flags = lmdb.MDB_CREATE }; drivers.keyval = keyval_mt; -function keyval:set(user, value) - local t = self.env:txn_begin(nil, 0); +function keyval:set(key, value) if type(value) == "table" and next(value) == nil then value = nil; end if value ~= nil then value = serialize(value); end - local ok, err; - if value ~= nil then - ok, err = t:put(self.db, user, value, 0); - else - ok, err = t:del(self.db, user, value); - end - if not ok then - t:abort(); - return nil, err; - end - return t:commit(); + return transaction(self.env, 0, keyvalue_set, self.db, key, value); end -function keyval:get(user) - local t = self.env:txn_begin(nil, 0); - local data, err = t:get(self.db, user, 0); - if not data then - t:abort(); - return nil, err; - end - t:commit(); +function keyval:get(key) + local ok, data = transaction(self.env, lmdb.MDB_RDONLY, keyvalue_get, self.db, key); + if not ok then return ok, data; end return deserialize(data); end @@ -95,7 +114,7 @@ maxdbs = module:get_option_number("lmdb_maxdbs", 20); }); - function module.unload() + function module.unload() --luacheck: ignore provider.env:sync(1); provider.env:close(); end
--- a/mod_storage_xmlarchive/README.markdown Mon Dec 21 11:31:10 2015 +0100 +++ b/mod_storage_xmlarchive/README.markdown Mon Dec 21 12:24:21 2015 +0100 @@ -23,7 +23,7 @@ } ``` -To use it with [mod\_mam\_muc] or [mod_http_muc_log]: +To use it with [mod\_mam\_muc] or [mod\_http\_muc\_log]: ``` lua storage = { @@ -31,8 +31,8 @@ } ``` -Refer to [Prosodys data storage -documentation](https://prosody.im/doc/storage) for more information. +Refer to [Prosodys data storage documentation][doc:storage] for more +information. Note that this module does not implement the "keyval" storage method and can't be used by anything other than archives.
--- a/mod_storage_xmlarchive/mod_storage_xmlarchive.lua Mon Dec 21 11:31:10 2015 +0100 +++ b/mod_storage_xmlarchive/mod_storage_xmlarchive.lua Mon Dec 21 12:24:21 2015 +0100 @@ -12,21 +12,6 @@ local new_stream = require "util.xmppstream".new; local empty = {}; -local function fallocate(f, offset, len) - -- This assumes that current position == offset - local fake_data = (" "):rep(len); - local ok, msg = f:write(fake_data); - if not ok then - return ok, msg; - end - return f:seek("set", offset); -end; - -pcall(function() - local pposix = require "util.pposix"; - fallocate = pposix.fallocate or fallocate; -end); - local archive = {}; local archive_mt = { __index = archive }; @@ -43,25 +28,23 @@ data = tostring(data) .. "\n"; local day = dt.date(when); - local filename = dm.getpath(username.."@"..day, module.host, self.store, "xml", true); - - local ok, err; - local f = io.open(filename, "r+"); - if not f then - f, err = io.open(filename, "w"); if not f then return nil, err; end - ok, err = dm.list_append(username, module.host, self.store, day); - if not ok then return nil, err; end + local ok, err = dm.append_raw(username.."@"..day, module.host, self.store, "xml", data); + if not ok then + return nil, err; end - local offset = f:seek("end"); -- Seek to the end and collect current file length - -- then make sure there is enough free space for what we're about to add - ok, err = fallocate(f, offset, #data); if not ok then return nil, err; end - ok, err = f:write(data); if not ok then return nil, err; end - ok, err = f:close(); if not ok then return nil, err; end + local offset = ok and err; local id = day .. "-" .. hmac_sha256(username.."@"..day.."+"..offset, data, true):sub(-16); ok, err = dm.list_append(username.."@"..day, module.host, self.store, { id = id, when = dt.datetime(when), with = with, offset = offset, length = #data }); - if not ok then return nil, err; end + if offset == 0 then + -- means the message is at the beginnig of the file, so it's a new day + -- so we add this new day to the "index" + dm.list_append(username, module.host, self.store, day); + end + if not ok then + return nil, err; + end return id; end