diff options
Diffstat (limited to 'plugins')
-rw-r--r-- | plugins/mod_admin_adhoc.lua | 20 | ||||
-rw-r--r-- | plugins/mod_admin_shell.lua | 21 | ||||
-rw-r--r-- | plugins/mod_authz_internal.lua | 2 | ||||
-rw-r--r-- | plugins/mod_bookmarks.lua | 15 | ||||
-rw-r--r-- | plugins/mod_csi.lua | 4 | ||||
-rw-r--r-- | plugins/mod_http_file_share.lua | 4 | ||||
-rw-r--r-- | plugins/mod_invites_adhoc.lua | 30 | ||||
-rw-r--r-- | plugins/mod_pubsub/mod_pubsub.lua | 23 | ||||
-rw-r--r-- | plugins/mod_pubsub/pubsub.lib.lua | 56 | ||||
-rw-r--r-- | plugins/mod_s2s.lua | 30 | ||||
-rw-r--r-- | plugins/mod_smacks.lua | 12 | ||||
-rw-r--r-- | plugins/muc/hats.lib.lua | 2 | ||||
-rw-r--r-- | plugins/muc/muc.lib.lua | 10 |
13 files changed, 146 insertions, 83 deletions
diff --git a/plugins/mod_admin_adhoc.lua b/plugins/mod_admin_adhoc.lua index ee26b7e5..ca84f975 100644 --- a/plugins/mod_admin_adhoc.lua +++ b/plugins/mod_admin_adhoc.lua @@ -592,15 +592,15 @@ end, function(fields, err, data) return generate_error_message(err); end local ok_list, err_list = {}, {}; - for _, module in ipairs(fields.modules) do - local ok, err = modulemanager.reload(module_host, module); + for _, module_ in ipairs(fields.modules) do + local ok, err = modulemanager.reload(module_host, module_); if ok then - ok_list[#ok_list + 1] = module; + ok_list[#ok_list + 1] = module_; else - err_list[#err_list + 1] = module .. "(Error: " .. tostring(err) .. ")"; + err_list[#err_list + 1] = module_ .. "(Error: " .. tostring(err) .. ")"; end + module:log("info", "mod_%s reloaded by %s", module_, jid.bare(data.from)); end - module:log("info", "mod_%s reloaded by %s", fields.module, jid.bare(data.from)); local info = (#ok_list > 0 and ("The following modules were successfully reloaded on host "..module_host..":\n"..t_concat(ok_list, "\n")) or "") .. ((#ok_list > 0 and #err_list > 0) and "\n" or "") .. (#err_list > 0 and ("Failed to reload the following modules on host "..module_host..":\n"..t_concat(err_list, "\n")) or ""); @@ -742,15 +742,15 @@ end, function(fields, err, data) return generate_error_message(err); end local ok_list, err_list = {}, {}; - for _, module in ipairs(fields.modules) do - local ok, err = modulemanager.unload(module_host, module); + for _, module_ in ipairs(fields.modules) do + local ok, err = modulemanager.unload(module_host, module_); if ok then - ok_list[#ok_list + 1] = module; + ok_list[#ok_list + 1] = module_; else - err_list[#err_list + 1] = module .. "(Error: " .. tostring(err) .. ")"; + err_list[#err_list + 1] = module_ .. "(Error: " .. tostring(err) .. ")"; end + module:log("info", "mod_%s unloaded by %s", module_, jid.bare(data.from)); end - module:log("info", "mod_%s unloaded by %s", fields.module, jid.bare(data.from)); local info = (#ok_list > 0 and ("The following modules were successfully unloaded on host "..module_host..":\n"..t_concat(ok_list, "\n")) or "") .. ((#ok_list > 0 and #err_list > 0) and "\n" or "") .. (#err_list > 0 and ("Failed to unload the following modules on host "..module_host..":\n"..t_concat(err_list, "\n")) or ""); diff --git a/plugins/mod_admin_shell.lua b/plugins/mod_admin_shell.lua index e6b44f00..0b8d3c43 100644 --- a/plugins/mod_admin_shell.lua +++ b/plugins/mod_admin_shell.lua @@ -631,6 +631,7 @@ describe_command [[module:load(module, host) - Load the specified module on the function def_env.module:load(name, hosts) hosts = get_hosts_with_module(hosts); + local already_loaded = set.new(); -- Load the module for each host local ok, err, count, mod = true, nil, 0; for host in hosts do @@ -655,10 +656,18 @@ function def_env.module:load(name, hosts) self.session.print("Note: Module will not be loaded after restart unless enabled in configuration"); end end + else + already_loaded:add(host); end end - return ok, (ok and "Module loaded onto "..count.." host"..(count ~= 1 and "s" or "")) or ("Last error: "..tostring(err)); + if not ok then + return ok, "Last error: "..tostring(err); + end + if already_loaded == hosts then + return ok, "Module already loaded"; + end + return ok, "Module loaded onto "..count.." host"..(count ~= 1 and "s" or ""); end describe_command [[module:unload(module, host) - The same, but just unloads the module from memory]] @@ -1770,7 +1779,11 @@ function def_env.user:setrole(jid, host, new_role) elseif prosody.hosts[userhost] and not um.user_exists(username, userhost) then return nil, "No such user"; end - return um.set_user_role(username, host, new_role); + if userhost == host then + return um.set_user_role(username, userhost, new_role); + else + return um.set_jid_role(jid, host, new_role); + end end describe_command [[user:addrole(jid, host, role) - Add a secondary role to a user]] @@ -1781,6 +1794,8 @@ function def_env.user:addrole(jid, host, new_role) return nil, "No such host: "..host; elseif prosody.hosts[userhost] and not um.user_exists(username, userhost) then return nil, "No such user"; + elseif userhost ~= host then + return nil, "Can't add roles outside users own host" end return um.add_user_secondary_role(username, host, new_role); end @@ -1793,6 +1808,8 @@ function def_env.user:delrole(jid, host, role_name) return nil, "No such host: "..host; elseif prosody.hosts[userhost] and not um.user_exists(username, userhost) then return nil, "No such user"; + elseif userhost ~= host then + return nil, "Can't remove roles outside users own host" end return um.remove_user_secondary_role(username, host, role_name); end diff --git a/plugins/mod_authz_internal.lua b/plugins/mod_authz_internal.lua index 96324734..07091a04 100644 --- a/plugins/mod_authz_internal.lua +++ b/plugins/mod_authz_internal.lua @@ -265,7 +265,7 @@ function get_jid_role(jid) end function set_jid_role(jid, role_name) -- luacheck: ignore 212 - return false; + return false, "not-implemented"; end function get_jids_with_role(role_name) diff --git a/plugins/mod_bookmarks.lua b/plugins/mod_bookmarks.lua index be665d0f..e6e74f56 100644 --- a/plugins/mod_bookmarks.lua +++ b/plugins/mod_bookmarks.lua @@ -167,10 +167,15 @@ local function publish_to_pep(jid, bookmarks, synchronise) if synchronise then -- If we set zero legacy bookmarks, purge the bookmarks 2 node. module:log("debug", "No bookmark in the set, purging instead."); - return service:purge(namespace, jid, true); - else - return true; + local ok, err = service:purge(namespace, jid, true); + -- It's okay if no node exists when purging, user has + -- no bookmarks anyway. + if not ok and err ~= "item-not-found" then + module:log("error", "Failed to clear items from bookmarks 2 node: %s", err); + return ok, err; + end end + return true; end -- Retrieve the current bookmarks2. @@ -309,7 +314,7 @@ local function on_publish_legacy_pep(event) local ok, err = publish_to_pep(session.full_jid, bookmarks, true); if not ok then - module:log("error", "Failed to publish to PEP bookmarks for %s@%s: %s", session.username, session.host, err); + module:log("error", "Failed to sync legacy bookmarks to PEP for %s@%s: %s", session.username, session.host, err); session.send(st.error_reply(stanza, "cancel", "internal-server-error", "Failed to store bookmarks to PEP")); return true; end @@ -335,7 +340,7 @@ local function on_publish_private_xml(event) local ok, err = publish_to_pep(session.full_jid, bookmarks, true); if not ok then - module:log("error", "Failed to publish to PEP bookmarks for %s@%s: %s", session.username, session.host, err); + module:log("error", "Failed to sync private XML bookmarks to PEP for %s@%s: %s", session.username, session.host, err); session.send(st.error_reply(stanza, "cancel", "internal-server-error", "Failed to store bookmarks to PEP")); return true; end diff --git a/plugins/mod_csi.lua b/plugins/mod_csi.lua index 73c081b1..76a5afd4 100644 --- a/plugins/mod_csi.lua +++ b/plugins/mod_csi.lua @@ -34,9 +34,9 @@ module:hook_global("stats-update", function() if session.state == "inactive" then inactive = inactive + 1; elseif session.state == "active" then - inactive = inactive + 1; + active = active + 1; elseif session.state == "flushing" then - inactive = inactive + 1; + flushing = flushing + 1; end end end diff --git a/plugins/mod_http_file_share.lua b/plugins/mod_http_file_share.lua index cfc647d4..48972067 100644 --- a/plugins/mod_http_file_share.lua +++ b/plugins/mod_http_file_share.lua @@ -79,12 +79,12 @@ local measure_upload_cache_size = module:measure("upload_cache", "amount"); local measure_quota_cache_size = module:measure("quota_cache", "amount"); local measure_total_storage_usage = module:measure("total_storage", "amount", { unit = "bytes" }); -module:on_ready(function () +do local total, err = persist_stats:get(nil, "total"); if not err then total_storage_usage = tonumber(total) or 0; end -end) +end module:hook_global("stats-update", function () measure_upload_cache_size(upload_cache:count()); diff --git a/plugins/mod_invites_adhoc.lua b/plugins/mod_invites_adhoc.lua index c9954d8c..3ef4116d 100644 --- a/plugins/mod_invites_adhoc.lua +++ b/plugins/mod_invites_adhoc.lua @@ -2,6 +2,7 @@ local dataforms = require "prosody.util.dataforms"; local datetime = require "prosody.util.datetime"; local split_jid = require "prosody.util.jid".split; +local adhocutil = require "prosody.util.adhoc"; local new_adhoc = module:require("adhoc").new; @@ -98,3 +99,32 @@ module:provides("adhoc", new_adhoc("Create new account invite", "urn:xmpp:invite }; }; end, "admin")); + +local password_reset_form = dataforms.new({ + title = "Generate Password Reset Invite"; + { + name = "accountjid"; + type = "jid-single"; + required = true; + label = "The XMPP ID for the account to generate a password reset invite for"; + }; +}); + +module:provides("adhoc", new_adhoc("Create password reset invite", "xmpp:prosody.im/mod_invites_adhoc#password-reset", + adhocutil.new_simple_form(password_reset_form, + function (fields, err) + if err then return { status = "completed"; error = { message = "Fill in the form correctly" } }; end + local username = split_jid(fields.accountjid); + local invite = invites.create_account_reset(username); + return { + status = "completed"; + result = { + layout = invite_result_form; + values = { + uri = invite.uri; + url = invite.landing_page; + expire = datetime.datetime(invite.expires); + }; + }; + }; + end), "admin")); diff --git a/plugins/mod_pubsub/mod_pubsub.lua b/plugins/mod_pubsub/mod_pubsub.lua index 4f83088a..c17d9e63 100644 --- a/plugins/mod_pubsub/mod_pubsub.lua +++ b/plugins/mod_pubsub/mod_pubsub.lua @@ -190,10 +190,22 @@ module:hook("host-disco-items", function (event) end); local admin_aff = module:get_option_enum("default_admin_affiliation", "owner", "publisher", "member", "outcast", "none"); + module:default_permission("prosody:admin", ":service-admin"); -local function get_affiliation(jid) +module:default_permission("prosody:admin", ":create-node"); + +local function get_affiliation(jid, _, action) local bare_jid = jid_bare(jid); - if bare_jid == module.host or module:may(":service-admin", bare_jid) then + if bare_jid == module.host then + -- The host itself (i.e. local modules) is treated as an admin. + -- Check this first as to avoid sendig a host JID to :may() + return admin_aff; + end + if action == "create" and module:may(":create-node", bare_jid) then + -- Only one affiliation is allowed to create nodes by default + return "owner"; + end + if module:may(":service-admin", bare_jid) then return admin_aff; end end @@ -244,6 +256,13 @@ function module.load() broadcaster = simple_broadcast; itemcheck = is_item_stanza; check_node_config = check_node_config; + metadata_subset = { + "title"; + "description"; + "payload_type"; + "access_model"; + "publish_model"; + }; get_affiliation = get_affiliation; jid = module.host; diff --git a/plugins/mod_pubsub/pubsub.lib.lua b/plugins/mod_pubsub/pubsub.lib.lua index 8ae0a896..f4d44f36 100644 --- a/plugins/mod_pubsub/pubsub.lib.lua +++ b/plugins/mod_pubsub/pubsub.lib.lua @@ -1,4 +1,3 @@ -local t_unpack = table.unpack; local time_now = os.time; local jid_prep = require "prosody.util.jid".prep; @@ -18,7 +17,7 @@ local _M = {}; local handlers = {}; _M.handlers = handlers; -local pubsub_errors = { +local pubsub_errors = errors.init("pubsub", xmlns_pubsub_errors, { ["conflict"] = { "cancel", "conflict" }; ["invalid-jid"] = { "modify", "bad-request", nil, "invalid-jid" }; ["jid-required"] = { "modify", "bad-request", nil, "jid-required" }; @@ -33,16 +32,13 @@ local pubsub_errors = { ["precondition-not-met"] = { "cancel", "conflict", nil, "precondition-not-met" }; ["invalid-item"] = { "modify", "bad-request", "invalid item" }; ["persistent-items-unsupported"] = { "cancel", "feature-not-implemented", nil, "persistent-items" }; -}; -local function pubsub_error_reply(stanza, error) - local e = pubsub_errors[error]; - if not e and errors.is_err(error) then - e = { error.type, error.condition, error.text, error.pubsub_condition }; - end - local reply = st.error_reply(stanza, t_unpack(e, 1, 3)); - if e[4] then - reply:tag(e[4], { xmlns = xmlns_pubsub_errors }):up(); +}); +local function pubsub_error_reply(stanza, error, context) + local err = pubsub_errors.wrap(error, context); + if error == "precondition-not-met" and type(context) == "table" and type(context.field) == "string" then + err.text = "Field does not match: " .. context.field; end + local reply = st.error_reply(stanza, err); return reply; end _M.pubsub_error_reply = pubsub_error_reply; @@ -206,23 +202,28 @@ local node_metadata_form = dataform { }; { type = "text-single"; - name = "pubsub#title"; + name = "title"; + var = "pubsub#title"; }; { type = "text-single"; - name = "pubsub#description"; + name = "description"; + var = "pubsub#description"; }; { type = "text-single"; - name = "pubsub#type"; + name = "payload_type"; + var = "pubsub#type"; }; { type = "text-single"; - name = "pubsub#access_model"; + name = "access_model"; + var = "pubsub#access_model"; }; { type = "text-single"; - name = "pubsub#publish_model"; + name = "publish_model"; + var = "pubsub#publish_model"; }; }; _M.node_metadata_form = node_metadata_form; @@ -294,27 +295,14 @@ end function _M.handle_disco_info_node(event, service) local stanza, reply, node = event.stanza, event.reply, event.node; - local ok, ret = service:get_nodes(stanza.attr.from); + local ok, meta = service:get_node_metadata(node, stanza.attr.from); if not ok then - event.origin.send(pubsub_error_reply(stanza, ret)); - return true; - end - local node_obj = ret[node]; - if not node_obj then - event.origin.send(pubsub_error_reply(stanza, "item-not-found")); + event.origin.send(pubsub_error_reply(stanza, meta)); return true; end event.exists = true; reply:tag("identity", { category = "pubsub", type = "leaf" }):up(); - if node_obj.config then - reply:add_child(node_metadata_form:form({ - ["pubsub#title"] = node_obj.config.title; - ["pubsub#description"] = node_obj.config.description; - ["pubsub#type"] = node_obj.config.payload_type; - ["pubsub#access_model"] = node_obj.config.access_model; - ["pubsub#publish_model"] = node_obj.config.publish_model; - }, "result")); - end + reply:add_child(node_metadata_form:form(meta, "result")); end function _M.handle_disco_items_node(event, service) @@ -685,7 +673,7 @@ function handlers.set_publish(origin, stanza, publish, service) if item then item.attr.publisher = service.config.normalize_jid(stanza.attr.from); end - local ok, ret = service:publish(node, stanza.attr.from, id, item, required_config); + local ok, ret, context = service:publish(node, stanza.attr.from, id, item, required_config); local reply; if ok then if type(ok) == "string" then @@ -696,7 +684,7 @@ function handlers.set_publish(origin, stanza, publish, service) :tag("publish", { node = node }) :tag("item", { id = id }); else - reply = pubsub_error_reply(stanza, ret); + reply = pubsub_error_reply(stanza, ret, context); end origin.send(reply); return true; diff --git a/plugins/mod_s2s.lua b/plugins/mod_s2s.lua index 04fd5bc3..638ace3d 100644 --- a/plugins/mod_s2s.lua +++ b/plugins/mod_s2s.lua @@ -13,7 +13,6 @@ local hosts = prosody.hosts; local core_process_stanza = prosody.core_process_stanza; local tostring, type = tostring, type; -local t_insert = table.insert; local traceback = debug.traceback; local add_task = require "prosody.util.timer".add_task; @@ -33,6 +32,7 @@ local service = require "prosody.net.resolvers.service"; local resolver_chain = require "prosody.net.resolvers.chain"; local errors = require "prosody.util.error"; local set = require "prosody.util.set"; +local queue = require "prosody.util.queue"; local connect_timeout = module:get_option_period("s2s_timeout", 90); local stream_close_timeout = module:get_option_period("s2s_close_timeout", 5); @@ -42,6 +42,7 @@ local secure_domains, insecure_domains = module:get_option_set("s2s_secure_domains", {})._items, module:get_option_set("s2s_insecure_domains", {})._items; local require_encryption = module:get_option_boolean("s2s_require_encryption", true); local stanza_size_limit = module:get_option_integer("s2s_stanza_size_limit", 1024*512, 10000); +local sendq_size = module:get_option_integer("s2s_send_queue_size", 1024*32, 1); local advertised_idle_timeout = 14*60; -- default in all net.server implementations local network_settings = module:get_option("network_settings"); @@ -134,7 +135,7 @@ local bouncy_stanzas = { message = true, presence = true, iq = true }; local function bounce_sendq(session, reason) local sendq = session.sendq; if not sendq then return; end - session.log("info", "Sending error replies for %d queued stanzas because of failed outgoing connection to %s", #sendq, session.to_host); + session.log("info", "Sending error replies for %d queued stanzas because of failed outgoing connection to %s", sendq.count(), session.to_host); local dummy = { type = "s2sin"; send = function () @@ -153,12 +154,12 @@ local function bounce_sendq(session, reason) if session.had_stream then -- set when a stream is opened by the remote error_type, condition = "wait", "remote-server-timeout"; end - if errors.is_err(reason) then + if errors.is_error(reason) then error_type, condition, reason_text = reason.type, reason.condition, reason.text; elseif type(reason) == "string" then reason_text = reason; end - for i, stanza in ipairs(sendq) do + for stanza in sendq:consume() do if not stanza.attr.xmlns and bouncy_stanzas[stanza.name] and stanza.attr.type ~= "error" and stanza.attr.type ~= "result" then local reply = st.error_reply( stanza, @@ -170,7 +171,6 @@ local function bounce_sendq(session, reason) else (session.log or log)("debug", "Not eligible for bouncing, discarding %s", stanza:top_tag()); end - sendq[i] = nil; end session.sendq = nil; end @@ -194,11 +194,14 @@ function route_to_existing_session(event) (host.log or log)("debug", "trying to send over unauthed s2sout to "..to_host); -- Queue stanza until we are able to send it - if host.sendq then - t_insert(host.sendq, st.clone(stanza)); - else + if not host.sendq then -- luacheck: ignore 122 - host.sendq = { st.clone(stanza) }; + host.sendq = queue.new(sendq_size); + end + if not host.sendq:push(st.clone(stanza)) then + host.log("warn", "stanza [%s] not queued ", stanza.name); + event.origin.send(st.error_reply(stanza, "wait", "resource-constraint", "Outgoing stanza queue full")); + return true; end host.log("debug", "stanza [%s] queued ", stanza.name); return true; @@ -223,7 +226,8 @@ function route_to_new_session(event) -- Store in buffer host_session.bounce_sendq = bounce_sendq; - host_session.sendq = { st.clone(stanza) }; + host_session.sendq = queue.new(sendq_size); + host_session.sendq:push(st.clone(stanza)); log("debug", "stanza [%s] queued until connection complete", stanza.name); -- FIXME Cleaner solution to passing extra data from resolvers to net.server -- This mt-clone allows resolvers to add extra data, currently used for DANE TLSA records @@ -362,11 +366,11 @@ function mark_connected(session) if session.direction == "outgoing" then if sendq then - session.log("debug", "sending %d queued stanzas across new outgoing connection to %s", #sendq, session.to_host); + session.log("debug", "sending %d queued stanzas across new outgoing connection to %s", sendq.count(), session.to_host); local send = session.sends2s; - for i, stanza in ipairs(sendq) do + for stanza in sendq:consume() do + -- TODO check send success send(stanza); - sendq[i] = nil; end session.sendq = nil; end diff --git a/plugins/mod_smacks.lua b/plugins/mod_smacks.lua index d4f0f371..7a9a67b3 100644 --- a/plugins/mod_smacks.lua +++ b/plugins/mod_smacks.lua @@ -541,11 +541,13 @@ module:hook("pre-resource-unbind", function (event) return end - session.log("debug", "Destroying session for hibernating too long"); - save_old_session(session); - session.resumption_token = nil; - sessionmanager.destroy_session(session, "Hibernating too long"); - sessions_expired(1); + prosody.main_thread:run(function () + session.log("debug", "Destroying session for hibernating too long"); + save_old_session(session); + session.resumption_token = nil; + sessionmanager.destroy_session(session, "Hibernating too long"); + sessions_expired(1); + end); end); if session.conn then local conn = session.conn; diff --git a/plugins/muc/hats.lib.lua b/plugins/muc/hats.lib.lua index 492dc72c..7eb71eb4 100644 --- a/plugins/muc/hats.lib.lua +++ b/plugins/muc/hats.lib.lua @@ -25,7 +25,7 @@ module:hook("muc-build-occupant-presence", function (event) hats_el:tag("hat", { uri = hat_id, title = hat_data.title }):up(); if hats_compat then - if not hats_el then + if not legacy_hats_el then legacy_hats_el = st.stanza("hats", { xmlns = xmlns_hats_legacy }); end legacy_hats_el:tag("hat", { uri = hat_id, title = hat_data.title }):up(); diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index b8f276cf..359afc87 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -304,10 +304,10 @@ function room_mt:publicise_occupant_status(occupant, x, nick, actor, reason, pre -- General populace for occupant_nick, n_occupant in self:each_occupant() do if occupant_nick ~= occupant.nick then - local pr = get_p(n_occupant); if broadcast_roles[occupant.role or "none"] or force_unavailable then - self:route_to_occupant(n_occupant, pr); + self:route_to_occupant(n_occupant, get_p(n_occupant)); elseif prev_role and broadcast_roles[prev_role] then + local pr = get_p(n_occupant); pr.attr.type = 'unavailable'; self:route_to_occupant(n_occupant, pr); end @@ -339,16 +339,14 @@ function room_mt:send_occupant_list(to, filter) local broadcast_bare_jids = {}; -- Track which bare JIDs we have sent presence for for occupant_jid, occupant in self:each_occupant() do broadcast_bare_jids[occupant.bare_jid] = true; - if filter == nil or filter(occupant_jid, occupant) then + if (filter == nil or filter(occupant_jid, occupant)) and (to_bare == occupant.bare_jid or broadcast_roles[occupant.role or "none"]) then local x = st.stanza("x", {xmlns='http://jabber.org/protocol/muc#user'}); self:build_item_list(occupant, x, is_anonymous and to_bare ~= occupant.bare_jid); -- can always see your own jids local pres = st.clone(occupant:get_presence()); pres.attr.to = to; pres:add_child(x); module:fire_event("muc-build-occupant-presence", { room = self, occupant = occupant, stanza = pres }); - if to_bare == occupant.bare_jid or broadcast_roles[occupant.role or "none"] then - self:route_stanza(pres); - end + self:route_stanza(pres); end end if broadcast_roles.none then |