2a88d82cad
CID 353531 CID 353530
272 lines
6.7 KiB
C
272 lines
6.7 KiB
C
/*
|
|
* compression.c: Generic wrapper for compression of data
|
|
*
|
|
* Copyright (C) 2006, Identity Alliance, Thomas Harning <thomas.harning@identityalliance.com>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#if HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#ifdef ENABLE_ZLIB /* empty file without zlib */
|
|
#include <zlib.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "internal.h"
|
|
#include "errors.h"
|
|
#include "compression.h"
|
|
|
|
static int zerr_to_opensc(int err) {
|
|
switch(err) {
|
|
case Z_OK:
|
|
case Z_STREAM_END:
|
|
return SC_SUCCESS;
|
|
case Z_UNKNOWN:
|
|
return SC_ERROR_UNKNOWN;
|
|
case Z_DATA_ERROR:
|
|
case Z_BUF_ERROR:
|
|
return SC_ERROR_UNKNOWN_DATA_RECEIVED;
|
|
case Z_MEM_ERROR:
|
|
return SC_ERROR_OUT_OF_MEMORY;
|
|
case Z_VERSION_ERROR:
|
|
case Z_STREAM_ERROR:
|
|
/* case Z_NEED_DICT: */
|
|
default:
|
|
return SC_ERROR_INTERNAL;
|
|
}
|
|
}
|
|
static int detect_method(const u8* in, size_t inLen) {
|
|
if (in != NULL && inLen > 1) {
|
|
if (in[0] == 0x1f && in[1] == 0x8b)
|
|
return COMPRESSION_GZIP;
|
|
/*
|
|
* A zlib stream has the following structure:
|
|
* 0 1
|
|
* +---+---+
|
|
* |CMF|FLG| (more-->)
|
|
* +---+---+
|
|
*
|
|
* FLG (FLaGs)
|
|
* This flag byte is divided as follows:
|
|
*
|
|
* bits 0 to 4 FCHECK (check bits for CMF and FLG)
|
|
* bit 5 FDICT (preset dictionary)
|
|
* bits 6 to 7 FLEVEL (compression level)
|
|
*
|
|
* The FCHECK value must be such that CMF and FLG, when viewed as
|
|
* a 16-bit unsigned integer stored in MSB order (CMF*256 + FLG),
|
|
* is a multiple of 31.
|
|
*/
|
|
if ((((uint16_t) in[0])*256 + in[1]) % 31 == 0)
|
|
return COMPRESSION_ZLIB;
|
|
}
|
|
return COMPRESSION_UNKNOWN;
|
|
}
|
|
|
|
static int sc_compress_gzip(u8* out, size_t* outLen, const u8* in, size_t inLen) {
|
|
/* Since compress does not offer a way to make it compress gzip... manually set it up */
|
|
z_stream gz;
|
|
int err;
|
|
int window_size = 15 + 0x10;
|
|
memset(&gz, 0, sizeof(gz));
|
|
|
|
gz.next_in = (u8*)in;
|
|
gz.avail_in = inLen;
|
|
gz.next_out = out;
|
|
gz.avail_out = *outLen;
|
|
|
|
err = deflateInit2(&gz, Z_BEST_COMPRESSION, Z_DEFLATED, window_size, 9, Z_DEFAULT_STRATEGY);
|
|
if(err != Z_OK) return zerr_to_opensc(err);
|
|
err = deflate(&gz, Z_FINISH);
|
|
if(err != Z_STREAM_END) {
|
|
deflateEnd(&gz);
|
|
return zerr_to_opensc(err);
|
|
}
|
|
*outLen = gz.total_out;
|
|
|
|
err = deflateEnd(&gz);
|
|
return zerr_to_opensc(err);
|
|
}
|
|
|
|
static int sc_decompress_gzip(u8* out, size_t* outLen, const u8* in, size_t inLen) {
|
|
/* Since uncompress does not offer a way to make it uncompress gzip... manually set it up */
|
|
z_stream gz;
|
|
int err;
|
|
int window_size = 15 + 0x20;
|
|
memset(&gz, 0, sizeof(gz));
|
|
|
|
gz.next_in = (u8*)in;
|
|
gz.avail_in = inLen;
|
|
gz.next_out = out;
|
|
gz.avail_out = *outLen;
|
|
|
|
*outLen = 0;
|
|
|
|
err = inflateInit2(&gz, window_size);
|
|
if (err != Z_OK) return zerr_to_opensc(err);
|
|
err = inflate(&gz, Z_FINISH);
|
|
if(err != Z_STREAM_END) {
|
|
inflateEnd(&gz);
|
|
return zerr_to_opensc(err);
|
|
}
|
|
*outLen = gz.total_out;
|
|
|
|
err = inflateEnd(&gz);
|
|
if (*outLen == 0) {
|
|
return SC_ERROR_UNKNOWN_DATA_RECEIVED;
|
|
}
|
|
return zerr_to_opensc(err);
|
|
}
|
|
|
|
int sc_compress(u8* out, size_t* outLen, const u8* in, size_t inLen, int method) {
|
|
unsigned long zlib_outlen;
|
|
int rc;
|
|
|
|
switch(method) {
|
|
case COMPRESSION_ZLIB:
|
|
zlib_outlen = *outLen;
|
|
rc = zerr_to_opensc(compress(out, &zlib_outlen, in, inLen));
|
|
*outLen = zlib_outlen;
|
|
return rc;
|
|
case COMPRESSION_GZIP:
|
|
return sc_compress_gzip(out, outLen, in, inLen);
|
|
default:
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
|
}
|
|
}
|
|
|
|
int sc_decompress(u8* out, size_t* outLen, const u8* in, size_t inLen, int method)
|
|
{
|
|
unsigned long zlib_outlen;
|
|
int rc;
|
|
|
|
if (in == NULL || out == NULL) {
|
|
return SC_ERROR_UNKNOWN_DATA_RECEIVED;
|
|
}
|
|
|
|
if (method == COMPRESSION_AUTO) {
|
|
method = detect_method(in, inLen);
|
|
if (method == COMPRESSION_UNKNOWN) {
|
|
*outLen = 0;
|
|
return SC_ERROR_UNKNOWN_DATA_RECEIVED;
|
|
}
|
|
}
|
|
switch (method) {
|
|
case COMPRESSION_ZLIB:
|
|
zlib_outlen = *outLen;
|
|
rc = zerr_to_opensc(uncompress(out, &zlib_outlen, in, inLen));
|
|
*outLen = zlib_outlen;
|
|
return rc;
|
|
case COMPRESSION_GZIP:
|
|
return sc_decompress_gzip(out, outLen, in, inLen);
|
|
default:
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
|
}
|
|
}
|
|
|
|
static int sc_decompress_zlib_alloc(u8** out, size_t* outLen, const u8* in, size_t inLen, int gzip) {
|
|
/* Since uncompress does not offer a way to make it uncompress gzip... manually set it up */
|
|
z_stream gz;
|
|
int err;
|
|
int window_size = 15;
|
|
const size_t startSize = inLen < 1024 ? 2048 : inLen * 2;
|
|
const size_t blockSize = inLen < 1024 ? 512 : inLen / 2;
|
|
size_t bufferSize = startSize;
|
|
if (gzip)
|
|
window_size += 0x20;
|
|
memset(&gz, 0, sizeof(gz));
|
|
|
|
if (!out || !outLen)
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
|
|
|
gz.next_in = (u8*)in;
|
|
gz.avail_in = inLen;
|
|
|
|
err = inflateInit2(&gz, window_size);
|
|
if (err != Z_OK)
|
|
return zerr_to_opensc(err);
|
|
|
|
*outLen = 0;
|
|
|
|
while (1) {
|
|
/* Setup buffer... */
|
|
size_t num;
|
|
u8* buf = realloc(*out, bufferSize);
|
|
if (!buf) {
|
|
free(*out);
|
|
*out = NULL;
|
|
return SC_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
*out = buf;
|
|
gz.next_out = buf + *outLen;
|
|
gz.avail_out = bufferSize - *outLen;
|
|
|
|
err = inflate(&gz, Z_FULL_FLUSH);
|
|
if (err != Z_STREAM_END && err != Z_OK) {
|
|
free(*out);
|
|
*out = NULL;
|
|
break;
|
|
}
|
|
num = *outLen + gz.avail_out;
|
|
if (bufferSize > num) {
|
|
*outLen += bufferSize - num;
|
|
bufferSize += bufferSize - num + blockSize;
|
|
}
|
|
if (err == Z_STREAM_END) {
|
|
if (*outLen > 0) {
|
|
/* Shrink it down, if it fails, just use old data */
|
|
buf = realloc(buf, *outLen);
|
|
if (buf) {
|
|
*out = buf;
|
|
}
|
|
} else {
|
|
free(*out);
|
|
*out = NULL;
|
|
err = Z_DATA_ERROR;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
inflateEnd(&gz);
|
|
return zerr_to_opensc(err);
|
|
}
|
|
|
|
int sc_decompress_alloc(u8** out, size_t* outLen, const u8* in, size_t inLen, int method)
|
|
{
|
|
if (in == NULL || out == NULL) {
|
|
return SC_ERROR_UNKNOWN_DATA_RECEIVED;
|
|
}
|
|
|
|
if (method == COMPRESSION_AUTO) {
|
|
method = detect_method(in, inLen);
|
|
if (method == COMPRESSION_UNKNOWN) {
|
|
return SC_ERROR_UNKNOWN_DATA_RECEIVED;
|
|
}
|
|
}
|
|
|
|
switch (method) {
|
|
case COMPRESSION_ZLIB:
|
|
return sc_decompress_zlib_alloc(out, outLen, in, inLen, 0);
|
|
case COMPRESSION_GZIP:
|
|
return sc_decompress_zlib_alloc(out, outLen, in, inLen, 1);
|
|
default:
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
|
}
|
|
}
|
|
#endif /* ENABLE_ZLIB */
|