/* * 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 "mbuf.h" #include "px.h" #include "pgp.h" /* * Compressed pkt writer */ #ifdef HAVE_LIBZ #include #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 px_alloc(n_items * item_len); } static void z_free(void *priv, void *addr) { px_free(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 = px_alloc(sizeof(*st)); memset(st, 0, 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) { px_free(st); return PXE_PGP_COMPRESSION_ERROR; } *priv_p = st; return ZIP_IN_BLOCK; } /* writes compressed data packet */ /* cant handle zero-len incoming data, but shouldnt */ static int compress_process(PushFilter *next, void *priv, const uint8 *data, int len) { int res, n_out; struct ZipStat *st = priv; /* * process data */ while (len > 0) { st->stream.next_in = (void *) data; st->stream.avail_in = len; st->stream.next_out = st->buf; st->stream.avail_out = st->buf_len; res = deflate(&st->stream, 0); 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; } len = st->stream.avail_in; } 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); memset(st, 0, sizeof(*st)); px_free(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 = px_alloc(sizeof(*dec)); memset(dec, 0, 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) { px_free(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) dec->eof = 1; goto restart; } static void decompress_free(void *priv) { struct DecomprData *dec = priv; inflateEnd(&dec->stream); memset(dec, 0, sizeof(*dec)); px_free(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_ZLIB */ 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