# HG changeset patch # User Matthew Wild # Date 1267653905 0 # Node ID 1edeb8fe7d149edb7861525791d6ba91aa513678 # Parent 46dfcc33ea9e7874f3cd3a2046a6c68b15888e49# Parent fa84451e9b359c299a2a947f282ccf32227c53d7 Merge 0.6.2/waqas with 0.6.2/MattJ diff -r 46dfcc33ea9e -r 1edeb8fe7d14 core/configmanager.lua --- a/core/configmanager.lua Fri Feb 19 03:30:27 2010 +0000 +++ b/core/configmanager.lua Wed Mar 03 22:05:05 2010 +0000 @@ -9,8 +9,9 @@ local _G = _G; -local setmetatable, loadfile, pcall, rawget, rawset, io, error, dofile, type = - setmetatable, loadfile, pcall, rawget, rawset, io, error, dofile, type; +local setmetatable, loadfile, pcall, rawget, rawset, io, error, dofile, type, pairs, table, format = + setmetatable, loadfile, pcall, rawget, rawset, io, error, dofile, type, pairs, table, string.format; + local eventmanager = require "core.eventmanager"; @@ -115,6 +116,10 @@ rawset(env, "__currenthost", "*") -- Default is global function env.Host(name) + if rawget(config, name) and rawget(config[name].core, "component_module") then + error(format("Host %q clashes with previously defined %s Component %q, for services use a sub-domain like conference.%s", + name, config[name].core.component_module:gsub("^%a+$", { component = "external", muc = "MUC"}), name, name), 0); + end rawset(env, "__currenthost", name); -- Needs at least one setting to logically exist :) set(name or "*", "core", "defined", true); @@ -122,6 +127,10 @@ env.host = env.Host; function env.Component(name) + if rawget(config, name) and rawget(config[name].core, "defined") and not rawget(config[name].core, "component_module") then + error(format("Component %q clashes with previously defined Host %q, for services use a sub-domain like conference.%s", + name, name, name), 0); + end set(name, "core", "component_module", "component"); -- Don't load the global modules by default set(name, "core", "load_global_modules", false); diff -r 46dfcc33ea9e -r 1edeb8fe7d14 core/hostmanager.lua --- a/core/hostmanager.lua Fri Feb 19 03:30:27 2010 +0000 +++ b/core/hostmanager.lua Wed Mar 03 22:05:05 2010 +0000 @@ -33,12 +33,19 @@ local function load_enabled_hosts(config) local defined_hosts = config or configmanager.getconfig(); + local activated_any_host; for host, host_config in pairs(defined_hosts) do if host ~= "*" and (host_config.core.enabled == nil or host_config.core.enabled) and not host_config.core.component_module then + activated_any_host = true; activate(host, host_config); end end + + if not activated_any_host then + log("error", "No hosts defined in the config file. This may cause unexpected behaviour as no modules will be loaded."); + end + eventmanager.fire_event("hosts-activated", defined_hosts); hosts_loaded_once = true; end diff -r 46dfcc33ea9e -r 1edeb8fe7d14 core/modulemanager.lua --- a/core/modulemanager.lua Fri Feb 19 03:30:27 2010 +0000 +++ b/core/modulemanager.lua Wed Mar 03 22:05:05 2010 +0000 @@ -197,6 +197,15 @@ end end hooks:remove(host, name); + if mod.module.items then -- remove items + for key,t in pairs(mod.module.items) do + for i = #t,1,-1 do + local value = t[i]; + t[i] = nil; + hosts[host].events.fire_event("item-removed/"..key, {source = self, item = value}); + end + end + end modulemap[host][name] = nil; return true; end diff -r 46dfcc33ea9e -r 1edeb8fe7d14 core/s2smanager.lua --- a/core/s2smanager.lua Fri Feb 19 03:30:27 2010 +0000 +++ b/core/s2smanager.lua Wed Mar 03 22:05:05 2010 +0000 @@ -504,6 +504,8 @@ end end +local function null_data_handler(conn, data) log("debug", "Discarding data from destroyed s2s session: %s", data); end + function destroy_session(session, reason) (session.log or log)("info", "Destroying "..tostring(session.direction).." session "..tostring(session.from_host).."->"..tostring(session.to_host)); @@ -519,6 +521,7 @@ session[k] = nil; end end + session.data = null_data_handler; end return _M; diff -r 46dfcc33ea9e -r 1edeb8fe7d14 core/sessionmanager.lua --- a/core/sessionmanager.lua Fri Feb 19 03:30:27 2010 +0000 +++ b/core/sessionmanager.lua Wed Mar 03 22:05:05 2010 +0000 @@ -10,7 +10,6 @@ local tonumber, tostring = tonumber, tostring; local ipairs, pairs, print, next= ipairs, pairs, print, next; -local collectgarbage = collectgarbage; local format = import("string", "format"); local hosts = hosts; @@ -25,6 +24,7 @@ local rm_load_roster = require "core.rostermanager".load_roster; local config_get = require "core.configmanager".get; local nameprep = require "util.encodings".stringprep.nameprep; +local resourceprep = require "util.encodings".stringprep.resourceprep; local fire_event = require "core.eventmanager".fire_event; local add_task = require "util.timer".add_task; @@ -66,6 +66,8 @@ return session; end +local function null_data_handler(conn, data) log("debug", "Discarding data from destroyed c2s session: %s", data); end + function destroy_session(session, err) (session.log or log)("info", "Destroying session for %s (%s@%s)", session.full_jid or "(unknown)", session.username or "(unknown)", session.host or "(unknown)"); @@ -88,6 +90,7 @@ session[k] = nil; end end + session.data = null_data_handler; end function make_authenticated(session, username) @@ -106,7 +109,8 @@ if session.resource then return nil, "cancel", "already-bound", "Cannot bind multiple resources on a single connection"; end -- We don't support binding multiple resources - resource = resource or uuid_generate(); + resource = resourceprep(resource); + resource = resource ~= "" and resource or uuid_generate(); --FIXME: Randomly-generated resources must be unique per-user, and never conflict with existing if not hosts[session.host].sessions[session.username] then diff -r 46dfcc33ea9e -r 1edeb8fe7d14 core/stanza_router.lua --- a/core/stanza_router.lua Fri Feb 19 03:30:27 2010 +0000 +++ b/core/stanza_router.lua Wed Mar 03 22:05:05 2010 +0000 @@ -36,12 +36,14 @@ end end - if origin.type == "c2s" then + if origin.type == "c2s" and stanza.attr.xmlns == "jabber:client" then if not origin.full_jid and not(stanza.name == "iq" and stanza.attr.type == "set" and stanza.tags[1] and stanza.tags[1].name == "bind" and stanza.tags[1].attr.xmlns == "urn:ietf:params:xml:ns:xmpp-bind") then -- authenticated client isn't bound and current stanza is not a bind request - origin.send(st.error_reply(stanza, "auth", "not-authorized")); -- FIXME maybe allow stanzas to account or server + if stanza.attr.type ~= "result" and stanza.attr.type ~= "error" then + origin.send(st.error_reply(stanza, "auth", "not-authorized")); -- FIXME maybe allow stanzas to account or server + end return; end @@ -98,7 +100,7 @@ return; -- FIXME what should we do here? does this work with subdomains? end end - core_post_stanza(origin, stanza); + core_post_stanza(origin, stanza, origin.full_jid); else local h = hosts[stanza.attr.to or origin.host or origin.to_host]; if h then @@ -119,7 +121,7 @@ end end -function core_post_stanza(origin, stanza) +function core_post_stanza(origin, stanza, preevents) local to = stanza.attr.to; local node, host, resource = jid_split(to); local to_bare = node and (node.."@"..host) or host; -- bare JID @@ -143,7 +145,7 @@ end local event_data = {origin=origin, stanza=stanza}; - if origin.full_jid == stanza.attr.from then -- c2s connection + if preevents then -- c2s connection if hosts[origin.host].events.fire_event('pre-'..stanza.name..to_type, event_data) then return; end -- do preprocessing end local h = hosts[to_bare] or hosts[host or origin.host]; @@ -191,6 +193,6 @@ log("debug", "Routing outgoing stanza for %s to %s", from_host, host); send_s2s(from_host, host, stanza); else - log("warn", "received stanza from unhandled connection type: %s", origin.type); + log("warn", "received %s stanza from unhandled connection type: %s", tostring(stanza.name), tostring(origin.type)); end end diff -r 46dfcc33ea9e -r 1edeb8fe7d14 man/prosodyctl.man --- a/man/prosodyctl.man Fri Feb 19 03:30:27 2010 +0000 +++ b/man/prosodyctl.man Wed Mar 03 22:05:05 2010 +0000 @@ -76,4 +76,4 @@ More information may be found online at: \fIhttp://prosody.im/\fP .SH AUTHORS -Dwayne Bent +Dwayne Bent diff -r 46dfcc33ea9e -r 1edeb8fe7d14 net/dns.lua --- a/net/dns.lua Fri Feb 19 03:30:27 2010 +0000 +++ b/net/dns.lua Wed Mar 03 22:05:05 2010 +0000 @@ -594,17 +594,18 @@ function resolver:remember(rr, type) -- - - - - - - - - - - - - - remember --print ('remember', type, rr.class, rr.type, rr.name) + local qname, qtype, qclass = standardize(rr.name, rr.type, rr.class); if type ~= '*' then - type = rr.type; - local all = get(self.cache, rr.class, '*', rr.name); + type = qtype; + local all = get(self.cache, qclass, '*', qname); --print('remember all', all); if all then append(all, rr); end end self.cache = self.cache or setmetatable({}, cache_metatable); - local rrs = get(self.cache, rr.class, type, rr.name) or - set(self.cache, rr.class, type, rr.name, setmetatable({}, rrs_metatable)); + local rrs = get(self.cache, qclass, type, qname) or + set(self.cache, qclass, type, qname, setmetatable({}, rrs_metatable)); append(rrs, rr); if type == 'MX' then self.unsorted[rrs] = true; end diff -r 46dfcc33ea9e -r 1edeb8fe7d14 net/httpserver.lua --- a/net/httpserver.lua Fri Feb 19 03:30:27 2010 +0000 +++ b/net/httpserver.lua Wed Mar 03 22:05:05 2010 +0000 @@ -36,8 +36,8 @@ local function send_response(request, response) -- Write status line local resp; - if response.body then - local body = tostring(response.body); + if response.body or response.headers then + local body = response.body and tostring(response.body); log("debug", "Sending response to %s", request.id); resp = { "HTTP/1.0 ", response.status or "200 OK", "\r\n"}; local h = response.headers; @@ -49,14 +49,14 @@ t_insert(resp, "\r\n"); end end - if not (h and h["Content-Length"]) then + if body and not (h and h["Content-Length"]) then t_insert(resp, "Content-Length: "); t_insert(resp, #body); t_insert(resp, "\r\n"); end t_insert(resp, "\r\n"); - if request.method ~= "HEAD" then + if body and request.method ~= "HEAD" then t_insert(resp, body); end else @@ -147,22 +147,29 @@ elseif request.state == "headers" then log("debug", "Reading headers...") local pos = startpos; - local headers = request.headers or {}; + local headers, headers_complete = request.headers; + if not headers then + headers = {}; + request.headers = headers; + end + for line in data:gmatch("(.-)\r\n") do startpos = (startpos or 1) + #line + 2; local k, v = line:match("(%S+): (.+)"); if k and v then headers[k:lower()] = v; --- log("debug", "Header: "..k:lower().." = "..v); + --log("debug", "Header: '"..k:lower().."' = '"..v.."'"); elseif #line == 0 then - request.headers = headers; + headers_complete = true; break; else log("debug", "Unhandled header line: "..line); end end - if not expectbody(request) then + if not headers_complete then return; end + + if not expectbody(request) then call_callback(request); return; end @@ -176,7 +183,10 @@ log("debug", "Reading request line...") local method, path, http, linelen = data:match("^(%S+) (%S+) HTTP/(%S+)\r\n()", startpos); if not method then - return call_callback(request, "invalid-status-line"); + log("warn", "Invalid HTTP status line, telling callback then closing"); + local ret = call_callback(request, "invalid-status-line"); + request:destroy(); + return ret; end request.method, request.path, request.httpversion = method, path, http; diff -r 46dfcc33ea9e -r 1edeb8fe7d14 net/xmppclient_listener.lua --- a/net/xmppclient_listener.lua Fri Feb 19 03:30:27 2010 +0000 +++ b/net/xmppclient_listener.lua Wed Mar 03 22:05:05 2010 +0000 @@ -140,7 +140,6 @@ sm_destroy_session(session, err); sessions[conn] = nil; session = nil; - collectgarbage("collect"); end end diff -r 46dfcc33ea9e -r 1edeb8fe7d14 net/xmppcomponent_listener.lua --- a/net/xmppcomponent_listener.lua Fri Feb 19 03:30:27 2010 +0000 +++ b/net/xmppcomponent_listener.lua Wed Mar 03 22:05:05 2010 +0000 @@ -169,7 +169,6 @@ sessions[conn] = nil; for k in pairs(session) do session[k] = nil; end session = nil; - collectgarbage("collect"); end end diff -r 46dfcc33ea9e -r 1edeb8fe7d14 net/xmppserver_listener.lua --- a/net/xmppserver_listener.lua Fri Feb 19 03:30:27 2010 +0000 +++ b/net/xmppserver_listener.lua Wed Mar 03 22:05:05 2010 +0000 @@ -162,7 +162,6 @@ s2s_destroy_session(session, err); sessions[conn] = nil; session = nil; - collectgarbage("collect"); end end diff -r 46dfcc33ea9e -r 1edeb8fe7d14 plugins/mod_bosh.lua --- a/plugins/mod_bosh.lua Fri Feb 19 03:30:27 2010 +0000 +++ b/plugins/mod_bosh.lua Wed Mar 03 22:05:05 2010 +0000 @@ -152,7 +152,7 @@ local r, send_buffer = session.requests, session.send_buffer; local response = { headers = default_headers } function session.send(s) - log("debug", "Sending BOSH data: %s", tostring(s)); + --log("debug", "Sending BOSH data: %s", tostring(s)); local oldest_request = r[1]; while oldest_request and oldest_request.destroyed do t_remove(r, 1); @@ -160,7 +160,7 @@ oldest_request = r[1]; end if oldest_request then - log("debug", "We have an open request, so using that to send with"); + log("debug", "We have an open request, so sending on that"); response.body = t_concat{"", tostring(s), "" }; oldest_request:send(response); --log("debug", "Sent"); diff -r 46dfcc33ea9e -r 1edeb8fe7d14 plugins/mod_component.lua --- a/plugins/mod_component.lua Fri Feb 19 03:30:27 2010 +0000 +++ b/plugins/mod_component.lua Wed Mar 03 22:05:05 2010 +0000 @@ -44,7 +44,7 @@ local secret = config.get(session.user, "core", "component_secret"); if not secret then - (session.log or log)("warn", "Component attempted to identify as %s, but component_password is not set", session.user); + (session.log or log)("warn", "Component attempted to identify as %s, but component_secret is not set", session.user); session:close("not-authorized"); return; end diff -r 46dfcc33ea9e -r 1edeb8fe7d14 plugins/mod_compression.lua --- a/plugins/mod_compression.lua Fri Feb 19 03:30:27 2010 +0000 +++ b/plugins/mod_compression.lua Wed Mar 03 22:05:05 2010 +0000 @@ -14,10 +14,10 @@ local compression_stream_feature = st.stanza("compression", {xmlns=xmlns_compression_feature}):tag("method"):text("zlib"):up(); local compression_level = module:get_option("compression_level"); - -- if not defined assume admin wants best compression if compression_level == nil then compression_level = 9 end; + compression_level = tonumber(compression_level); if not compression_level or compression_level < 1 or compression_level > 9 then module:log("warn", "Invalid compression level in config: %s", tostring(compression_level)); @@ -41,7 +41,7 @@ if session.compressed then local error_st = st.stanza("failure", {xmlns=xmlns_compression_protocol}):tag("unsupported-method"); session.send(error_st); - session:log("warn", "Tried to establish another compression layer."); + session.log("warn", "Tried to establish another compression layer."); end -- checking if the compression method is supported @@ -56,7 +56,7 @@ if status == false then local error_st = st.stanza("failure", {xmlns=xmlns_compression_protocol}):tag("setup-failed"); session.send(error_st); - session:log("error", "Failed to create zlib.deflate filter."); + session.log("error", "Failed to create zlib.deflate filter."); module:log("error", deflate_stream); return end @@ -65,7 +65,7 @@ if status == false then local error_st = st.stanza("failure", {xmlns=xmlns_compression_protocol}):tag("setup-failed"); session.send(error_st); - session:log("error", "Failed to create zlib.deflate filter."); + session.log("error", "Failed to create zlib.deflate filter."); module:log("error", inflate_stream); return end diff -r 46dfcc33ea9e -r 1edeb8fe7d14 plugins/mod_console.lua --- a/plugins/mod_console.lua Fri Feb 19 03:30:27 2010 +0000 +++ b/plugins/mod_console.lua Wed Mar 03 22:05:05 2010 +0000 @@ -192,7 +192,7 @@ elseif section == "server" then print [[server:version() - Show the server's version number]] print [[server:uptime() - Show how long the server has been running]] - --print [[server:shutdown(reason) - Shut down the server, with an optional reason to be broadcast to all connections]] + print [[server:shutdown(reason) - Shut down the server, with an optional reason to be broadcast to all connections]] elseif section == "config" then print [[config:reload() - Reload the server configuration. Modules may need to be reloaded for changes to take effect.]] elseif section == "console" then diff -r 46dfcc33ea9e -r 1edeb8fe7d14 plugins/mod_debug.lua --- a/plugins/mod_debug.lua Fri Feb 19 03:30:27 2010 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,191 +0,0 @@ --- Prosody IM --- Copyright (C) 2008-2009 Matthew Wild --- Copyright (C) 2008-2009 Waqas Hussain --- --- This project is MIT/X11 licensed. Please see the --- COPYING file in the source package for more information. --- - -module.host = "*"; - -local connlisteners_register = require "net.connlisteners".register; - -local console_listener = { default_port = 5583; default_mode = "*l"; default_interface = "127.0.0.1" }; - -local sha256, missingglobal = require "util.hashes".sha256; - -local commands = {}; -local debug_env = {}; -local debug_env_mt = { __index = function (t, k) return rawget(_G, k) or missingglobal(k); end, __newindex = function (t, k, v) rawset(_G, k, v); end }; - -local t_insert, t_concat = table.insert, table.concat; -local t_concatall = function (t, sep) local tt = {}; for k, s in pairs(t) do tt[k] = tostring(s); end return t_concat(tt, sep); end - - -setmetatable(debug_env, debug_env_mt); - -console = {}; - -function console:new_session(conn) - local w = function(s) conn.write(s:gsub("\n", "\r\n")); end; - local session = { conn = conn; - send = function (t) w(tostring(t)); end; - print = function (t) w("| "..tostring(t).."\n"); end; - disconnect = function () conn.close(); end; - }; - - return session; -end - -local sessions = {}; - -function console_listener.listener(conn, data) - local session = sessions[conn]; - - if not session then - -- Handle new connection - session = console:new_session(conn); - sessions[conn] = session; - printbanner(session); - end - if data then - -- Handle data - (function(session, data) - if data:match("[!.]$") then - local command = data:lower(); - command = data:match("^%w+") or data:match("%p"); - if commands[command] then - commands[command](session, data); - return; - end - end - - local chunk, err = loadstring("return "..data); - if not chunk then - chunk, err = loadstring(data); - if not chunk then - err = err:gsub("^%[string .-%]:%d+: ", ""); - err = err:gsub("^:%d+: ", ""); - err = err:gsub("''", "the end of the line"); - session.print("Sorry, I couldn't understand that... "..err); - return; - end - end - - debug_env.print = session.print; - - setfenv(chunk, debug_env); - - local ret = { pcall(chunk) }; - - if not ret[1] then - session.print("Fatal error while running command, it did not complete"); - session.print("Error: "..ret[2]); - return; - end - - table.remove(ret, 1); - - local retstr = t_concatall(ret, ", "); - if retstr ~= "" then - session.print("Result: "..retstr); - else - session.print("No result, or nil"); - return; - end - end)(session, data); - end - session.send(string.char(0)); -end - -function console_listener.disconnect(conn, err) - -end - -connlisteners_register('debug', console_listener); -require "net.connlisteners".start("debug"); - --- Console commands -- --- These are simple commands, not valid standalone in Lua - -function commands.bye(session) - session.print("See you! :)"); - session.disconnect(); -end - -commands["!"] = function (session, data) - if data:match("^!!") then - session.print("!> "..session.env._); - return console_listener.listener(session.conn, session.env._); - end - local old, new = data:match("^!(.-[^\\])!(.-)!$"); - if old and new then - local ok, res = pcall(string.gsub, session.env._, old, new); - if not ok then - session.print(res) - return; - end - session.print("!> "..res); - return console_listener.listener(session.conn, res); - end - session.print("Sorry, not sure what you want"); -end - -function printbanner(session) -session.print [[ - ____ \ / _ - | _ \ _ __ ___ ___ _-_ __| |_ _ - | |_) | '__/ _ \/ __|/ _ \ / _` | | | | - | __/| | | (_) \__ \ |_| | (_| | |_| | - |_| |_| \___/|___/\___/ \__,_|\__, | - A study in simplicity |___/ - -]] -session.print("Welcome to the Prosody debug console. For a list of commands, type: help"); -session.print("You may find more help on using this console in our online documentation at "); -session.print("http://prosody.im/doc/debugconsole\n"); -end - -local byte, char = string.byte, string.char; -local gmatch, gsub = string.gmatch, string.gsub; - -local function vdecode(text, key) - local keyarr = {}; - for l in gmatch(key, ".") do t_insert(keyarr, byte(l) - 32) end - local pos, keylen = 0, #keyarr; - return (gsub(text, ".", function (letter) - if byte(letter) < 32 then return ""; end - pos = (pos%keylen)+1; - return char(((byte(letter) - 32 - keyarr[pos]) % 94) + 32); - end)); -end - -local subst = { - ["f880c08056ba7dbecb1ccfe5d7728bd6dcd654e94f7a9b21788c43397bae0bc5"] = - [=[nRYeKR$l'5Ix%u*1Mc-K}*bwv*\ $1KLMBd$KH R38`$[6}VQ@,6Qn]=]; - ["92f718858322157202ec740698c1390e47bc819e52b6a099c54c378a9f7529d6"] = - [=[V\Z5`WZ5,T$<)7LM'w3Z}M(7V'{pa) &'>0+{v)O(0M*V5K$$LL$|2wT}6 - 1as*")e!>]=]; - ["467b65edcc7c7cd70abf2136cc56abd037216a6cd9e17291a2219645be2e2216"] = - [=[i#'Z,E1-"YaHW(j/0xs]I4x&%(Jx1h&18'(exNWT D3b+K{*8}w(%D {]=]; - ["f73729d7f2fbe686243a25ac088c7e6aead3d535e081329f2817438a5c78bee5"] = - [=[,3+(Q{3+W\ftQ%wvv/C0z-l%f>ABc(vkp + payload = payload.tags[1]; + if payload and (payload.name == 'publish' or payload.name == 'retract') and payload.attr.node then -- + local node = payload.attr.node; + payload = payload.tags[1]; + if payload and payload.name == "item" then -- + local id = payload.attr.id; + session.send(st.reply(stanza)); + publish(session, node, id, st.clone(payload)); + return true; + end + end + elseif stanza.attr.type == 'get' then + local user = stanza.attr.to and jid_bare(stanza.attr.to) or session.username..'@'..session.host; + if subscription_presence(user, stanza.attr.from) then + local user_data = data[user]; + local node, requested_id; payload = payload.tags[1]; - if payload and (payload.name == 'publish' or payload.name == 'retract') and payload.attr.node then -- - local node = payload.attr.node; - payload = payload.tags[1]; - if payload and payload.name == "item" then -- - session.send(st.reply(stanza)); - publish(session, node, st.clone(payload)); + if payload and payload.name == 'items' then + node = payload.attr.node; + local item = payload.tags[1]; + if item and item.name == "item" then + requested_id = item.attr.id; + end + end + if node and user_data and user_data[node] then -- Send the last item + local id, item = unpack(user_data[node]); + if not requested_id or id == requested_id then + local stanza = st.reply(stanza) + :tag('pubsub', {xmlns='http://jabber.org/protocol/pubsub'}) + :tag('items', {node=node}) + :add_child(item) + :up() + :up(); + session.send(stanza); + return true; + else -- requested item doesn't exist + local stanza = st.reply(stanza) + :tag('pubsub', {xmlns='http://jabber.org/protocol/pubsub'}) + :tag('items', {node=node}) + :up(); + session.send(stanza); return true; end + elseif node then -- node doesn't exist + session.send(st.error_reply(stanza, 'cancel', 'item-not-found')); + return true; + else --invalid request + session.send(st.error_reply(stanza, 'modify', 'bad-request')); + return true; end + else --no presence subscription + session.send(st.error_reply(stanza, 'auth', 'not-authorized') + :tag('presence-subscription-required', {xmlns='http://jabber.org/protocol/pubsub#errors'})); + return true; end end end); diff -r 46dfcc33ea9e -r 1edeb8fe7d14 plugins/mod_presence.lua --- a/plugins/mod_presence.lua Fri Feb 19 03:30:27 2010 +0000 +++ b/plugins/mod_presence.lua Wed Mar 03 22:05:05 2010 +0000 @@ -18,6 +18,7 @@ local jid_split = require "util.jid".split; local jid_bare = require "util.jid".bare; local hosts = hosts; +local NULL = {}; local rostermanager = require "core.rostermanager"; local sessionmanager = require "core.sessionmanager"; @@ -54,16 +55,21 @@ end return recipients; end -local function recalc_resource_map(origin) - local user = hosts[origin.host].sessions[origin.username]; - user.top_resources = select_top_resources(user); - if #user.top_resources == 0 then user.top_resources = nil; end +local function recalc_resource_map(user) + if user then + user.top_resources = select_top_resources(user); + if #user.top_resources == 0 then user.top_resources = nil; end + end end function handle_normal_presence(origin, stanza, core_route_stanza) + if full_sessions[origin.full_jid] then -- if user is still connected + origin.send(stanza); -- reflect their presence back to them + end local roster = origin.roster; local node, host = origin.username, origin.host; - for _, res in pairs(hosts[host].sessions[node].sessions) do -- broadcast to all resources + local user = bare_sessions[node.."@"..host]; + for _, res in pairs(user and user.sessions or NULL) do -- broadcast to all resources if res ~= origin and res.presence then -- to resource stanza.attr.to = res.full_jid; core_route_stanza(origin, stanza); @@ -76,6 +82,7 @@ end end if stanza.attr.type == nil and not origin.presence then -- initial presence + origin.presence = stanza; -- FIXME repeated later local probe = st.presence({from = origin.full_jid, type = "probe"}); for jid, item in pairs(roster) do -- probe all contacts we are subscribed to if item.subscription == "both" or item.subscription == "to" then @@ -83,7 +90,7 @@ core_route_stanza(origin, probe); end end - for _, res in pairs(hosts[host].sessions[node].sessions) do -- broadcast from all available resources + for _, res in pairs(user and user.sessions or NULL) do -- broadcast from all available resources if res ~= origin and res.presence then res.presence.attr.to = origin.full_jid; core_route_stanza(res, res.presence); @@ -114,7 +121,7 @@ origin.presence = nil; if origin.priority then origin.priority = nil; - recalc_resource_map(origin); + recalc_resource_map(user); end if origin.directed then for jid in pairs(origin.directed) do @@ -136,7 +143,7 @@ else priority = 0; end if origin.priority ~= priority then origin.priority = priority; - recalc_resource_map(origin); + recalc_resource_map(user); end end stanza.attr.to = nil; -- reset it @@ -217,7 +224,7 @@ if stanza.attr.type == "probe" then if rostermanager.is_contact_subscribed(node, host, from_bare) then if 0 == send_presence_of_available_resources(node, host, st_from, origin, core_route_stanza) then - -- TODO send last recieved unavailable presence (or we MAY do nothing, which is fine too) + core_route_stanza(hosts[host], st.presence({from=to_bare, to=from_bare, type="unavailable"})); -- TODO send last activity end else core_route_stanza(hosts[host], st.presence({from=to_bare, to=from_bare, type="unsubscribed"})); @@ -227,7 +234,7 @@ core_route_stanza(hosts[host], st.presence({from=to_bare, to=from_bare, type="subscribed"})); -- already subscribed -- Sending presence is not clearly stated in the RFC, but it seems appropriate if 0 == send_presence_of_available_resources(node, host, from_bare, origin, core_route_stanza) then - -- TODO send last recieved unavailable presence (or we MAY do nothing, which is fine too) + core_route_stanza(hosts[host], st.presence({from=to_bare, to=from_bare, type="unavailable"})); -- TODO send last activity end else core_route_stanza(hosts[host], st.presence({from=to_bare, to=from_bare, type="unavailable"})); -- acknowledging receipt @@ -326,6 +333,20 @@ end -- resource not online, discard return true; end); +module:hook("presence/host", function(data) + -- inbound presence to the host + local origin, stanza = data.origin, data.stanza; + + local from_bare = jid_bare(stanza.attr.from); + local t = stanza.attr.type; + if t == "probe" then + core_route_stanza(hosts[module.host], st.presence({ from = module.host, to = from_bare, id = stanza.attr.id })); + elseif t == "subscribe" then + core_route_stanza(hosts[module.host], st.presence({ from = module.host, to = from_bare, id = stanza.attr.id, type = "subscribed" })); + core_route_stanza(hosts[module.host], st.presence({ from = module.host, to = from_bare, id = stanza.attr.id })); + end + return true; +end); module:hook("resource-unbind", function(event) local session, err = event.session, event.error; diff -r 46dfcc33ea9e -r 1edeb8fe7d14 plugins/mod_register.lua --- a/plugins/mod_register.lua Fri Feb 19 03:30:27 2010 +0000 +++ b/plugins/mod_register.lua Wed Mar 03 22:05:05 2010 +0000 @@ -141,7 +141,7 @@ username = nodeprep(table.concat(username)); password = table.concat(password); local host = module.host; - if not username then + if not username or username == "" then session.send(st.error_reply(stanza, "modify", "not-acceptable", "The requested username is invalid.")); elseif usermanager_user_exists(username, host) then session.send(st.error_reply(stanza, "cancel", "conflict", "The requested username already exists.")); diff -r 46dfcc33ea9e -r 1edeb8fe7d14 plugins/mod_saslauth.lua --- a/plugins/mod_saslauth.lua Fri Feb 19 03:30:27 2010 +0000 +++ b/plugins/mod_saslauth.lua Wed Mar 03 22:05:05 2010 +0000 @@ -38,13 +38,13 @@ local function build_reply(status, ret, err_msg) local reply = st.stanza(status, {xmlns = xmlns_sasl}); if status == "challenge" then - log("debug", "%s", ret or ""); + --log("debug", "CHALLENGE: %s", ret or ""); reply:text(base64.encode(ret or "")); elseif status == "failure" then reply:tag(ret):up(); if err_msg then reply:tag("text"):text(err_msg); end elseif status == "success" then - log("debug", "%s", ret or ""); + --log("debug", "SUCCESS: %s", ret or ""); reply:text(base64.encode(ret or "")); else module:log("error", "Unknown sasl status: %s", status); @@ -124,7 +124,7 @@ local text = stanza[1]; if text then text = base64.decode(text); - log("debug", "%s", text); + --log("debug", "AUTH: %s", text:gsub("[%z\001-\008\011\012\014-\031]", " ")); if not text then session.sasl_handler = nil; session.send(build_reply("failure", "incorrect-encoding")); diff -r 46dfcc33ea9e -r 1edeb8fe7d14 plugins/mod_selftests.lua --- a/plugins/mod_selftests.lua Fri Feb 19 03:30:27 2010 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,61 +0,0 @@ --- Prosody IM --- Copyright (C) 2008-2009 Matthew Wild --- Copyright (C) 2008-2009 Waqas Hussain --- --- This project is MIT/X11 licensed. Please see the --- COPYING file in the source package for more information. --- - -module.host = "*" -- Global module - -local st = require "util.stanza"; -local register_component = require "core.componentmanager".register_component; -local core_route_stanza = core_route_stanza; -local socket = require "socket"; -local ping_hosts = module:get_option("ping_hosts") or { "coversant.interop.xmpp.org", "djabberd.interop.xmpp.org", "djabberd-trunk.interop.xmpp.org", "ejabberd.interop.xmpp.org", "openfire.interop.xmpp.org" }; - -local open_pings = {}; - -local t_insert = table.insert; - -local log = require "util.logger".init("mod_selftests"); - -local tests_jid = "self_tests@getjabber.ath.cx"; -local host = "getjabber.ath.cx"; - -if not (tests_jid and host) then - for currhost in pairs(host) do - if currhost ~= "localhost" then - tests_jid, host = "self_tests@"..currhost, currhost; - end - end -end - -if tests_jid and host then - local bot = register_component(tests_jid, function(origin, stanza, ourhost) - local time = open_pings[stanza.attr.id]; - - if time then - log("info", "Ping reply from %s in %fs", tostring(stanza.attr.from), socket.gettime() - time); - else - log("info", "Unexpected reply: %s", stanza:pretty_print()); - end - end); - - - local our_origin = hosts[host]; - module:add_event_hook("server-started", - function () - local id = st.new_id(); - local ping_attr = { xmlns = 'urn:xmpp:ping' }; - local function send_ping(to) - log("info", "Sending ping to %s", to); - core_route_stanza(our_origin, st.iq{ to = to, from = tests_jid, id = id, type = "get" }:tag("ping", ping_attr)); - open_pings[id] = socket.gettime(); - end - - for _, host in ipairs(ping_hosts) do - send_ping(host); - end - end); -end diff -r 46dfcc33ea9e -r 1edeb8fe7d14 plugins/mod_tls.lua --- a/plugins/mod_tls.lua Fri Feb 19 03:30:27 2010 +0000 +++ b/plugins/mod_tls.lua Wed Mar 03 22:05:05 2010 +0000 @@ -14,9 +14,11 @@ local secure_auth_only = module:get_option("c2s_require_encryption") or module:get_option("require_encryption"); local secure_s2s_only = module:get_option("s2s_require_encryption"); +local host = hosts[module.host]; + module:add_handler("c2s_unauthed", "starttls", xmlns_starttls, function (session, stanza) - if session.conn.starttls then + if session.conn.starttls and host.ssl_ctx_in then session.send(st.stanza("proceed", { xmlns = xmlns_starttls })); session:reset_stream(); if session.host and hosts[session.host].ssl_ctx_in then @@ -26,14 +28,15 @@ session.log("info", "TLS negotiation started..."); session.secure = false; else - -- FIXME: What reply? session.log("warn", "Attempt to start TLS, but TLS is not available on this connection"); + (session.sends2s or session.send)(st.stanza("failure", { xmlns = xmlns_starttls })); + session:close(); end end); module:add_handler("s2sin_unauthed", "starttls", xmlns_starttls, function (session, stanza) - if session.conn.starttls then + if session.conn.starttls and host.ssl_ctx_in then session.sends2s(st.stanza("proceed", { xmlns = xmlns_starttls })); session:reset_stream(); if session.to_host and hosts[session.to_host].ssl_ctx_in then @@ -43,8 +46,9 @@ session.log("info", "TLS negotiation started for incoming s2s..."); session.secure = false; else - -- FIXME: What reply? session.log("warn", "Attempt to start TLS, but TLS is not available on this s2s connection"); + (session.sends2s or session.send)(st.stanza("failure", { xmlns = xmlns_starttls })); + session:close(); end end); @@ -66,7 +70,7 @@ function (data) local session, features = data.session, data.features; if session.to_host and session.conn.starttls then - features:tag("starttls", starttls_attr):up(); + features:tag("starttls", starttls_attr); if secure_s2s_only then features:tag("required"):up():up(); else diff -r 46dfcc33ea9e -r 1edeb8fe7d14 plugins/muc/muc.lib.lua --- a/plugins/muc/muc.lib.lua Fri Feb 19 03:30:27 2010 +0000 +++ b/plugins/muc/muc.lib.lua Wed Mar 03 22:05:05 2010 +0000 @@ -128,19 +128,21 @@ end end function room_mt:broadcast_message(stanza, historic) + local to = stanza.attr.to; for occupant, o_data in pairs(self._occupants) do for jid in pairs(o_data.sessions) do stanza.attr.to = jid; self:_route_stanza(stanza); end end + stanza.attr.to = to; if historic then -- add to history local history = self._data['history']; if not history then history = {}; self._data['history'] = history; end - -- stanza = st.clone(stanza); + stanza = st.clone(stanza); stanza:tag("delay", {xmlns = "urn:xmpp:delay", from = muc_domain, stamp = datetime.datetime()}):up(); -- XEP-0203 stanza:tag("x", {xmlns = "jabber:x:delay", from = muc_domain, stamp = datetime.legacy()}):up(); -- XEP-0091 (deprecated) - t_insert(history, st.clone(st.preserialize(stanza))); + t_insert(history, st.preserialize(stanza)); while #history > history_length do t_remove(history, 1) end end end @@ -461,6 +463,9 @@ if not item.attr.jid and item.attr.nick then -- COMPAT Workaround for Miranda sending 'nick' instead of 'jid' when changing affiliation local occupant = self._occupants[self.jid.."/"..item.attr.nick]; if occupant then item.attr.jid = occupant.jid; end + elseif not item.attr.nick and item.attr.jid then + local nick = self._jid_nick[item.attr.jid]; + if nick then item.attr.nick = select(3, jid_split(nick)); end end local reason = item.tags[1] and item.tags[1].name == "reason" and #item.tags[1] == 1 and item.tags[1][1]; if item.attr.affiliation and item.attr.jid and not item.attr.role then @@ -492,9 +497,14 @@ -- TODO allow admins and owners not in room? Provide read-only access to everyone who can see the participants anyway? if _rol == "none" then _rol = nil; end local reply = st.reply(stanza):query("http://jabber.org/protocol/muc#admin"); - for nick, occupant in pairs(self._occupants) do + for occupant_jid, occupant in pairs(self._occupants) do if occupant.role == _rol then - reply:tag("item", {nick = nick, role = _rol or "none", affiliation = occupant.affiliation or "none", jid = occupant.jid}):up(); + reply:tag("item", { + nick = select(3, jid_split(occupant_jid)), + role = _rol or "none", + affiliation = occupant.affiliation or "none", + jid = occupant.jid + }):up(); end end origin.send(reply); @@ -517,17 +527,26 @@ local from, to = stanza.attr.from, stanza.attr.to; local room = jid_bare(to); local current_nick = self._jid_nick[from]; - if not current_nick then -- not in room + local occupant = self._occupants[current_nick]; + if not occupant then -- not in room origin.send(st.error_reply(stanza, "cancel", "not-acceptable")); + elseif occupant.role == "visitor" then + origin.send(st.error_reply(stanza, "cancel", "forbidden")); else local from = stanza.attr.from; stanza.attr.from = current_nick; local subject = getText(stanza, {"subject"}); if subject then - self:set_subject(current_nick, subject); -- TODO use broadcast_message_stanza + if occupant.role == "moderator" then + self:set_subject(current_nick, subject); -- TODO use broadcast_message_stanza + else + stanza.attr.from = from; + origin.send(st.error_reply(stanza, "cancel", "forbidden")); + end else self:broadcast_message(stanza, true); end + stanza.attr.from = from; end elseif stanza.name == "message" and type == "error" and is_kickable_error(stanza) then local current_nick = self._jid_nick[stanza.attr.from]; @@ -651,21 +670,21 @@ local session = self._occupants[nick]; return session and session.role or nil; end -function room_mt:set_role(actor, nick, role, callback, reason) +function room_mt:set_role(actor, occupant_jid, role, callback, reason) if role == "none" then role = nil; end if role and role ~= "moderator" and role ~= "participant" and role ~= "visitor" then return nil, "modify", "not-acceptable"; end if self:get_affiliation(actor) ~= "owner" then return nil, "cancel", "not-allowed"; end - local occupant = self._occupants[nick]; + local occupant = self._occupants[occupant_jid]; if not occupant then return nil, "modify", "not-acceptable"; end if occupant.affiliation == "owner" or occupant.affiliation == "admin" then return nil, "cancel", "not-allowed"; end - local p = st.presence({from = nick}) + local p = st.presence({from = occupant_jid}) :tag("x", {xmlns = "http://jabber.org/protocol/muc#user"}) - :tag("item", {affiliation=occupant.affiliation or "none", nick=nick, role=role or "none"}) + :tag("item", {affiliation=occupant.affiliation or "none", nick=select(3, jid_split(occupant_jid)), role=role or "none"}) :tag("reason"):text(reason or ""):up() :up(); if not role then -- kick p.attr.type = "unavailable"; - self._occupants[nick] = nil; + self._occupants[occupant_jid] = nil; for jid in pairs(occupant.sessions) do -- remove for all sessions of the nick self._jid_nick[jid] = nil; end @@ -678,7 +697,7 @@ self:_route_stanza(p); end if callback then callback(); end - self:broadcast_except_nick(p, nick); + self:broadcast_except_nick(p, occupant_jid); return true; end diff -r 46dfcc33ea9e -r 1edeb8fe7d14 prosody --- a/prosody Fri Feb 19 03:30:27 2010 +0000 +++ b/prosody Wed Mar 03 22:05:05 2010 +0000 @@ -198,7 +198,7 @@ if type(port) ~= "number" then log("error", "Non-numeric "..option.."_ports: "..tostring(port)); else - cl.start(listener, { + local ok, err = cl.start(listener, { ssl = conntype ~= "tcp" and global_ssl_ctx, port = port, interface = (option and config.get("*", "core", option.."_interface")) @@ -206,6 +206,33 @@ or config.get("*", "core", "interface"), type = conntype }); + if not ok then + local friendly_message = err; + if err:match(" in use") then + if port == 5222 or port == 5223 or port == 5269 then + friendly_message = "check that Prosody or another XMPP server is " + .."not already running and using this port"; + elseif port == 80 or port == 81 then + friendly_message = "check that a HTTP server is not already using " + .."this port"; + elseif port == 5280 then + friendly_message = "check that Prosody or a BOSH connection manager " + .."is not already running"; + else + friendly_message = "this port is in use by another application"; + end + elseif err:match("permission") then + friendly_message = "Prosody does not have sufficient privileges to use this port"; + elseif err == "no ssl context" then + if not config.get("*", "core", "ssl") then + friendly_message = "there is no 'ssl' config under Host \"*\" which is " + .."require for legacy SSL ports"; + else + friendly_message = "initializing SSL support failed, see previous log entries"; + end + end + log("error", "Failed to open server port %d, %s", port, friendly_message); + end end end end @@ -280,15 +307,18 @@ -- start listening on sockets prosody.net_activate_ports("c2s", "xmppclient", {5222}); prosody.net_activate_ports("s2s", "xmppserver", {5269}); - prosody.net_activate_ports("component", "xmppcomponent", {}, "tcp"); + prosody.net_activate_ports("component", "xmppcomponent", {5347}, "tcp"); prosody.net_activate_ports("legacy_ssl", "xmppclient", {}, "ssl"); prosody.start_time = os.time(); end function init_global_protection() - -- Catch global accesses -- - local locked_globals_mt = { __index = function (t, k) error("Attempt to read a non-existent global '"..k.."'", 2); end, __newindex = function (t, k, v) error("Attempt to set a global: "..tostring(k).." = "..tostring(v), 2); end } + -- Catch global accesses + local locked_globals_mt = { + __index = function (t, k) log("warn", "%s", debug.traceback("Attempt to read a non-existent global '"..tostring(k).."'", 2)); end; + __newindex = function (t, k, v) error("Attempt to set a global: "..tostring(k).." = "..tostring(v), 2); end; + }; function prosody.unlock_globals() setmetatable(_G, nil); diff -r 46dfcc33ea9e -r 1edeb8fe7d14 prosody.cfg.lua.dist --- a/prosody.cfg.lua.dist Fri Feb 19 03:30:27 2010 +0000 +++ b/prosody.cfg.lua.dist Wed Mar 03 22:05:05 2010 +0000 @@ -63,7 +63,7 @@ -- Other specific functionality --"posix"; -- POSIX functionality, sends server to background, enables syslog, etc. - --"console"; -- telnet to port 5582 (needs console_enabled = true) + --"console"; -- Opens admin telnet interface on localhost port 5582 --"bosh"; -- Enable BOSH clients, aka "Jabber over HTTP" --"httpserver"; -- Serve static files from a directory over HTTP }; diff -r 46dfcc33ea9e -r 1edeb8fe7d14 tests/test_util_jid.lua --- a/tests/test_util_jid.lua Fri Feb 19 03:30:27 2010 +0000 +++ b/tests/test_util_jid.lua Wed Mar 03 22:05:05 2010 +0000 @@ -6,6 +6,16 @@ -- COPYING file in the source package for more information. -- +function join(join) + assert_equal(join("a", "b", "c"), "a@b/c", "builds full JID"); + assert_equal(join("a", "b", nil), "a@b", "builds bare JID"); + assert_equal(join(nil, "b", "c"), "b/c", "builds full host JID"); + assert_equal(join(nil, "b", nil), "b", "builds bare host JID"); + assert_equal(join(nil, nil, nil), nil, "invalid JID is nil"); + assert_equal(join("a", nil, nil), nil, "invalid JID is nil"); + assert_equal(join(nil, nil, "c"), nil, "invalid JID is nil"); + assert_equal(join("a", nil, "c"), nil, "invalid JID is nil"); +end function split(split) @@ -43,3 +53,4 @@ assert_equal(bare("user@@host/resource"), nil, "invalid JID is nil"); assert_equal(bare("user@host/"), nil, "invalid JID is nil"); end + diff -r 46dfcc33ea9e -r 1edeb8fe7d14 tools/ejabberd2prosody.lua --- a/tools/ejabberd2prosody.lua Fri Feb 19 03:30:27 2010 +0000 +++ b/tools/ejabberd2prosody.lua Wed Mar 03 22:05:05 2010 +0000 @@ -49,7 +49,7 @@ end function password(node, host, password) local ret, err = dm.store(node, host, "accounts", {password = password}); - print("["..(err or "success").."] accounts: "..node.."@"..host.." = "..password); + print("["..(err or "success").."] accounts: "..node.."@"..host); end function roster(node, host, jid, item) local roster = dm.load(node, host, "roster") or {}; diff -r 46dfcc33ea9e -r 1edeb8fe7d14 tools/ejabberdsql2prosody.lua --- a/tools/ejabberdsql2prosody.lua Fri Feb 19 03:30:27 2010 +0000 +++ b/tools/ejabberdsql2prosody.lua Wed Mar 03 22:05:05 2010 +0000 @@ -254,7 +254,7 @@ for i, row in ipairs(t["users"] or NULL) do local node, password = row.username, row.password; local ret, err = dm.store(node, host, "accounts", {password = password}); - print("["..(err or "success").."] accounts: "..node.."@"..host.." = "..password); + print("["..(err or "success").."] accounts: "..node.."@"..host); end function roster(node, host, jid, item) diff -r 46dfcc33ea9e -r 1edeb8fe7d14 util-src/encodings.c --- a/util-src/encodings.c Fri Feb 19 03:30:27 2010 +0000 +++ b/util-src/encodings.c Wed Mar 03 22:05:05 2010 +0000 @@ -174,7 +174,7 @@ size_t len; const char *s = luaL_checklstring(L, 1, &len); char* output = NULL; - int ret = idna_to_ascii_8z(s, &output, 0); + int ret = idna_to_ascii_8z(s, &output, IDNA_USE_STD3_ASCII_RULES); if (ret == IDNA_SUCCESS) { lua_pushstring(L, output); idn_free(output); diff -r 46dfcc33ea9e -r 1edeb8fe7d14 util-src/pposix.c --- a/util-src/pposix.c Fri Feb 19 03:30:27 2010 +0000 +++ b/util-src/pposix.c Wed Mar 03 22:05:05 2010 +0000 @@ -463,9 +463,10 @@ return 3; } -void lc_abort(lua_State* L) +int lc_abort(lua_State* L) { abort(); + return 0; } /* Register functions */ diff -r 46dfcc33ea9e -r 1edeb8fe7d14 util/dataforms.lua --- a/util/dataforms.lua Fri Feb 19 03:30:27 2010 +0000 +++ b/util/dataforms.lua Wed Mar 03 22:05:05 2010 +0000 @@ -23,8 +23,8 @@ return setmetatable(layout, form_mt); end -function form_t.form(layout, data) - local form = st.stanza("x", { xmlns = xmlns_forms, type = "form" }); +function form_t.form(layout, data, formtype) + local form = st.stanza("x", { xmlns = xmlns_forms, type = formtype or "form" }); if layout.title then form:tag("title"):text(layout.title):up(); end diff -r 46dfcc33ea9e -r 1edeb8fe7d14 util/dependencies.lua --- a/util/dependencies.lua Fri Feb 19 03:30:27 2010 +0000 +++ b/util/dependencies.lua Wed Mar 03 22:05:05 2010 +0000 @@ -17,8 +17,12 @@ print("Prosody was unable to find "..tostring(name)); print("This package can be obtained in the following ways:"); print(""); - for k,v in pairs(sources) do - print("", k, v); + local longest_platform = 0; + for platform in pairs(sources) do + longest_platform = math.max(longest_platform, #platform); + end + for platform, source in pairs(sources) do + print("", platform..":"..(" "):rep(4+longest_platform-#platform)..source); end print(""); print(msg or (name.." is required for Prosody to run, so we will now exit.")); diff -r 46dfcc33ea9e -r 1edeb8fe7d14 util/jid.lua --- a/util/jid.lua Fri Feb 19 03:30:27 2010 +0000 +++ b/util/jid.lua Wed Mar 03 22:05:05 2010 +0000 @@ -65,4 +65,17 @@ return host; end +function join(node, host, resource) + if node and host and resource then + return node.."@"..host.."/"..resource; + elseif node and host then + return node.."@"..host; + elseif host and resource then + return host.."/"..resource; + elseif host then + return host; + end + return nil; -- Invalid JID +end + return _M; diff -r 46dfcc33ea9e -r 1edeb8fe7d14 util/sasl.lua --- a/util/sasl.lua Fri Feb 19 03:30:27 2010 +0000 +++ b/util/sasl.lua Wed Mar 03 22:05:05 2010 +0000 @@ -38,7 +38,7 @@ function object.feed(self, message) if message == "" or message == nil then return "failure", "malformed-request" end local response = message - local authorization = s_match(response, "([^%z]+)") + local authorization = s_match(response, "([^%z]*)") local authentication = s_match(response, "%z([^%z]+)%z") local password = s_match(response, "%z[^%z]+%z([^%z]+)") diff -r 46dfcc33ea9e -r 1edeb8fe7d14 util/stanza.lua --- a/util/stanza.lua Fri Feb 19 03:30:27 2010 +0000 +++ b/util/stanza.lua Wed Mar 03 22:05:05 2010 +0000 @@ -93,6 +93,17 @@ return self; end +function stanza_mt:get_child(name, xmlns) + for _, child in ipairs(self.tags) do + if (not name or child.name == name) + and ((not xmlns and self.attr.xmlns == child.attr.xmlns) + or child.attr.xmlns == xmlns) then + + return child; + end + end +end + function stanza_mt:child_with_name(name) for _, child in ipairs(self.tags) do if child.name == name then return child; end @@ -280,13 +291,16 @@ return stanza(orig.name, orig.attr and { to = orig.attr.from, from = orig.attr.to, id = orig.attr.id, type = ((orig.name == "iq" and "result") or orig.attr.type) }); end -function error_reply(orig, type, condition, message) - local t = reply(orig); - t.attr.type = "error"; - t:tag("error", {type = type}) - :tag(condition, {xmlns = "urn:ietf:params:xml:ns:xmpp-stanzas"}):up(); - if (message) then t:tag("text"):text(message):up(); end - return t; -- stanza ready for adding app-specific errors +do + local xmpp_stanzas_attr = { xmlns = xmlns_stanzas }; + function error_reply(orig, type, condition, message) + local t = reply(orig); + t.attr.type = "error"; + t:tag("error", {type = type}) --COMPAT: Some day xmlns:stanzas goes here + :tag(condition, xmpp_stanzas_attr):up(); + if (message) then t:tag("text", xmpp_stanzas_attr):text(message):up(); end + return t; -- stanza ready for adding app-specific errors + end end function presence(attr)