# HG changeset patch # User Matthew Wild # Date 1452604476 0 # Node ID 82ad158714e54397f02c175252f648deaf37833b # Parent 6042c938e3691dc27af5b4ed60b44917f6d07287# Parent b4ce2e524ed8dad11c35670407a773d0795d3e7b Merge with Zash diff -r 6042c938e369 -r 82ad158714e5 init.lua --- a/init.lua Sat Jan 09 11:03:30 2016 +0000 +++ b/init.lua Tue Jan 12 13:14:36 2016 +0000 @@ -25,7 +25,7 @@ for i=1,select("#", ...) do local ok, err = pcall(require, "verse."..select(i,...)); if not ok then - error("Verse connection module not found: verse."..select(i,...).."\n"..err); + error("Verse connection module not found: verse."..select(i,...)..err); end end return verse; @@ -119,6 +119,7 @@ -- Create and initiate connection local conn = socket.tcp() conn:settimeout(0); + conn:setoption("keepalive", true); local success, err = conn:connect(connect_host, connect_port); if not success and err ~= "timeout" then diff -r 6042c938e369 -r 82ad158714e5 libs/adhoc.lib.lua --- a/libs/adhoc.lib.lua Sat Jan 09 11:03:30 2016 +0000 +++ b/libs/adhoc.lib.lua Tue Jan 12 13:14:36 2016 +0000 @@ -12,7 +12,7 @@ local _M = {}; -function _cmdtag(desc, status, sessionid, action) +local function _cmdtag(desc, status, sessionid, action) local cmd = st.stanza("command", { xmlns = xmlns_cmd, node = desc.node, status = status }); if sessionid then cmd.attr.sessionid = sessionid; end if action then cmd.attr.action = action; end @@ -35,6 +35,7 @@ local data, state = command:handler(dataIn, states[sessionid]); states[sessionid] = state; local stanza = st.reply(stanza); + local cmdtag; if data.status == "completed" then states[sessionid] = nil; cmdtag = command:cmdtag("completed", sessionid); diff -r 6042c938e369 -r 82ad158714e5 libs/encodings.lua --- a/libs/encodings.lua Sat Jan 09 11:03:30 2016 +0000 +++ b/libs/encodings.lua Tue Jan 12 13:14:36 2016 +0000 @@ -7,6 +7,6 @@ module "encodings" stringprep = {}; -base64 = { encode = mime.b64, decode = not_impl }; --mime.unb64 is buggy with \0 +base64 = { encode = mime.b64, decode = mime.unb64 }; return _M; diff -r 6042c938e369 -r 82ad158714e5 libs/hashes.lua --- a/libs/hashes.lua Sat Jan 09 11:03:30 2016 +0000 +++ b/libs/hashes.lua Tue Jan 12 13:14:36 2016 +0000 @@ -1,3 +1,58 @@ -local sha1 = require "util.sha1"; +local have_luacrypto, crypto = pcall(require, "crypto"); + +if have_luacrypto then + local hashes = {}; + + local digest = crypto.digest; + local function gethash(algo) + return function (string, hex) + return digest(algo, string, not hex); + end + end + + local hmac = crypto.hmac.digest; + local function gethmac(algo) + return function (key, message, hex) + return hmac(algo, message, key, not hex); + end + end + + local hash_algos = { "md5", "sha1", "sha256", "sha512" }; + + for _, hash_algo in ipairs(hash_algos) do + hashes[hash_algo] = gethash(hash_algo); + hashes["hmac_"..hash_algo] = gethmac(hash_algo); + end -return { sha1 = sha1.sha1 }; + return hashes; +else + local sha1 = require"util.sha1".sha1; + local bxor = require"bit".bxor; + + local s_rep = string.rep; + local s_char = string.char; + local s_byte = string.byte; + local t_concat = table.concat; + + local function hmac_sha1(key, message, hexres) + if #key > 64 then + key = sha1(key); + elseif #key < 64 then + key = key .. s_rep("\0", 64 - #key); + end + local o_key_pad, i_key_pad = {}, {} + for i = 1, 64 do + local b = s_byte(key, i) + o_key_pad[i] = s_char(bxor(b, 0x5c)); + i_key_pad[i] = s_char(bxor(b, 0x36)); + end + o_key_pad = t_concat(o_key_pad); + i_key_pad = t_concat(i_key_pad); + return sha1(o_key_pad .. sha1(i_key_pad .. message), hexres); + end + + return { + sha1 = sha1; + hmac_sha1 = hmac_sha1; + }; +end diff -r 6042c938e369 -r 82ad158714e5 plugins/adhoc.lua --- a/plugins/adhoc.lua Sat Jan 09 11:03:30 2016 +0000 +++ b/plugins/adhoc.lua Tue Jan 12 13:14:36 2016 +0000 @@ -25,15 +25,15 @@ return callback(command_list); end); end - + function stream:execute_command(jid, command, callback) local cmd = setmetatable({ stream = stream, jid = jid, - command = command, callback = callback + command = command, callback = callback }, command_mt); return cmd:execute(); end - + -- ACL checker for commands we provide local function has_affiliation(jid, aff) if not(aff) or aff == "user" then return true; end @@ -42,31 +42,31 @@ end -- TODO: Support 'roster', etc. end - + function stream:add_adhoc_command(name, node, handler, permission) commands[node] = adhoc.new(name, node, handler, permission); stream:add_disco_item({ jid = stream.jid, node = node, name = name }, xmlns_commands); return commands[node]; end - + local function handle_command(stanza) local command_tag = stanza.tags[1]; local node = command_tag.attr.node; - + local handler = commands[node]; if not handler then return; end - + if not has_affiliation(stanza.attr.from, handler.permission) then stream:send(verse.error_reply(stanza, "auth", "forbidden", "You don't have permission to execute this command"):up() :add_child(handler:cmdtag("canceled") :tag("note", {type="error"}):text("You don't have permission to execute this command"))); return true end - + -- User has permission now execute the command return adhoc.handle_cmd(handler, { send = function (d) return stream:send(d) end }, stanza); end - + stream:hook("iq/"..xmlns_commands, function (stanza) local type = stanza.attr.type; local name = stanza.tags[1].name; @@ -106,9 +106,9 @@ node = self.command, sessionid = self.sessionid }); - + if form then iq:add_child(form); end - + self.stream:send_iq(iq, function (result) self:_process_response(result); end); diff -r 6042c938e369 -r 82ad158714e5 plugins/archive.lua --- a/plugins/archive.lua Sat Jan 09 11:03:30 2016 +0000 +++ b/plugins/archive.lua Tue Jan 12 13:14:36 2016 +0000 @@ -27,7 +27,7 @@ local query_st = st.iq{ type="set", to = where } :tag("query", { xmlns = xmlns_mam, queryid = queryid }); - + local qstart, qend = tonumber(query_params["start"]), tonumber(query_params["end"]); query_params["start"] = qstart and datetime(qstart); query_params["end"] = qend and datetime(qend); diff -r 6042c938e369 -r 82ad158714e5 plugins/compression.lua --- a/plugins/compression.lua Sat Jan 09 11:03:30 2016 +0000 +++ b/plugins/compression.lua Tue Jan 12 13:14:36 2016 +0000 @@ -1,6 +1,6 @@ -- Copyright (C) 2009-2010 Matthew Wild -- Copyright (C) 2009-2010 Tobias Markmann --- +-- -- This project is MIT/X11 licensed. Please see the -- COPYING file in the source package for more information. -- @@ -53,7 +53,7 @@ return; end session.conn:write(compressed); - end; + end; end -- setup decompression for a stream @@ -101,16 +101,16 @@ -- create deflate and inflate streams local deflate_stream = get_deflate_stream(stream); if not deflate_stream then return end - + local inflate_stream = get_inflate_stream(stream); if not inflate_stream then return end - + -- setup compression for stream.w setup_compression(stream, deflate_stream); - + -- setup decompression for stream.data setup_decompression(stream, inflate_stream); - + stream.compressed = true; stream:reopen(); elseif stanza.name == "failure" then diff -r 6042c938e369 -r 82ad158714e5 plugins/disco.lua --- a/plugins/disco.lua Sat Jan 09 11:03:30 2016 +0000 +++ b/plugins/disco.lua Tue Jan 12 13:14:36 2016 +0000 @@ -1,14 +1,14 @@ -- Verse XMPP Library -- Copyright (C) 2010 Hubert Chathi -- Copyright (C) 2010 Matthew Wild --- +-- -- This project is MIT/X11 licensed. Please see the -- COPYING file in the source package for more information. -- local verse = require "verse"; local b64 = require("mime").b64; -local sha1 = require("util.sha1").sha1; +local sha1 = require("util.hashes").sha1; local xmlns_caps = "http://jabber.org/protocol/caps"; local xmlns_disco = "http://jabber.org/protocol/disco"; @@ -118,7 +118,7 @@ }) end }) - + function stream:set_identity(identity, node) self.disco.info[node or false].identities = { identity }; stream:resend_presence(); @@ -135,7 +135,7 @@ self.disco.info[node or false].features[feature] = true; stream:resend_presence(); end - + function stream:remove_disco_feature(feature, node) local feature = feature.var or feature; self.disco.info[node or false].features[feature] = nil; @@ -181,13 +181,13 @@ end return cached_disco.features[feature] or false; end - + function stream:get_local_services(category, type) local host_disco = self.disco.cache[self.host]; if not(host_disco) or not(host_disco.items) then return nil, "no-cache"; end - + local results = {}; for _, service in ipairs(host_disco.items) do if self:jid_has_identity(service.jid, category, type) then @@ -196,7 +196,7 @@ end return results; end - + function stream:disco_local_services(callback) self:disco_items(self.host, nil, function (items) if not items then @@ -209,7 +209,7 @@ return callback(items); end end - + for _, item in ipairs(items) do if item.jid then n_items = n_items + 1; @@ -221,7 +221,7 @@ end end); end - + function stream:disco_info(jid, node, callback) local disco_request = verse.iq({ to = jid, type = "get" }) :tag("query", { xmlns = xmlns_disco_info, node = node }); @@ -229,9 +229,9 @@ if result.attr.type == "error" then return callback(nil, result:get_error()); end - + local identities, features = {}, {}; - + for tag in result:get_child("query", xmlns_disco_info):childtags() do if tag.name == "identity" then identities[tag.attr.category.."/"..tag.attr.type] = tag.attr.name or true; @@ -239,7 +239,7 @@ features[tag.attr.var] = true; end end - + if not self.disco.cache[jid] then self.disco.cache[jid] = { nodes = {} }; @@ -258,7 +258,7 @@ return callback(self.disco.cache[jid]); end); end - + function stream:disco_items(jid, node, callback) local disco_request = verse.iq({ to = jid, type = "get" }) :tag("query", { xmlns = xmlns_disco_items, node = node }); @@ -276,11 +276,11 @@ }); end end - + if not self.disco.cache[jid] then self.disco.cache[jid] = { nodes = {} }; end - + if node then if not self.disco.cache[jid].nodes[node] then self.disco.cache[jid].nodes[node] = { nodes = {} }; @@ -292,7 +292,7 @@ return callback(disco_items); end); end - + stream:hook("iq/"..xmlns_disco_info, function (stanza) local query = stanza.tags[1]; if stanza.attr.type == 'get' and query.name == "query" then @@ -337,7 +337,7 @@ return true end end); - + local initial_disco_started; stream:hook("ready", function () if initial_disco_started then return; end @@ -358,7 +358,7 @@ end); return true; end, 50); - + stream:hook("presence-out", function (presence) if not presence:get_child("c", xmlns_caps) then presence:reset():add_child(stream:caps()):reset(); diff -r 6042c938e369 -r 82ad158714e5 plugins/groupchat.lua --- a/plugins/groupchat.lua Sat Jan 09 11:03:30 2016 +0000 +++ b/plugins/groupchat.lua Tue Jan 12 13:14:36 2016 +0000 @@ -11,7 +11,7 @@ function verse.plugins.groupchat(stream) stream:add_plugin("presence") stream.rooms = {}; - + stream:hook("stanza", function (stanza) local room_jid = jid.bare(stanza.attr.from); if not room_jid then return end @@ -37,7 +37,7 @@ return ret or (stanza.name == "message") or nil; end end, 500); - + function stream:join_room(jid, nick, opts) if not nick then return false, "no nickname supplied" diff -r 6042c938e369 -r 82ad158714e5 plugins/jingle.lua --- a/plugins/jingle.lua Sat Jan 09 11:03:30 2016 +0000 +++ b/plugins/jingle.lua Tue Jan 12 13:14:36 2016 +0000 @@ -1,5 +1,4 @@ local verse = require "verse"; -local sha1 = require "util.sha1".sha1; local timer = require "util.timer"; local uuid_generate = require "util.uuid".generate; @@ -16,7 +15,7 @@ stream:hook("ready", function () stream:add_disco_feature(xmlns_jingle); end, 10); - + function stream:jingle(to) return verse.eventable(setmetatable(base or { role = "initiator"; @@ -25,20 +24,20 @@ stream = stream; }, jingle_mt)); end - + function stream:register_jingle_transport(transport) -- transport is a function that receives a -- element, and returns a connection -- We wait for 'connected' on that connection, -- and use :send() and 'incoming-raw'. end - + function stream:register_jingle_content_type(content) -- Call content() for every 'incoming-raw'? -- I think content() returns the object we return -- on jingle:accept() end - + local function handle_incoming_jingle(stanza) local jingle_tag = stanza:get_child("jingle", xmlns_jingle); local sid = jingle_tag.attr.sid; @@ -57,21 +56,21 @@ stream:send(reply); return; end - + -- Ok, session-initiate, new session - + -- Create new Jingle object local sid = jingle_tag.attr.sid; - + local jingle = verse.eventable{ role = "receiver"; peer = stanza.attr.from; sid = sid; stream = stream; }; - + setmetatable(jingle, jingle_mt); - + local content_tag; local content, transport; for tag in jingle_tag:childtags() do @@ -84,10 +83,10 @@ content = desc_handler; end end - + local transport_tag = tag:child_with_name("transport"); local transport_xmlns = transport_tag.attr.xmlns; - + transport = stream:event("jingle/transport/"..transport_xmlns, jingle, transport_tag); if content and transport then content_tag = tag; @@ -100,23 +99,23 @@ stream:send(verse.error_reply(stanza, "cancel", "feature-not-implemented", "The specified content is not supported")); return true; end - + if not transport then -- FIXME: Refuse session, no transport stream:send(verse.error_reply(stanza, "cancel", "feature-not-implemented", "The specified transport is not supported")); return true; end - + stream:send(verse.reply(stanza)); - + jingle.content_tag = content_tag; jingle.creator, jingle.name = content_tag.attr.creator, content_tag.attr.name; jingle.content, jingle.transport = content, transport; - + function jingle:decline() -- FIXME: Decline session end - + stream:hook("jingle/"..sid, function (stanza) if stanza.attr.from ~= jingle.peer then return false; @@ -124,11 +123,11 @@ local jingle_tag = stanza:get_child("jingle", xmlns_jingle); return jingle:handle_command(jingle_tag); end); - + stream:event("jingle", jingle); return true; end - + function jingle_mt:handle_command(jingle_tag) local action = jingle_tag.attr.action; stream:debug("Handling Jingle command: %s", action); @@ -166,7 +165,7 @@ self.stream:send_iq(stanza, callback); end end - + function jingle_mt:accept(options) local accept_stanza = verse.iq({ to = self.peer, type = "set" }) :tag("jingle", { @@ -176,13 +175,13 @@ responder = stream.jid, }) :tag("content", { creator = self.creator, name = self.name }); - + local content_accept_tag = self.content:generate_accept(self.content_tag:child_with_name("description"), options); accept_stanza:add_child(content_accept_tag); - + local transport_accept_tag = self.transport:generate_accept(self.content_tag:child_with_name("transport"), options); accept_stanza:add_child(transport_accept_tag); - + local jingle = self; stream:send_iq(accept_stanza, function (result) if result.attr.type == "error" then @@ -197,7 +196,7 @@ end); end); end - + stream:hook("iq/"..xmlns_jingle, handle_incoming_jingle); return true; @@ -207,26 +206,26 @@ local session_initiate = verse.iq({ to = self.peer, type = "set" }) :tag("jingle", { xmlns = xmlns_jingle, action = "session-initiate", initiator = self.stream.jid, sid = self.sid }); - + -- Content tag session_initiate:tag("content", { creator = self.role, name = name }); - + -- Need description element from someone who can turn 'content' into XML local description = self.stream:event("jingle/describe/"..name, content); - + if not description then return false, "Unknown content type"; end - + session_initiate:add_child(description); - + -- FIXME: Sort transports by 1) recipient caps 2) priority (SOCKS vs IBB, etc.) -- Fixed to s5b in the meantime local transport = self.stream:event("jingle/transport/".."urn:xmpp:jingle:transports:s5b:1", self); self.transport = transport; - + session_initiate:add_child(transport:generate_initiate()); - + self.stream:debug("Hooking %s", "jingle/"..self.sid); self.stream:hook("jingle/"..self.sid, function (stanza) if stanza.attr.from ~= self.peer then @@ -235,7 +234,7 @@ local jingle_tag = stanza:get_child("jingle", xmlns_jingle); return self:handle_command(jingle_tag) end); - + self.stream:send_iq(session_initiate, function (result) if result.attr.type == "error" then self.state = "terminated"; diff -r 6042c938e369 -r 82ad158714e5 plugins/jingle_ft.lua --- a/plugins/jingle_ft.lua Sat Jan 09 11:03:30 2016 +0000 +++ b/plugins/jingle_ft.lua Tue Jan 12 13:14:36 2016 +0000 @@ -3,16 +3,15 @@ local dirsep = package.config:sub(1,1); -local xmlns_jingle_ft = "urn:xmpp:jingle:apps:file-transfer:1"; -local xmlns_si_file_transfer = "http://jabber.org/protocol/si/profile/file-transfer"; +local xmlns_jingle_ft = "urn:xmpp:jingle:apps:file-transfer:4"; function verse.plugins.jingle_ft(stream) stream:hook("ready", function () stream:add_disco_feature(xmlns_jingle_ft); end, 10); - + local ft_content = { type = "file" }; - + function ft_content:generate_accept(description, options) if options and options.save_file then self.jingle:hook("connected", function () @@ -20,21 +19,23 @@ self.jingle:set_sink(sink); end); end - + return description; end - + local ft_mt = { __index = ft_content }; stream:hook("jingle/content/"..xmlns_jingle_ft, function (jingle, description_tag) - local file_tag = description_tag:get_child("offer"):get_child("file", xmlns_si_file_transfer); + local file_tag = description_tag:get_child("file"); local file = { - name = file_tag.attr.name; - size = tonumber(file_tag.attr.size); + name = file_tag:get_child_text("name"); + size = tonumber(file_tag:get_child_text("size")); + desc = file_tag:get_child_text("desc"); + date = file_tag:get_child_text("date"); }; - + return setmetatable({ jingle = jingle, file = file }, ft_mt); end); - + stream:hook("jingle/describe/file", function (file_info) -- Return local date; @@ -42,25 +43,23 @@ date = os.date("!%Y-%m-%dT%H:%M:%SZ", file_info.timestamp); end return verse.stanza("description", { xmlns = xmlns_jingle_ft }) - :tag("offer") - :tag("file", { xmlns = xmlns_si_file_transfer, - name = file_info.filename, -- Mandatory - size = file_info.size, -- Mandatory - date = date, - hash = file_info.hash, - }) - :tag("desc"):text(file_info.description or ""); + :tag("file") + :tag("name"):text(file_info.filename):up() + :tag("size"):text(tostring(file_info.size)):up() + :tag("date"):text(date):up() + :tag("desc"):text(file_info.description):up() + :up(); end); function stream:send_file(to, filename) local file, err = io.open(filename); if not file then return file, err; end - + local file_size = file:seek("end", 0); file:seek("set", 0); - + local source = ltn12.source.file(file); - + local jingle = self:jingle(to); jingle:offer("file", { filename = filename:match("[^"..dirsep.."]+$"); diff -r 6042c938e369 -r 82ad158714e5 plugins/jingle_s5b.lua --- a/plugins/jingle_s5b.lua Sat Jan 09 11:03:30 2016 +0000 +++ b/plugins/jingle_s5b.lua Tue Jan 12 13:14:36 2016 +0000 @@ -2,7 +2,7 @@ local xmlns_s5b = "urn:xmpp:jingle:transports:s5b:1"; local xmlns_bytestreams = "http://jabber.org/protocol/bytestreams"; -local sha1 = require "util.sha1".sha1; +local sha1 = require "util.hashes".sha1; local uuid_generate = require "util.uuid".generate; local function negotiate_socks5(conn, hash) @@ -12,7 +12,7 @@ end local function receive_connection_response(data) conn:unhook("incoming-raw", receive_connection_response); - + if data:sub(1, 2) ~= "\005\000" then return conn:event("error", "connection-failure"); end @@ -47,7 +47,7 @@ --Attempt to connect to the next host local function attempt_next_streamhost(event) if event then - return callback(nil, event.reason); + return callback(nil, event.reason); end -- First connect, or the last connect failed if conn.current_host < #conn.streamhosts then @@ -58,7 +58,7 @@ conn.streamhosts[conn.current_host].port ); if not ok then - conn:debug("Error connecting to proxy (%s:%s): %s", + conn:debug("Error connecting to proxy (%s:%s): %s", conn.streamhosts[conn.current_host].host, conn.streamhosts[conn.current_host].port, err @@ -90,7 +90,7 @@ end, 10); local s5b = {}; - + function s5b:generate_initiate() self.s5b_sid = uuid_generate(); local transport = verse.stanza("transport", { xmlns = xmlns_s5b, @@ -104,13 +104,13 @@ stream:debug("Have %d proxies", p) return transport; end - + function s5b:generate_accept(initiate_transport) local candidates = {}; self.s5b_peer_candidates = candidates; self.s5b_mode = initiate_transport.attr.mode or "tcp"; self.s5b_sid = initiate_transport.attr.sid or self.jingle.sid; - + -- Import the list of candidates the initiator offered us for candidate in initiate_transport:childtags() do --if candidate.attr.jid == "asterix4@jabber.lagaule.org/Gajim" @@ -125,21 +125,21 @@ }; --end end - + -- Import our own candidates -- TODO ^ local transport = verse.stanza("transport", { xmlns = xmlns_s5b }); return transport; end - + function s5b:connect(callback) stream:warn("Connecting!"); - + local streamhost_array = {}; for cid, streamhost in pairs(self.s5b_peer_candidates or {}) do streamhost_array[#streamhost_array+1] = streamhost; end - + if #streamhost_array > 0 then self.connecting_peer_candidates = true; local function onconnect(streamhost, conn) @@ -156,7 +156,7 @@ self.onconnect_callback = callback; end end - + function s5b:info_received(jingle_tag) stream:warn("Info received"); local content_tag = jingle_tag:child_with_name("content"); @@ -171,7 +171,7 @@ self.jingle.stream:send_iq(verse.iq({ to = streamhost.jid, type = "set" }) :tag("query", { xmlns = xmlns_bytestreams, sid = self.s5b_sid }) :tag("activate"):text(self.jingle.peer), function (result) - + if result.attr.type == "result" then self.jingle:send_command("transport-info", verse.stanza("content", content_tag.attr) :tag("transport", { xmlns = xmlns_s5b, sid = self.s5b_sid }) @@ -184,7 +184,7 @@ end); end end - + -- FIXME: Another assumption that cid==jid, and that it was our candidate self.jingle.stream:debug("CID: %s", self.jingle.stream.proxy65.available_streamhosts[candidate_used.attr.cid]); local streamhost_array = { @@ -198,13 +198,13 @@ self.onconnect_callback(self.conn); end end - + function s5b:disconnect() if self.conn then self.conn:close(); end end - + function s5b:handle_accepted(jingle_tag) end diff -r 6042c938e369 -r 82ad158714e5 plugins/legacy.lua --- a/plugins/legacy.lua Sat Jan 09 11:03:30 2016 +0000 +++ b/plugins/legacy.lua Tue Jan 12 13:14:36 2016 +0000 @@ -4,7 +4,7 @@ local xmlns_auth = "jabber:iq:auth"; function verse.plugins.legacy(stream) - function handle_auth_form(result) + local function handle_auth_form(result) local query = result:get_child("query", xmlns_auth); if result.attr.type ~= "result" or not query then local type, cond, text = result:get_error(); @@ -51,14 +51,13 @@ end end); end - - function handle_opened(attr) + + local function handle_opened(attr) if not attr.version then stream:send_iq(verse.iq({type="get"}) :tag("query", { xmlns = "jabber:iq:auth" }) :tag("username"):text(stream.username), handle_auth_form); - end end stream:hook("opened", handle_opened); diff -r 6042c938e369 -r 82ad158714e5 plugins/pep.lua --- a/plugins/pep.lua Sat Jan 09 11:03:30 2016 +0000 +++ b/plugins/pep.lua Tue Jan 12 13:14:36 2016 +0000 @@ -7,11 +7,11 @@ stream:add_plugin("disco"); stream:add_plugin("pubsub"); stream.pep = {}; - + stream:hook("pubsub/event", function(event) return stream:event("pep/"..event.node, { from = event.from, item = event.item.tags[1] } ); end); - + function stream:hook_pep(node, callback, priority) local handlers = stream.events._handlers["pep/"..node]; if not(handlers) or #handlers == 0 then @@ -19,7 +19,7 @@ end stream:hook("pep/"..node, callback, priority); end - + function stream:unhook_pep(node, callback) stream:unhook("pep/"..node, callback); local handlers = stream.events._handlers["pep/"..node]; @@ -27,7 +27,7 @@ stream:remove_disco_feature(node.."+notify"); end end - + function stream:publish_pep(item, node) return stream.pubsub:service(nil):node(node or item.attr.xmlns):publish(nil, nil, item) end diff -r 6042c938e369 -r 82ad158714e5 plugins/ping.lua --- a/plugins/ping.lua Sat Jan 09 11:03:30 2016 +0000 +++ b/plugins/ping.lua Tue Jan 12 13:14:36 2016 +0000 @@ -1,11 +1,12 @@ local verse = require "verse"; +local gettime = require"socket".gettime; local xmlns_ping = "urn:xmpp:ping"; function verse.plugins.ping(stream) function stream:ping(jid, callback) - local t = socket.gettime(); - stream:send_iq(verse.iq{ to = jid, type = "get" }:tag("ping", { xmlns = xmlns_ping }), + local t = gettime(); + stream:send_iq(verse.iq{ to = jid, type = "get" }:tag("ping", { xmlns = xmlns_ping }), function (reply) if reply.attr.type == "error" then local type, condition, text = reply:get_error(); @@ -14,7 +15,7 @@ return; end end - callback(socket.gettime()-t, jid); + callback(gettime()-t, jid); end); end stream:hook("iq/"..xmlns_ping, function(stanza) diff -r 6042c938e369 -r 82ad158714e5 plugins/private.lua --- a/plugins/private.lua Sat Jan 09 11:03:30 2016 +0000 +++ b/plugins/private.lua Tue Jan 12 13:14:36 2016 +0000 @@ -18,7 +18,7 @@ end self:send_iq(iq, callback); end - + function stream:private_get(name, xmlns, callback) self:send_iq(verse.iq({type="get"}) :tag("query", { xmlns = xmlns_private }) diff -r 6042c938e369 -r 82ad158714e5 plugins/proxy65.lua --- a/plugins/proxy65.lua Sat Jan 09 11:03:30 2016 +0000 +++ b/plugins/proxy65.lua Tue Jan 12 13:14:36 2016 +0000 @@ -1,6 +1,6 @@ -local events = require "util.events"; +local verse = require "verse"; local uuid = require "util.uuid"; -local sha1 = require "util.sha1"; +local sha1 = require "util.hashes".sha1; local proxy65_mt = {}; proxy65_mt.__index = proxy65_mt; @@ -19,12 +19,12 @@ outstanding_proxies = outstanding_proxies + 1; stream:send_iq(verse.iq({ to = service.jid, type = "get" }) :tag("query", { xmlns = xmlns_bytestreams }), function (result) - + outstanding_proxies = outstanding_proxies - 1; if result.attr.type == "result" then local streamhost = result:get_child("query", xmlns_bytestreams) :get_child("streamhost").attr; - + stream.proxy65.available_streamhosts[streamhost.jid] = { jid = streamhost.jid; host = streamhost.host; @@ -43,14 +43,14 @@ streamhosts = {}, current_host = 0; }); - + -- Parse hosts from request for tag in request.tags[1]:childtags() do if tag.name == "streamhost" then - table.insert(conn.streamhosts, tag.attr); + table.insert(conn.streamhosts, tag.attr); end end - + --Attempt to connect to the next host local function attempt_next_streamhost() -- First connect, or the last connect failed @@ -68,7 +68,7 @@ stream:send(verse.error_reply(request, "cancel", "item-not-found")); -- Let disconnected event fall through to user handlers... end - + function conn:accept() conn:hook("disconnected", attempt_next_streamhost, 100); -- When this event fires, we're connected to a streamhost @@ -94,14 +94,14 @@ target_jid = target_jid; bytestream_sid = uuid.generate(); }); - + local request = verse.iq{type="set", to = target_jid} :tag("query", { xmlns = xmlns_bytestreams, mode = "tcp", sid = conn.bytestream_sid }); for _, proxy in ipairs(proxies or self.proxies) do request:tag("streamhost", proxy):up(); end - - + + self.stream:send_iq(request, function (reply) if reply.attr.type == "error" then local type, condition, text = reply:get_error(); @@ -109,9 +109,9 @@ else -- Target connected to streamhost, connect ourselves local streamhost_used = reply.tags[1]:get_child("streamhost-used"); - if not streamhost_used then + -- if not streamhost_used then --FIXME: Emit error - end + -- end conn.streamhost_jid = streamhost_used.attr.jid; local host, port; for _, proxy in ipairs(proxies or self.proxies) do @@ -120,24 +120,23 @@ break; end end - if not (host and port) then + -- if not (host and port) then --FIXME: Emit error - end - + -- end + conn:connect(host, port); local function handle_proxy_connected() conn:unhook("connected", handle_proxy_connected); -- Both of us connected, tell proxy to activate connection - local request = verse.iq{to = conn.streamhost_jid, type="set"} + local activate_request = verse.iq{to = conn.streamhost_jid, type="set"} :tag("query", { xmlns = xmlns_bytestreams, sid = conn.bytestream_sid }) :tag("activate"):text(target_jid); - self.stream:send_iq(request, function (reply) - if reply.attr.type == "result" then + self.stream:send_iq(activate_request, function (activated) + if activated.attr.type == "result" then -- Connection activated, ready to use conn:event("connected", conn); - else - --FIXME: Emit error + -- else --FIXME: Emit error end end); return true; @@ -151,14 +150,14 @@ end function negotiate_socks5(stream, conn, sid, requester_jid, target_jid) - local hash = sha1.sha1(sid..requester_jid..target_jid); + local hash = sha1(sid..requester_jid..target_jid); local function suppress_connected() conn:unhook("connected", suppress_connected); return true; end local function receive_connection_response(data) conn:unhook("incoming-raw", receive_connection_response); - + if data:sub(1, 2) ~= "\005\000" then return conn:event("error", "connection-failure"); end diff -r 6042c938e369 -r 82ad158714e5 plugins/pubsub.lua --- a/plugins/pubsub.lua Sat Jan 09 11:03:30 2016 +0000 +++ b/plugins/pubsub.lua Tue Jan 12 13:14:36 2016 +0000 @@ -1,12 +1,11 @@ local verse = require "verse"; -local jid_bare = require "util.jid".bare; local t_insert = table.insert; local xmlns_pubsub = "http://jabber.org/protocol/pubsub"; local xmlns_pubsub_owner = "http://jabber.org/protocol/pubsub#owner"; local xmlns_pubsub_event = "http://jabber.org/protocol/pubsub#event"; -local xmlns_pubsub_errors = "http://jabber.org/protocol/pubsub#errors"; +-- local xmlns_pubsub_errors = "http://jabber.org/protocol/pubsub#errors"; local pubsub = {}; local pubsub_mt = { __index = pubsub }; @@ -213,7 +212,7 @@ if options ~= nil then error("Subscription configuration is not implemented yet."); end - self.stream:send_iq(pubsub_iq("set", self.service, nil, "subscribe", self.node, jid, id) + self.stream:send_iq(pubsub_iq("set", self.service, nil, "subscribe", self.node, jid) , callback); end diff -r 6042c938e369 -r 82ad158714e5 plugins/roster.lua --- a/plugins/roster.lua Sat Jan 09 11:03:30 2016 +0000 +++ b/plugins/roster.lua Tue Jan 12 13:14:36 2016 +0000 @@ -24,7 +24,7 @@ local function item_lua2xml(item_table) local xml_item = verse.stanza("item", { xmlns = xmlns_roster }); for k, v in pairs(item_table) do - if k ~= "groups" then + if k ~= "groups" then xml_item.attr[k] = v; else for i = 1,#v do @@ -39,7 +39,6 @@ local item_table = { }; local groups = {}; item_table.groups = groups; - local jid = xml_item.attr.jid; for k, v in pairs(xml_item.attr) do if k ~= "xmlns" then @@ -75,8 +74,7 @@ if reply.attr.type == "result" then callback(true); else - local type, condition, text = reply:get_error(); - callback(nil, { type, condition, text }); + callback(nil, reply); end end); end @@ -94,8 +92,7 @@ if reply.attr.type == "result" then callback(true); else - local type, condition, text = reply:get_error(); - callback(nil, { type, condition, text }); + callback(nil, reply); end end); end @@ -126,8 +123,7 @@ end callback(roster); else - local type, condition, text = stanza:get_error(); - callback(nil, { type, condition, text }); --FIXME + callback(nil, result); end end); end diff -r 6042c938e369 -r 82ad158714e5 plugins/sasl.lua --- a/plugins/sasl.lua Sat Jan 09 11:03:30 2016 +0000 +++ b/plugins/sasl.lua Tue Jan 12 13:14:36 2016 +0000 @@ -1,4 +1,4 @@ --- local verse = require"verse"; +local verse = require"verse"; local base64, unbase64 = require "mime".b64, require"mime".unb64; local xmlns_sasl = "urn:ietf:params:xml:ns:xmpp-sasl"; @@ -48,7 +48,7 @@ stream:send(auth_stanza); return true; end - + local function handle_sasl(sasl_stanza) if sasl_stanza.name == "failure" then local err = sasl_stanza.tags[1]; @@ -71,10 +71,10 @@ end return true; end - + stream:hook("stream-features", handle_features, 300); stream:hook("stream/"..xmlns_sasl, handle_sasl); - + return true; end diff -r 6042c938e369 -r 82ad158714e5 plugins/session.lua --- a/plugins/session.lua Sat Jan 09 11:03:30 2016 +0000 +++ b/plugins/session.lua Tue Jan 12 13:14:36 2016 +0000 @@ -3,7 +3,7 @@ local xmlns_session = "urn:ietf:params:xml:ns:xmpp-session"; function verse.plugins.session(stream) - + local function handle_features(features) local session_feature = features:get_child("session", xmlns_session); if session_feature and not session_feature:get_child("optional") then @@ -14,7 +14,6 @@ if reply.attr.type == "result" then stream:event("session-success"); elseif reply.attr.type == "error" then - local err = reply:child_with_name("error"); local type, condition, text = reply:get_error(); stream:event("session-failure", { error = condition, text = text, type = type }); end @@ -25,6 +24,6 @@ end end stream:hook("stream-features", handle_features); - + return true; end diff -r 6042c938e369 -r 82ad158714e5 plugins/smacks.lua --- a/plugins/smacks.lua Sat Jan 09 11:03:30 2016 +0000 +++ b/plugins/smacks.lua Tue Jan 12 13:14:36 2016 +0000 @@ -1,5 +1,5 @@ local verse = require "verse"; -local now = socket.gettime; +local now = require"socket".gettime; local xmlns_sm = "urn:xmpp:sm:2"; @@ -9,10 +9,10 @@ local last_ack = 0; local last_stanza_time = now(); local timer_active; - + -- State for incoming stanzas local handled_stanza_count = 0; - + -- Catch incoming stanzas local function incoming_stanza(stanza) if stanza.attr.xmlns == "jabber:client" or not stanza.attr.xmlns then @@ -22,7 +22,7 @@ end -- Catch outgoing stanzas - function outgoing_stanza(stanza) + local function outgoing_stanza(stanza) -- NOTE: This will not behave nice if stanzas are serialized before this point if stanza.name and not stanza.attr.xmlns then -- serialize stanzas in order to bypass this on resumption @@ -59,14 +59,14 @@ end); return true; end - end + end -- Graceful shutdown local function on_close() stream.resumption_token = nil; stream:unhook("disconnected", on_disconnect); end - + local function handle_sm_command(stanza) if stanza.name == "r" then -- Request for acks for stanzas we received stream:debug("Ack requested... acking %d handled stanzas", handled_stanza_count); diff -r 6042c938e369 -r 82ad158714e5 plugins/tls.lua --- a/plugins/tls.lua Sat Jan 09 11:03:30 2016 +0000 +++ b/plugins/tls.lua Tue Jan 12 13:14:36 2016 +0000 @@ -18,7 +18,7 @@ local function handle_tls(tls_status) if tls_status.name == "proceed" then stream:debug("Server says proceed, handshake starting..."); - stream.conn:starttls({mode="client", protocol="sslv23", options="no_sslv2"}, true); + stream.conn:starttls(stream.ssl or {mode="client", protocol="sslv23", options="no_sslv2",capath="/etc/ssl/certs"}, true); end end local function handle_status(new_status) @@ -31,6 +31,6 @@ stream:hook("stream-features", handle_features, 400); stream:hook("stream/"..xmlns_tls, handle_tls); stream:hook("status", handle_status, 400); - + return true; end diff -r 6042c938e369 -r 82ad158714e5 plugins/vcard.lua --- a/plugins/vcard.lua Sat Jan 09 11:03:30 2016 +0000 +++ b/plugins/vcard.lua Tue Jan 12 13:14:36 2016 +0000 @@ -7,8 +7,7 @@ function stream:get_vcard(jid, callback) --jid = nil for self stream:send_iq(verse.iq({to = jid, type="get"}) :tag("vCard", {xmlns=xmlns_vcard}), callback and function(stanza) - local lCard, xCard; - vCard = stanza:get_child("vCard", xmlns_vcard); + local vCard = stanza:get_child("vCard", xmlns_vcard); if stanza.attr.type == "result" and vCard then vCard = vcard.from_xep54(vCard) callback(vCard) diff -r 6042c938e369 -r 82ad158714e5 plugins/vcard_update.lua --- a/plugins/vcard_update.lua Sat Jan 09 11:03:30 2016 +0000 +++ b/plugins/vcard_update.lua Tue Jan 12 13:14:36 2016 +0000 @@ -1,16 +1,9 @@ local verse = require "verse"; -local xmlns_vcard, xmlns_vcard_update = "vcard-temp", "vcard-temp:x:update"; +-- local xmlns_vcard = "vcard-temp"; +local xmlns_vcard_update = "vcard-temp:x:update"; --- MMMmmmm.. hacky -local ok, fun = pcall(function() return require("util.hashes").sha1; end); -if not ok then - ok, fun = pcall(function() return require("util.sha1").sha1; end); - if not ok then - error("Could not find a sha1()") - end -end -local sha1 = fun; +local sha1 = require("util.hashes").sha1; local ok, fun = pcall(function() local unb64 = require("util.encodings").base64.decode; @@ -32,7 +25,7 @@ local x_vcard_update; - function update_vcard_photo(vCard) + local function update_vcard_photo(vCard) local data; for i=1,#vCard do if vCard[i].name == "PHOTO" then @@ -51,10 +44,10 @@ end end - local _set_vcard = stream.set_vcard; --[[ TODO Complete this, it's probably broken. -- Maybe better to hook outgoing stanza? + local _set_vcard = stream.set_vcard; function stream:set_vcard(vCard, callback) _set_vcard(vCard, function(event, ...) if event.attr.type == "result" then @@ -71,7 +64,7 @@ --]] local initial_vcard_fetch_started; - stream:hook("ready", function(event) + stream:hook("ready", function() if initial_vcard_fetch_started then return; end initial_vcard_fetch_started = true; -- if stream:jid_supports(nil, xmlns_vcard) then TODO this, correctly diff -r 6042c938e369 -r 82ad158714e5 plugins/version.lua --- a/plugins/version.lua Sat Jan 09 11:03:30 2016 +0000 +++ b/plugins/version.lua Tue Jan 12 13:14:36 2016 +0000 @@ -26,11 +26,11 @@ stream:send(reply); return true; end); - + function stream:query_version(target_jid, callback) - callback = callback or function (version) return stream:event("version/response", version); end - stream:send_iq(verse.iq({ type = "get", to = target_jid }) - :tag("query", { xmlns = xmlns_version }), + callback = callback or function (version) return self:event("version/response", version); end + self:send_iq(verse.iq({ type = "get", to = target_jid }) + :tag("query", { xmlns = xmlns_version }), function (reply) if reply.attr.type == "result" then local query = reply:get_child("query", xmlns_version); diff -r 6042c938e369 -r 82ad158714e5 squishy --- a/squishy Sat Jan 09 11:03:30 2016 +0000 +++ b/squishy Tue Jan 12 13:14:36 2016 +0000 @@ -6,11 +6,9 @@ Module "util.sha1" "util/sha1.lua" Module "lib.adhoc" "libs/adhoc.lib.lua" -AutoFetchURL("http://hg.prosody.im/prosody-modules/raw-file/tip/mod_mam/?"); -Module "util.rsm" "rsm.lib.lua" -- Prosody libraries if not GetOption("prosody") then - AutoFetchURL "http://hg.prosody.im/0.9/raw-file/tip/?" + AutoFetchURL "http://hg.prosody.im/0.9/raw-file/0.9.9/?" else AutoFetchURL(GetOption("prosody").."/?") end @@ -30,7 +28,11 @@ Module "util.vcard" "util/vcard.lua" Module "util.logger" "util/logger.lua" Module "util.datetime" "util/datetime.lua" -Module "util.ip" "util/ip.lua" +Module "util.json" "util/json.lua" +Module "util.xml" "util/xml.lua" +Module "util.rsm" "util/rsm.lua" +Module "util.random" "util/random.lua" +Module "util.ip" "util/ip.lua" Module "util.sasl.scram" "util/sasl/scram.lua" Module "util.sasl.plain" "util/sasl/plain.lua" diff -r 6042c938e369 -r 82ad158714e5 util/random.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/util/random.lua Tue Jan 12 13:14:36 2016 +0000 @@ -0,0 +1,43 @@ +-- Prosody IM +-- Copyright (C) 2008-2014 Matthew Wild +-- Copyright (C) 2008-2014 Waqas Hussain +-- +-- This project is MIT/X11 licensed. Please see the +-- COPYING file in the source package for more information. +-- + +local tostring = tostring; +local os_time = os.time; +local os_clock = os.clock; +local ceil = math.ceil; +local H = require "util.hashes".sha1; + +local last_uniq_time = 0; +local function uniq_time() + local new_uniq_time = os_time(); + if last_uniq_time >= new_uniq_time then new_uniq_time = last_uniq_time + 1; end + last_uniq_time = new_uniq_time; + return new_uniq_time; +end + +local function new_random(x) + return H(x..os_clock()..tostring({})); +end + +local buffer = new_random(uniq_time()); + +local function seed(x) + buffer = new_random(buffer..x); +end + +local function bytes(n) + if #buffer < n+4 then seed(uniq_time()); end + local r = buffer:sub(1, n); + buffer = buffer:sub(n+1); + return r; +end + +return { + seed = seed; + bytes = bytes; +}; diff -r 6042c938e369 -r 82ad158714e5 util/rsm.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/util/rsm.lua Tue Jan 12 13:14:36 2016 +0000 @@ -0,0 +1,87 @@ +local stanza = require"util.stanza".stanza; +local tostring, tonumber = tostring, tonumber; +local type = type; +local pairs = pairs; + +local xmlns_rsm = 'http://jabber.org/protocol/rsm'; + +local element_parsers = {}; + +do + local parsers = element_parsers; + local function xs_int(st) + return tonumber((st:get_text())); + end + local function xs_string(st) + return st:get_text(); + end + + parsers.after = xs_string; + parsers.before = function(st) + local text = st:get_text(); + return text == "" or text; + end; + parsers.max = xs_int; + parsers.index = xs_int; + + parsers.first = function(st) + return { index = tonumber(st.attr.index); st:get_text() }; + end; + parsers.last = xs_string; + parsers.count = xs_int; +end + +local element_generators = setmetatable({ + first = function(st, data) + if type(data) == "table" then + st:tag("first", { index = data.index }):text(data[1]):up(); + else + st:tag("first"):text(tostring(data)):up(); + end + end; + before = function(st, data) + if data == true then + st:tag("before"):up(); + else + st:tag("before"):text(tostring(data)):up(); + end + end +}, { + __index = function(_, name) + return function(st, data) + st:tag(name):text(tostring(data)):up(); + end + end; +}); + + +local function parse(set) + local rs = {}; + for tag in set:childtags() do + local name = tag.name; + local parser = name and element_parsers[name]; + if parser then + rs[name] = parser(tag); + end + end + return rs; +end + +local function generate(t) + local st = stanza("set", { xmlns = xmlns_rsm }); + for k,v in pairs(t) do + if element_parsers[k] then + element_generators[k](st, v); + end + end + return st; +end + +local function get(st) + local set = st:get_child("set", xmlns_rsm); + if set and #set.tags > 0 then + return parse(set); + end +end + +return { parse = parse, generate = generate, get = get }; diff -r 6042c938e369 -r 82ad158714e5 util/sasl/scram.lua --- a/util/sasl/scram.lua Sat Jan 09 11:03:30 2016 +0000 +++ b/util/sasl/scram.lua Tue Jan 12 13:14:36 2016 +0000 @@ -1,7 +1,8 @@ local base64, unbase64 = require "mime".b64, require"mime".unb64; -local crypto = require"crypto"; +local hashes = require"util.hashes"; local bit = require"bit"; +local random = require"util.random"; local tonumber = tonumber; local char, byte = string.char, string.byte; @@ -14,14 +15,7 @@ end)); end -local function H(str) - return crypto.digest("sha1", str, true); -end - -local _hmac_digest = crypto.hmac.digest; -local function HMAC(key, str) - return _hmac_digest("sha1", str, key, true); -end +local H, HMAC = hashes.sha1, hashes.hmac_sha1; local function Hi(str, salt, i) local U = HMAC(str, salt .. "\0\0\0\1"); @@ -43,7 +37,7 @@ local function scram(stream, name) local username = "n=" .. value_safe(stream.username); - local c_nonce = base64(crypto.rand.bytes(15)); + local c_nonce = base64(random.bytes(15)); local our_nonce = "r=" .. c_nonce; local client_first_message_bare = username .. "," .. our_nonce; local cbind_data = "";