/* * $Id: zlib_interface.c 289 2004-10-30 13:26:49Z kiza $ * JaguarFoundation * Copyright © 2004 René Puls * * Latest version: * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License version 2.1, as published by the Free Software Foundation. * * 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 * */ #include "zlib_interface.h" #include #include #include #include int JG_ZLIB_DEBUG = 0; struct gzip_header { unsigned char magic[2]; unsigned char method; unsigned char flags; unsigned char mtime[4]; unsigned char xfl; unsigned char os; }; struct gzip_footer { unsigned char crc32[4]; unsigned char size[4]; }; int jg_zlib_uncompress(void const *in_buf, int in_size, void **out_buf_ptr, int *out_size, int gzip) { char tmpstring[1024]; z_stream stream; char *out_buf = NULL; int out_buf_bytes = 0; char tmp_buf[4096]; int result; int new_bytes; /* Prepare the stream structure. */ stream.zalloc = NULL; stream.zfree = NULL; stream.opaque = NULL; stream.next_in = (void *)in_buf; stream.avail_in = in_size; stream.next_out = tmp_buf; stream.avail_out = sizeof tmp_buf; if (out_size != NULL) *out_size = 0; if (gzip) result = inflateInit2(&stream, MAX_WBITS + 32); /* UNTESTED */ else result = inflateInit2(&stream, -MAX_WBITS); if (result != 0) { if (JG_ZLIB_DEBUG) fprintf(stderr, "inflateInit2 failed: %d\n", result); return JG_ZLIB_ERROR_OLDVERSION; } do { /* Should be Z_FINISH? */ result = inflate(&stream, Z_NO_FLUSH); switch (result) { case Z_BUF_ERROR: if (stream.avail_in == 0) goto DONE; /* zlib bug */ case Z_ERRNO: case Z_NEED_DICT: case Z_MEM_ERROR: case Z_DATA_ERROR: case Z_VERSION_ERROR: inflateEnd(&stream); free(out_buf); if (JG_ZLIB_DEBUG) { snprintf (tmpstring, sizeof(tmpstring), "ERROR: zlib_uncompress: %d %s\n", result, stream.msg); fprintf(stderr, tmpstring); } return JG_ZLIB_ERROR_UNCOMPRESS; } if (stream.avail_out < sizeof tmp_buf) { /* Add the new uncompressed data to our output buffer. */ new_bytes = sizeof tmp_buf - stream.avail_out; out_buf = realloc(out_buf, out_buf_bytes + new_bytes); memcpy(out_buf + out_buf_bytes, tmp_buf, new_bytes); out_buf_bytes += new_bytes; stream.next_out = tmp_buf; stream.avail_out = sizeof tmp_buf; } else { /* For some reason, inflate() didn't write out a single byte. */ inflateEnd(&stream); free(out_buf); if (JG_ZLIB_DEBUG) fprintf(stderr, "ERROR: No output during decompression\n"); return JG_ZLIB_ERROR_NODATA; } } while (result != Z_STREAM_END); DONE: inflateEnd(&stream); /* Null-terminate the output buffer so it can be handled like a string. */ out_buf = realloc(out_buf, out_buf_bytes + 1); out_buf[out_buf_bytes] = 0; /* The returned size does NOT include the additionall null byte! */ if (out_size != NULL) *out_size = out_buf_bytes; *out_buf_ptr = out_buf; return 0; } /* Decompressed gzip,deflate compressed data. This is what the webservers usually send. */ int jg_gzip_uncompress(void const *in_buf, int in_size, void **out_buf_ptr, int *out_size) { char tmpstring[1024]; struct gzip_header const *header; char *data_start; int offset = sizeof *header; header = in_buf; if (out_size != NULL) *out_size = 0; if ((header->magic[0] != 0x1F) || (header->magic[1] != 0x8B)) { if (JG_ZLIB_DEBUG) fprintf(stderr, "ERROR: Invalid magic bytes for GZIP data\n"); return JG_ZLIB_ERROR_BAD_MAGIC; } if (header->method != 8) { if (JG_ZLIB_DEBUG) fprintf(stderr, "ERROR: Compression method is not deflate\n"); return JG_ZLIB_ERROR_BAD_METHOD; } if (header->flags != 0 && header->flags != 8) { if (JG_ZLIB_DEBUG) { snprintf (tmpstring, sizeof(tmpstring), "ERROR: Unsupported flags %d", header->flags); fprintf(stderr, "ERROR: %s\n", tmpstring); } return JG_ZLIB_ERROR_BAD_FLAGS; } if (header->flags & 8) { /* skip the file name */ while (offset < in_size) { if (((char *)in_buf)[offset] == 0) { offset++; break; } offset++; } } data_start = (char *)in_buf + offset; return jg_zlib_uncompress(data_start, in_size - offset - 8, out_buf_ptr, out_size, 0); }