diff -up dovecot-2.3.16/configure.ac.keeplzma dovecot-2.3.16/configure.ac
--- dovecot-2.3.16/configure.ac.keeplzma 2021-08-06 11:25:51.000000000 +0200
+++ dovecot-2.3.16/configure.ac 2022-02-28 13:58:02.337149927 +0100
@@ -173,7 +173,7 @@ AS_HELP_STRING([--with-bzlib], [Build wi
want_bzlib=auto)
AC_ARG_WITH(lzma,
-AS_HELP_STRING([--with-lzma], [Build with LZMA decompression support (auto)]),
+AS_HELP_STRING([--with-lzma], [Build with LZMA compression support (auto)]),
TEST_WITH(lzma, $withval),
want_lzma=auto)
diff -up dovecot-2.3.16/run-test-valgrind.supp.keeplzma dovecot-2.3.16/run-test-valgrind.supp
--- dovecot-2.3.16/run-test-valgrind.supp.keeplzma 2021-08-06 11:25:51.000000000 +0200
+++ dovecot-2.3.16/run-test-valgrind.supp 2022-02-28 13:58:02.337149927 +0100
@@ -5,6 +5,17 @@
obj:*/bash
}
{
+ <liblzma>
+ Memcheck:Cond
+ obj:/lib/x86_64-linux-gnu/liblzma.so.5.*
+ obj:/lib/x86_64-linux-gnu/liblzma.so.5.*
+ obj:/lib/x86_64-linux-gnu/liblzma.so.5.*
+ obj:/lib/x86_64-linux-gnu/liblzma.so.5.*
+ obj:/lib/x86_64-linux-gnu/liblzma.so.5.*
+ fun:lzma_stream_encoder
+ fun:lzma_easy_encoder
+}
+{
<openssl_centos6_i386_v1_0_1_compression_methods>
Memcheck:Leak
fun:malloc
diff -up dovecot-2.3.16/src/lib-compression/compression.c.keeplzma dovecot-2.3.16/src/lib-compression/compression.c
--- dovecot-2.3.16/src/lib-compression/compression.c.keeplzma 2021-08-06 11:25:51.000000000 +0200
+++ dovecot-2.3.16/src/lib-compression/compression.c 2022-02-28 14:22:32.467944396 +0100
@@ -25,6 +25,7 @@
#endif
#ifndef HAVE_LZMA
# define i_stream_create_lzma NULL
+# define o_stream_create_lzma NULL
#endif
#ifndef HAVE_LZ4
# define i_stream_create_lz4 NULL
@@ -216,7 +217,7 @@ const struct compression_handler compres
.ext = ".xz",
.is_compressed = is_compressed_xz,
.create_istream = i_stream_create_lzma,
- .create_ostream = NULL,
+ .create_ostream = o_stream_create_lzma,
.get_min_level = compression_get_min_level_unsupported,
.get_default_level = compression_get_default_level_unsupported,
.get_max_level = compression_get_max_level_unsupported,
diff -up dovecot-2.3.16/src/lib-compression/Makefile.am.keeplzma dovecot-2.3.16/src/lib-compression/Makefile.am
--- dovecot-2.3.16/src/lib-compression/Makefile.am.keeplzma 2021-08-06 11:25:51.000000000 +0200
+++ dovecot-2.3.16/src/lib-compression/Makefile.am 2022-02-28 13:58:02.337149927 +0100
@@ -13,6 +13,7 @@ libcompression_la_SOURCES = \
istream-zlib.c \
istream-bzlib.c \
istream-zstd.c \
+ ostream-lzma.c \
ostream-lz4.c \
ostream-zlib.c \
ostream-bzlib.c \
diff -up dovecot-2.3.16/src/lib-compression/ostream-lzma.c.keeplzma dovecot-2.3.16/src/lib-compression/ostream-lzma.c
--- dovecot-2.3.16/src/lib-compression/ostream-lzma.c.keeplzma 2022-02-28 13:58:02.338149934 +0100
+++ dovecot-2.3.16/src/lib-compression/ostream-lzma.c 2022-02-28 13:58:02.338149934 +0100
@@ -0,0 +1,263 @@
+/* Copyright (c) 2010-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+
+#ifdef HAVE_LZMA
+
+#include "ostream-private.h"
+#include "ostream-zlib.h"
+#include <lzma.h>
+
+#define CHUNK_SIZE (1024*64)
+
+struct lzma_ostream {
+ struct ostream_private ostream;
+ lzma_stream strm;
+
+ unsigned char outbuf[CHUNK_SIZE];
+ unsigned int outbuf_offset, outbuf_used;
+
+ bool flushed:1;
+};
+
+static void o_stream_lzma_close(struct iostream_private *stream,
+ bool close_parent)
+{
+ struct lzma_ostream *zstream = (struct lzma_ostream *)stream;
+ i_assert(zstream->ostream.finished ||
+ zstream->ostream.ostream.stream_errno != 0 ||
+ zstream->ostream.error_handling_disabled);
+ lzma_end(&zstream->strm);
+ if (close_parent)
+ o_stream_close(zstream->ostream.parent);
+}
+
+static int o_stream_zlib_send_outbuf(struct lzma_ostream *zstream)
+{
+ ssize_t ret;
+ size_t size;
+
+ if (zstream->outbuf_used == 0)
+ return 1;
+
+ size = zstream->outbuf_used - zstream->outbuf_offset;
+ i_assert(size > 0);
+ ret = o_stream_send(zstream->ostream.parent,
+ zstream->outbuf + zstream->outbuf_offset, size);
+ if (ret < 0) {
+ o_stream_copy_error_from_parent(&zstream->ostream);
+ return -1;
+ }
+ if ((size_t)ret != size) {
+ zstream->outbuf_offset += ret;
+ return 0;
+ }
+ zstream->outbuf_offset = 0;
+ zstream->outbuf_used = 0;
+ return 1;
+}
+
+static ssize_t
+o_stream_lzma_send_chunk(struct lzma_ostream *zstream,
+ const void *data, size_t size)
+{
+ lzma_stream *zs = &zstream->strm;
+ int ret;
+
+ i_assert(zstream->outbuf_used == 0);
+
+ zs->next_in = (void *)data;
+ zs->avail_in = size;
+ while (zs->avail_in > 0) {
+ if (zs->avail_out == 0) {
+ /* previous block was compressed. send it and start
+ compression for a new block. */
+ zs->next_out = zstream->outbuf;
+ zs->avail_out = sizeof(zstream->outbuf);
+
+ zstream->outbuf_used = sizeof(zstream->outbuf);
+ if ((ret = o_stream_zlib_send_outbuf(zstream)) < 0)
+ return -1;
+ if (ret == 0) {
+ /* parent stream's buffer full */
+ break;
+ }
+ }
+
+ ret = lzma_code(zs, LZMA_RUN);
+ switch (ret) {
+ case LZMA_OK:
+ break;
+ case LZMA_MEM_ERROR:
+ i_fatal_status(FATAL_OUTOFMEM,
+ "lzma.write(%s): Out of memory",
+ o_stream_get_name(&zstream->ostream.ostream));
+ default:
+ i_panic("lzma.write(%s) failed with unexpected code %d",
+ o_stream_get_name(&zstream->ostream.ostream), ret);
+ }
+ }
+ size -= zs->avail_in;
+
+ return size;
+}
+
+static int o_stream_lzma_send_flush(struct lzma_ostream *zstream, bool final)
+{
+ lzma_stream *zs = &zstream->strm;
+ size_t len;
+ bool done = FALSE;
+ int ret;
+
+ i_assert(zs->avail_in == 0);
+
+ if (zstream->flushed) {
+ i_assert(zstream->outbuf_used == 0);
+ return 1;
+ }
+
+ if ((ret = o_stream_flush_parent_if_needed(&zstream->ostream)) <= 0)
+ return ret;
+ if ((ret = o_stream_zlib_send_outbuf(zstream)) <= 0)
+ return ret;
+
+ if (!final)
+ return 1;
+
+ i_assert(zstream->outbuf_used == 0);
+ do {
+ len = sizeof(zstream->outbuf) - zs->avail_out;
+ if (len != 0) {
+ zs->next_out = zstream->outbuf;
+ zs->avail_out = sizeof(zstream->outbuf);
+
+ zstream->outbuf_used = len;
+ if ((ret = o_stream_zlib_send_outbuf(zstream)) <= 0)
+ return ret;
+ if (done)
+ break;
+ }
+ ret = lzma_code(zs, LZMA_FINISH);
+ switch (ret) {
+ case LZMA_OK:
+ /* still unfinished - need to call lzma_code() again */
+ break;
+ case LZMA_STREAM_END:
+ /* output is fully finished */
+ done = TRUE;
+ break;
+ case LZMA_MEM_ERROR:
+ i_fatal_status(FATAL_OUTOFMEM,
+ "lzma.write(%s): Out of memory",
+ o_stream_get_name(&zstream->ostream.ostream));
+ default:
+ i_panic("lzma.write(%s) flush failed with unexpected code %d",
+ o_stream_get_name(&zstream->ostream.ostream), ret);
+ }
+ } while (zs->avail_out != sizeof(zstream->outbuf));
+
+ if (final)
+ zstream->flushed = TRUE;
+ i_assert(zstream->outbuf_used == 0);
+ return 1;
+}
+
+static int o_stream_lzma_flush(struct ostream_private *stream)
+{
+ struct lzma_ostream *zstream = (struct lzma_ostream *)stream;
+ int ret;
+
+ if ((ret = o_stream_lzma_send_flush(zstream, stream->finished)) < 0)
+ return -1;
+ else if (ret > 0)
+ return o_stream_flush_parent(stream);
+ return ret;
+}
+
+static size_t
+o_stream_lzma_get_buffer_used_size(const struct ostream_private *stream)
+{
+ const struct lzma_ostream *zstream =
+ (const struct lzma_ostream *)stream;
+
+ /* outbuf has already compressed data that we're trying to send to the
+ parent stream. We're not including lzma's internal compression
+ buffer size. */
+ return (zstream->outbuf_used - zstream->outbuf_offset) +
+ o_stream_get_buffer_used_size(stream->parent);
+}
+
+static size_t
+o_stream_lzma_get_buffer_avail_size(const struct ostream_private *stream)
+{
+ /* FIXME: not correct - this is counting compressed size, which may be
+ too larger than uncompressed size in some situations. Fixing would
+ require some kind of additional buffering. */
+ return o_stream_get_buffer_avail_size(stream->parent);
+}
+
+static ssize_t
+o_stream_lzma_sendv(struct ostream_private *stream,
+ const struct const_iovec *iov, unsigned int iov_count)
+{
+ struct lzma_ostream *zstream = (struct lzma_ostream *)stream;
+ ssize_t ret, bytes = 0;
+ unsigned int i;
+
+ if ((ret = o_stream_zlib_send_outbuf(zstream)) <= 0) {
+ /* error / we still couldn't flush existing data to
+ parent stream. */
+ return ret;
+ }
+
+ for (i = 0; i < iov_count; i++) {
+ ret = o_stream_lzma_send_chunk(zstream, iov[i].iov_base,
+ iov[i].iov_len);
+ if (ret < 0)
+ return -1;
+ bytes += ret;
+ if ((size_t)ret != iov[i].iov_len)
+ break;
+ }
+ stream->ostream.offset += bytes;
+
+ /* avail_in!=0 check is used to detect errors. if it's non-zero here
+ it simply means we didn't send all the data */
+ zstream->strm.avail_in = 0;
+ return bytes;
+}
+
+struct ostream *o_stream_create_lzma(struct ostream *output, int level)
+{
+ struct lzma_ostream *zstream;
+ lzma_ret ret;
+
+ i_assert(level >= 1 && level <= 9);
+
+ zstream = i_new(struct lzma_ostream, 1);
+ zstream->ostream.sendv = o_stream_lzma_sendv;
+ zstream->ostream.flush = o_stream_lzma_flush;
+ zstream->ostream.get_buffer_used_size =
+ o_stream_lzma_get_buffer_used_size;
+ zstream->ostream.get_buffer_avail_size =
+ o_stream_lzma_get_buffer_avail_size;
+ zstream->ostream.iostream.close = o_stream_lzma_close;
+
+ ret = lzma_easy_encoder(&zstream->strm, level, LZMA_CHECK_CRC64);
+ switch (ret) {
+ case LZMA_OK:
+ break;
+ case LZMA_MEM_ERROR:
+ i_fatal_status(FATAL_OUTOFMEM, "lzma: Out of memory");
+ case LZMA_OPTIONS_ERROR:
+ i_fatal("lzma: Invalid level");
+ default:
+ i_fatal("lzma_easy_encoder() failed with %d", ret);
+ }
+
+ zstream->strm.next_out = zstream->outbuf;
+ zstream->strm.avail_out = sizeof(zstream->outbuf);
+ return o_stream_create(&zstream->ostream, output,
+ o_stream_get_fd(output));
+}
+#endif
diff -up dovecot-2.3.16/src/lib-compression/ostream-zlib.h.keeplzma dovecot-2.3.16/src/lib-compression/ostream-zlib.h
--- dovecot-2.3.16/src/lib-compression/ostream-zlib.h.keeplzma 2021-08-06 11:25:51.000000000 +0200
+++ dovecot-2.3.16/src/lib-compression/ostream-zlib.h 2022-02-28 13:58:02.338149934 +0100
@@ -4,6 +4,7 @@
struct ostream *o_stream_create_gz(struct ostream *output, int level);
struct ostream *o_stream_create_deflate(struct ostream *output, int level);
struct ostream *o_stream_create_bz2(struct ostream *output, int level);
+struct ostream *o_stream_create_lzma(struct ostream *output, int level);
struct ostream *o_stream_create_lz4(struct ostream *output, int level);
struct ostream *o_stream_create_zstd(struct ostream *output, int level);
diff -up dovecot-2.3.16/src/lib-compression/test-compression.c.keeplzma dovecot-2.3.16/src/lib-compression/test-compression.c
--- dovecot-2.3.16/src/lib-compression/test-compression.c.keeplzma 2021-08-06 11:25:51.000000000 +0200
+++ dovecot-2.3.16/src/lib-compression/test-compression.c 2022-02-28 13:58:02.338149934 +0100
@@ -730,7 +730,6 @@ static void test_compression_int(bool au
for (i = 0; compression_handlers[i].name != NULL; i++) {
if (compression_handlers[i].create_istream != NULL &&
- compression_handlers[i].create_ostream != NULL &&
(!autodetect ||
compression_handlers[i].is_compressed != NULL)) T_BEGIN {
if (compression_handlers[i].is_compressed != NULL &&