summaryrefslogtreecommitdiffstats
path: root/contrib/pgcrypto/pgp-compress.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--contrib/pgcrypto/pgp-compress.c346
1 files changed, 346 insertions, 0 deletions
diff --git a/contrib/pgcrypto/pgp-compress.c b/contrib/pgcrypto/pgp-compress.c
new file mode 100644
index 0000000..086bec3
--- /dev/null
+++ b/contrib/pgcrypto/pgp-compress.c
@@ -0,0 +1,346 @@
+/*
+ * pgp-compress.c
+ * ZIP and ZLIB compression via zlib.
+ *
+ * Copyright (c) 2005 Marko Kreen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * contrib/pgcrypto/pgp-compress.c
+ */
+
+#include "postgres.h"
+
+#include "pgp.h"
+#include "px.h"
+
+/*
+ * Compressed pkt writer
+ */
+
+#ifdef HAVE_LIBZ
+
+#include <zlib.h>
+
+#define ZIP_OUT_BUF 8192
+#define ZIP_IN_BLOCK 8192
+
+struct ZipStat
+{
+ uint8 type;
+ int buf_len;
+ int hdr_done;
+ z_stream stream;
+ uint8 buf[ZIP_OUT_BUF];
+};
+
+static void *
+z_alloc(void *priv, unsigned n_items, unsigned item_len)
+{
+ return palloc(n_items * item_len);
+}
+
+static void
+z_free(void *priv, void *addr)
+{
+ pfree(addr);
+}
+
+static int
+compress_init(PushFilter *next, void *init_arg, void **priv_p)
+{
+ int res;
+ struct ZipStat *st;
+ PGP_Context *ctx = init_arg;
+ uint8 type = ctx->compress_algo;
+
+ if (type != PGP_COMPR_ZLIB && type != PGP_COMPR_ZIP)
+ return PXE_PGP_UNSUPPORTED_COMPR;
+
+ /*
+ * init
+ */
+ st = palloc0(sizeof(*st));
+ st->buf_len = ZIP_OUT_BUF;
+ st->stream.zalloc = z_alloc;
+ st->stream.zfree = z_free;
+
+ if (type == PGP_COMPR_ZIP)
+ res = deflateInit2(&st->stream, ctx->compress_level,
+ Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY);
+ else
+ res = deflateInit(&st->stream, ctx->compress_level);
+ if (res != Z_OK)
+ {
+ pfree(st);
+ return PXE_PGP_COMPRESSION_ERROR;
+ }
+ *priv_p = st;
+
+ return ZIP_IN_BLOCK;
+}
+
+/* writes compressed data packet */
+
+/* can handle zero-len incoming data, but shouldn't */
+static int
+compress_process(PushFilter *next, void *priv, const uint8 *data, int len)
+{
+ int res,
+ n_out;
+ struct ZipStat *st = priv;
+
+ /*
+ * process data
+ */
+ st->stream.next_in = unconstify(uint8 *, data);
+ st->stream.avail_in = len;
+ while (st->stream.avail_in > 0)
+ {
+ st->stream.next_out = st->buf;
+ st->stream.avail_out = st->buf_len;
+ res = deflate(&st->stream, Z_NO_FLUSH);
+ if (res != Z_OK)
+ return PXE_PGP_COMPRESSION_ERROR;
+
+ n_out = st->buf_len - st->stream.avail_out;
+ if (n_out > 0)
+ {
+ res = pushf_write(next, st->buf, n_out);
+ if (res < 0)
+ return res;
+ }
+ }
+
+ return 0;
+}
+
+static int
+compress_flush(PushFilter *next, void *priv)
+{
+ int res,
+ zres,
+ n_out;
+ struct ZipStat *st = priv;
+
+ st->stream.next_in = NULL;
+ st->stream.avail_in = 0;
+ while (1)
+ {
+ st->stream.next_out = st->buf;
+ st->stream.avail_out = st->buf_len;
+ zres = deflate(&st->stream, Z_FINISH);
+ if (zres != Z_STREAM_END && zres != Z_OK)
+ return PXE_PGP_COMPRESSION_ERROR;
+
+ n_out = st->buf_len - st->stream.avail_out;
+ if (n_out > 0)
+ {
+ res = pushf_write(next, st->buf, n_out);
+ if (res < 0)
+ return res;
+ }
+ if (zres == Z_STREAM_END)
+ break;
+ }
+ return 0;
+}
+
+static void
+compress_free(void *priv)
+{
+ struct ZipStat *st = priv;
+
+ deflateEnd(&st->stream);
+ px_memset(st, 0, sizeof(*st));
+ pfree(st);
+}
+
+static const PushFilterOps
+ compress_filter = {
+ compress_init, compress_process, compress_flush, compress_free
+};
+
+int
+pgp_compress_filter(PushFilter **res, PGP_Context *ctx, PushFilter *dst)
+{
+ return pushf_create(res, &compress_filter, ctx, dst);
+}
+
+/*
+ * Decompress
+ */
+struct DecomprData
+{
+ int buf_len; /* = ZIP_OUT_BUF */
+ int buf_data; /* available data */
+ uint8 *pos;
+ z_stream stream;
+ int eof;
+ uint8 buf[ZIP_OUT_BUF];
+};
+
+static int
+decompress_init(void **priv_p, void *arg, PullFilter *src)
+{
+ PGP_Context *ctx = arg;
+ struct DecomprData *dec;
+ int res;
+
+ if (ctx->compress_algo != PGP_COMPR_ZLIB
+ && ctx->compress_algo != PGP_COMPR_ZIP)
+ return PXE_PGP_UNSUPPORTED_COMPR;
+
+ dec = palloc0(sizeof(*dec));
+ dec->buf_len = ZIP_OUT_BUF;
+ *priv_p = dec;
+
+ dec->stream.zalloc = z_alloc;
+ dec->stream.zfree = z_free;
+
+ if (ctx->compress_algo == PGP_COMPR_ZIP)
+ res = inflateInit2(&dec->stream, -15);
+ else
+ res = inflateInit(&dec->stream);
+ if (res != Z_OK)
+ {
+ pfree(dec);
+ px_debug("decompress_init: inflateInit error");
+ return PXE_PGP_COMPRESSION_ERROR;
+ }
+
+ return 0;
+}
+
+static int
+decompress_read(void *priv, PullFilter *src, int len,
+ uint8 **data_p, uint8 *buf, int buflen)
+{
+ int res;
+ int flush;
+ struct DecomprData *dec = priv;
+
+restart:
+ if (dec->buf_data > 0)
+ {
+ if (len > dec->buf_data)
+ len = dec->buf_data;
+ *data_p = dec->pos;
+ dec->pos += len;
+ dec->buf_data -= len;
+ return len;
+ }
+
+ if (dec->eof)
+ return 0;
+
+ if (dec->stream.avail_in == 0)
+ {
+ uint8 *tmp;
+
+ res = pullf_read(src, 8192, &tmp);
+ if (res < 0)
+ return res;
+ dec->stream.next_in = tmp;
+ dec->stream.avail_in = res;
+ }
+
+ dec->stream.next_out = dec->buf;
+ dec->stream.avail_out = dec->buf_len;
+ dec->pos = dec->buf;
+
+ /*
+ * Z_SYNC_FLUSH is tell zlib to output as much as possible. It should do
+ * it anyway (Z_NO_FLUSH), but seems to reserve the right not to. So lets
+ * follow the API.
+ */
+ flush = dec->stream.avail_in ? Z_SYNC_FLUSH : Z_FINISH;
+ res = inflate(&dec->stream, flush);
+ if (res != Z_OK && res != Z_STREAM_END)
+ {
+ px_debug("decompress_read: inflate error: %d", res);
+ return PXE_PGP_CORRUPT_DATA;
+ }
+
+ dec->buf_data = dec->buf_len - dec->stream.avail_out;
+ if (res == Z_STREAM_END)
+ {
+ uint8 *tmp;
+
+ /*
+ * A stream must be terminated by a normal packet. If the last stream
+ * packet in the source stream is a full packet, a normal empty packet
+ * must follow. Since the underlying packet reader doesn't know that
+ * the compressed stream has been ended, we need to consume the
+ * terminating packet here. This read does not harm even if the
+ * stream has already ended.
+ */
+ res = pullf_read(src, 1, &tmp);
+
+ if (res < 0)
+ return res;
+ else if (res > 0)
+ {
+ px_debug("decompress_read: extra bytes after end of stream");
+ return PXE_PGP_CORRUPT_DATA;
+ }
+ dec->eof = 1;
+ }
+ goto restart;
+}
+
+static void
+decompress_free(void *priv)
+{
+ struct DecomprData *dec = priv;
+
+ inflateEnd(&dec->stream);
+ px_memset(dec, 0, sizeof(*dec));
+ pfree(dec);
+}
+
+static const PullFilterOps
+ decompress_filter = {
+ decompress_init, decompress_read, decompress_free
+};
+
+int
+pgp_decompress_filter(PullFilter **res, PGP_Context *ctx, PullFilter *src)
+{
+ return pullf_create(res, &decompress_filter, ctx, src);
+}
+#else /* !HAVE_LIBZ */
+
+int
+pgp_compress_filter(PushFilter **res, PGP_Context *ctx, PushFilter *dst)
+{
+ return PXE_PGP_UNSUPPORTED_COMPR;
+}
+
+int
+pgp_decompress_filter(PullFilter **res, PGP_Context *ctx, PullFilter *src)
+{
+ return PXE_PGP_UNSUPPORTED_COMPR;
+}
+
+#endif