/*
 * bonobo-moniker-gunzip.c: gunzip based Moniker
 *
 * Author:
 *   Joe Shaw (joe@helixcode.com)
 *
 * Copyright (c) 2000 Helix Code, Inc.
 */
#include <config.h>
#include <zlib.h>
#include <gnome.h>
#include <liboaf/liboaf.h>
#include <bonobo.h>
#include <bonobo/bonobo-stream-memory.h>
#include <bonobo/bonobo-moniker-extender.h>

#include "bonobo-moniker-gunzip.h"

/* Count number of bytes to skip at start of buf */
static int gz_magic [2] = {0x1f, 0x8b};
/* gzip flag byte */
#define GZ_ASCII_FLAG   0x01 /* bit 0 set: file probably ascii text */
#define GZ_HEAD_CRC     0x02 /* bit 1 set: header CRC present */
#define GZ_EXTRA_FIELD  0x04 /* bit 2 set: extra field present */
#define GZ_ORIG_NAME    0x08 /* bit 3 set: original file name present */
#define GZ_COMMENT      0x10 /* bit 4 set: file comment present */
#define GZ_RESERVED     0xE0 /* bits 5..7: reserved */

static int
count_gzip_header (guint8 *buf, guint32 input_length)
{
	int method, flags;
	guint8 *s = buf;
	guint32 left_len = input_length;
	
	if (left_len < 4)
		return -1;

	if (*s++ != gz_magic [0] || *s++ != gz_magic[1])
		return -2;
	
	method = *s++;
	flags = *s++;
	left_len -= 4;
	
	if (method != Z_DEFLATED || (flags & GZ_RESERVED) != 0) {
		/* If it's not deflated, or the reserved isn't 0 */
		return -3;
	}
	
	/* Skip time, xflags, OS code */
	if (left_len < 6)
		return -4;
	s += 6;
	left_len -= 6;
	
	if (flags & GZ_EXTRA_FIELD) {
		unsigned int len;
		if (left_len < 2)
			return -5;
		len = (unsigned int) (*s++);
		len += ((unsigned int) (*s++)) << 8;
		if (left_len < len)
			return -6;
		s += len;
		left_len -= len;
	}
	
	/* Skip filename */
	if (flags & GZ_ORIG_NAME) {
		while (--left_len != 0 && *s++ != '\0') ;
		if (left_len == 0)
			return -7;
	}
	/* Skip comment */
	if (flags & GZ_COMMENT) {
		while (--left_len != 0 && *s++ != '\0') ;
		if (left_len == 0)
			return -7;
	}
	/* Skip CRC */
	if (flags & GZ_HEAD_CRC) {
		if (left_len < 2)
			return -7;
		s += 2;
		left_len -= 2;
	}
	
	return input_length - left_len;
}

static char *
uncompress_memory (char *input_buffer, int size, int *out_size)
{
	z_stream zs;
	gchar *outbuf = NULL;
	GByteArray *data;
	int data_len = 0;
	int zret;
	int gzip_hdr;

	g_return_val_if_fail(input_buffer, NULL);

	data = g_byte_array_new ();

	gzip_hdr = count_gzip_header (input_buffer, size);
	if (gzip_hdr < 0) {
		/* This is not a gzipped stream. Just return the stuff */
		g_byte_array_free(data, FALSE);
		outbuf = g_memdup(input_buffer, size);
		if (out_size)
			*out_size = size;
		return outbuf;
	}

	zs.next_in = input_buffer + gzip_hdr;
	zs.avail_in = size - gzip_hdr;
	zs.zalloc = NULL;
	zs.zfree = NULL;
	zs.opaque = NULL;
	
	outbuf = g_malloc (10000);
	zs.next_out = outbuf;
	zs.avail_out = 10000;

	/* Negative inflateinit is magic to tell zlib that there is no
	 * zlib header */
	inflateInit2 (&zs, -MAX_WBITS);

	while (1) {
		zret = inflate (&zs, Z_SYNC_FLUSH);
		if (zret != Z_OK && zret != Z_STREAM_END)
			break;

		g_byte_array_append(data, outbuf, 10000 - zs.avail_out);
		data_len += 10000 - zs.avail_out;
		zs.next_out = outbuf;
		zs.avail_out = 10000;

		if (zret == Z_STREAM_END)
			break;
	}
	
	if (zret != Z_STREAM_END)
		g_warning ("libz inflate failed! (%d)", zret);
	
	inflateEnd (&zs);

	g_free (outbuf);

	outbuf = data->data;
	if (out_size)
		*out_size = data->len;
	g_byte_array_free(data, FALSE);

	return outbuf;
}

static Bonobo_Stream
gunzip_resolve_stream (BonoboMoniker               *moniker,
		       const Bonobo_ResolveOptions *options,
		       CORBA_Environment           *ev)
{
	Bonobo_Moniker parent;
	Bonobo_Stream in_stream;
	BonoboStream *memstream;
	Bonobo_Stream out_stream;
	int len;
	Bonobo_Stream_iobuf *buffer;
	gboolean cont;
	char *ob;
	GByteArray *data;

	parent = bonobo_moniker_get_parent (moniker, ev);

	if (BONOBO_EX (ev) || parent == CORBA_OBJECT_NIL)
		return CORBA_OBJECT_NIL;

	in_stream = Bonobo_Moniker_resolve (
		parent, options, "IDL:Bonobo/Stream:1.0", ev);
		
	if (BONOBO_EX (ev))
		goto return_unref_parent;
		
	if (in_stream == CORBA_OBJECT_NIL) {
		g_warning ("Failed to obtain a stream from the parent");
		goto return_unref_parent;
	}
		
	/* This is necessary because while there is a 
	   bonobo_stream_client_read_string, there isn't any
	   bonobo_stream_client_read, and this compressed data isn't a
	   string. */
	data = g_byte_array_new ();
	do {
		Bonobo_Stream_read (in_stream, 1, &buffer, ev);
			
		if (BONOBO_EX (ev)) {
			g_byte_array_free (data, TRUE);
			goto return_unref_stream;
		}
		cont = buffer->_length ? TRUE : FALSE;
		if (buffer->_length) {
			data = g_byte_array_append (
				data, buffer->_buffer, buffer->_length);
		}
		CORBA_free (buffer);
	} while (cont);
		
	/* This function will return a memdup of the data we pass to it if it
	   can't decode the header. */
	ob = uncompress_memory (data->data, data->len, &len);
	g_byte_array_free (data, TRUE);
		
	memstream = bonobo_stream_mem_create (ob, len, TRUE, FALSE);
	g_free (ob);
	out_stream = BONOBO_OBJREF (memstream);

	bonobo_object_release_unref (in_stream, ev);
	bonobo_object_release_unref (parent, ev);
		
	return out_stream;
		
 return_unref_stream:
	bonobo_object_release_unref (in_stream, ev);

 return_unref_parent:
	bonobo_object_release_unref (parent, ev);
	return CORBA_OBJECT_NIL;
}

static Bonobo_Unknown
gunzip_resolve (BonoboMoniker *moniker,
		const Bonobo_ResolveOptions *options,
		const CORBA_char *requested_interface,
		CORBA_Environment *ev)
{
	g_warning ("Going to resolve the gunzip now");

	if (!strcmp (requested_interface, "IDL:Bonobo/Stream:1.0"))
		return gunzip_resolve_stream (moniker, options, ev);
	
	return bonobo_moniker_use_extender (
		"OAFIID:Bonobo_MonikerExtender_stream",
		moniker, options, requested_interface, ev);
}

static BonoboObject *
bonobo_moniker_gunzip_factory (BonoboGenericFactory *this, void *closure)
{
	return BONOBO_OBJECT (bonobo_moniker_simple_new ("gunzip:", 
							 gunzip_resolve));
}

BONOBO_OAF_FACTORY ("OAFIID:Bonobo_Moniker_gzip_Factory",
		    "gunzip-moniker", VERSION,
		    bonobo_moniker_gunzip_factory,
		    NULL)



syntax highlighted by Code2HTML, v. 0.9.1