File

plugins/compression.lua @ 498:50d0bd035bb7

util.sasl.oauthbearer: Don't send authzid It's not needed and not recommended in XMPP unless we want to act as someone other than who we authenticate as. We find out the JID during resource binding.
author Kim Alvefur <zash@zash.se>
date Fri, 23 Jun 2023 12:09:49 +0200
parent 380:0891b4e27766
line wrap: on
line source

-- 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.
--

local verse = require "verse";
local zlib = require "zlib";

local xmlns_compression_feature = "http://jabber.org/features/compress"
local xmlns_compression_protocol = "http://jabber.org/protocol/compress"
local xmlns_stream = "http://etherx.jabber.org/streams";

local compression_level = 9;

-- returns either nil or a fully functional ready to use inflate stream
local function get_deflate_stream(session)
	local status, deflate_stream = pcall(zlib.deflate, compression_level);
	if status == false then
		local error_st = verse.stanza("failure", {xmlns=xmlns_compression_protocol}):tag("setup-failed");
		session:send(error_st);
		session:error("Failed to create zlib.deflate filter: %s", tostring(deflate_stream));
		return
	end
	return deflate_stream
end

-- returns either nil or a fully functional ready to use inflate stream
local function get_inflate_stream(session)
	local status, inflate_stream = pcall(zlib.inflate);
	if status == false then
		local error_st = verse.stanza("failure", {xmlns=xmlns_compression_protocol}):tag("setup-failed");
		session:send(error_st);
		session:error("Failed to create zlib.inflate filter: %s", tostring(inflate_stream));
		return
	end
	return inflate_stream
end

-- setup compression for a stream
local function setup_compression(session, deflate_stream)
	function session:send(t)
			--TODO: Better code injection in the sending process
			local status, compressed, eof = pcall(deflate_stream, tostring(t), 'sync');
			if status == false then
				session:close({
					condition = "undefined-condition";
					text = compressed;
					extra = verse.stanza("failure", {xmlns=xmlns_compression_protocol}):tag("processing-failed");
				});
				session:warn("Compressed send failed: %s", tostring(compressed));
				return;
			end
			session.conn:write(compressed);
		end;
end

-- setup decompression for a stream
local function setup_decompression(session, inflate_stream)
	local old_data = session.data
	session.data = function(conn, data)
			session:debug("Decompressing data...");
			local status, decompressed, eof = pcall(inflate_stream, data);
			if status == false then
				session:close({
					condition = "undefined-condition";
					text = decompressed;
					extra = verse.stanza("failure", {xmlns=xmlns_compression_protocol}):tag("processing-failed");
				});
				stream:warn("%s", tostring(decompressed));
				return;
			end
			return old_data(conn, decompressed);
		end;
end

function verse.plugins.compression(stream)
	local function handle_features(features)
		if not stream.compressed then
			-- does remote server support compression?
			local comp_st = features:child_with_name("compression");
			if comp_st then
				-- do we support the mechanism
				for a in comp_st:children() do
					local algorithm = a[1]
					if algorithm == "zlib" then
						stream:send(verse.stanza("compress", {xmlns=xmlns_compression_protocol}):tag("method"):text("zlib"))
						stream:debug("Enabled compression using zlib.")
						return true;
					end
				end
				session:debug("Remote server supports no compression algorithm we support.")
			end
		end
	end
	local function handle_compressed(stanza)
		if stanza.name == "compressed" then
			stream:debug("Activating compression...")

			-- 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
			stream:warn("Failed to establish compression");
		end
	end
	stream:hook("stream-features", handle_features, 250);
	stream:hook("stream/"..xmlns_compression_protocol, handle_compressed);
end