1/*
2 * Copyright (c) 2005-2007 Rob Braun
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of Rob Braun nor the names of his contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 */
29/*
30 * 03-Apr-2005
31 * DRI: Rob Braun <bbraun@synack.net>
32 */
33/*
34 * Portions Copyright 2006, Apple Computer, Inc.
35 * Christopher Ryan <ryanc@apple.com>
36*/
37
38
39#include "config.h"
40#ifndef HAVE_ASPRINTF
41#include "asprintf.h"
42#endif
43#include <stdio.h>
44#include <stdlib.h>
45#include <string.h>
46#include <sys/types.h>
47#include <zlib.h>
48#include <errno.h>
49#include "xar.h"
50#include "filetree.h"
51#include "io.h"
52
53struct _gzip_context{
54	uint8_t		gzipcompressed;
55	uint64_t        count;
56	z_stream	z;
57};
58
59#define GZIP_CONTEXT(x) ((struct _gzip_context *)(*x))
60
61int xar_gzip_fromheap_done(xar_t x, xar_file_t f, xar_prop_t p, void **context) {
62
63	if( !context || !GZIP_CONTEXT(context) )
64		return 0;
65
66	if( GZIP_CONTEXT(context)->gzipcompressed){
67		inflateEnd(&GZIP_CONTEXT(context)->z);
68	}
69
70	/* free the context */
71	free(GZIP_CONTEXT(context));
72	*context = NULL;
73
74	return 0;
75}
76
77int xar_gzip_fromheap_in(xar_t x, xar_file_t f, xar_prop_t p, void **in, size_t *inlen, void **context) {
78	const char *opt;
79	void *out = NULL;
80	size_t outlen, offset = 0;
81	int r;
82	xar_prop_t tmpp;
83
84	/* on first run, we init the context and check the compression type */
85	if( !GZIP_CONTEXT(context) ) {
86		*context = calloc(1,sizeof(struct _gzip_context));
87
88		opt = NULL;
89		tmpp = xar_prop_pget(p, "encoding");
90		if( tmpp )
91			opt = xar_attr_pget(f, tmpp, "style");
92		if( !opt ) return 0;
93		if( strcmp(opt, "application/x-gzip") != 0 ) return 0;
94
95		inflateInit(&GZIP_CONTEXT(context)->z);
96		GZIP_CONTEXT(context)->gzipcompressed = 1;
97	}else if( !GZIP_CONTEXT(context)->gzipcompressed ){
98		/* once the context has been initialized, then we have already
99		   checked the compression type, so we need only check if we
100		   actually are compressed */
101		return 0;
102	}
103
104	outlen = *inlen;
105
106	GZIP_CONTEXT(context)->z.next_in = *in;
107	GZIP_CONTEXT(context)->z.avail_in = *inlen;
108	GZIP_CONTEXT(context)->z.next_out = out;
109	GZIP_CONTEXT(context)->z.avail_out = 0;
110
111	while( GZIP_CONTEXT(context)->z.avail_in != 0 ) {
112		outlen = outlen * 2;
113		out = realloc(out, outlen);
114		if( out == NULL ) abort();
115
116		GZIP_CONTEXT(context)->z.next_out = ((unsigned char *)out) + offset;
117		GZIP_CONTEXT(context)->z.avail_out = outlen - offset;
118
119		r = inflate(&(GZIP_CONTEXT(context)->z), Z_NO_FLUSH);
120		if( (r != Z_OK) && (r != Z_STREAM_END) ) {
121			xar_err_new(x);
122			xar_err_set_file(x, f);
123			xar_err_set_string(x, "Error decompressing file");
124			xar_err_callback(x, XAR_SEVERITY_FATAL, XAR_ERR_ARCHIVE_EXTRACTION);
125			return -1;
126		}
127		offset += outlen - offset - GZIP_CONTEXT(context)->z.avail_out;
128		if( (r == Z_STREAM_END) && (offset == 0) )
129			break;
130	}
131
132	free(*in);
133	*in = out;
134	*inlen = offset;
135	return 0;
136}
137
138int xar_gzip_toheap_done(xar_t x, xar_file_t f, xar_prop_t p, void **context) {
139	xar_prop_t tmpp;
140
141	if( GZIP_CONTEXT(context)->gzipcompressed){
142		deflateEnd(&GZIP_CONTEXT(context)->z);
143
144		if( GZIP_CONTEXT(context)->count ) {
145			tmpp = xar_prop_pset(f, p, "encoding", NULL);
146			if( tmpp )
147				xar_attr_pset(f, tmpp, "style", "application/x-gzip");
148		}
149	}
150
151	/* free the context */
152	free(GZIP_CONTEXT(context));
153	*context = NULL;
154
155	return 0;
156}
157
158int32_t xar_gzip_toheap_in(xar_t x, xar_file_t f, xar_prop_t p, void **in, size_t *inlen, void **context) {
159	void *out = NULL;
160	size_t outlen, offset = 0;
161	int r;
162	const char *opt;
163
164	/* on first run, we init the context and check the compression type */
165	if( !GZIP_CONTEXT(context) ) {
166		int level = Z_BEST_COMPRESSION;
167		*context = calloc(1,sizeof(struct _gzip_context));
168
169		opt = xar_opt_get(x, XAR_OPT_COMPRESSION);
170		if( !opt )
171			return 0;
172
173		if( strcmp(opt, XAR_OPT_VAL_GZIP) != 0 )
174			return 0;
175
176		opt = xar_opt_get(x, XAR_OPT_COMPRESSIONARG);
177		if( opt ) {
178			int tmp;
179			errno = 0;
180			tmp = strtol(opt, NULL, 10);
181			if( errno == 0 ) {
182				if( (level >= 0) && (level <= 9) )
183					level = tmp;
184			}
185		}
186
187		deflateInit(&GZIP_CONTEXT(context)->z, level);
188		GZIP_CONTEXT(context)->gzipcompressed = 1;
189		if( *inlen == 0 )
190			return 0;
191	}else if( !GZIP_CONTEXT(context)->gzipcompressed ){
192		/* once the context has been initialized, then we have already
193		checked the compression type, so we need only check if we
194		actually are compressed */
195		return 0;
196	}
197
198	outlen = *inlen/2;
199	if(outlen == 0) outlen = 1024;
200	GZIP_CONTEXT(context)->z.next_in = *in;
201	GZIP_CONTEXT(context)->z.avail_in = *inlen;
202	GZIP_CONTEXT(context)->z.next_out = out;
203	GZIP_CONTEXT(context)->z.avail_out = 0;
204
205	if( *inlen != 0 ) {
206		do {
207			outlen *= 2;
208			out = realloc(out, outlen);
209			if( out == NULL ) abort();
210
211			GZIP_CONTEXT(context)->z.next_out = ((unsigned char *)out) + offset;
212			GZIP_CONTEXT(context)->z.avail_out = outlen - offset;
213
214			r = deflate(&GZIP_CONTEXT(context)->z, Z_NO_FLUSH);
215			offset = outlen - GZIP_CONTEXT(context)->z.avail_out;
216		} while( r == Z_OK && GZIP_CONTEXT(context)->z.avail_in != 0 );
217	} else {
218		do {
219			outlen *= 2;
220			out = realloc(out, outlen);
221			if( out == NULL ) abort();
222
223			GZIP_CONTEXT(context)->z.next_out = ((unsigned char *)out) + offset;
224			GZIP_CONTEXT(context)->z.avail_out = outlen - offset;
225
226			r = deflate(&GZIP_CONTEXT(context)->z, Z_FINISH);
227			offset = outlen - GZIP_CONTEXT(context)->z.avail_out;
228		} while( r == Z_OK && r != Z_STREAM_END /* no-op */);
229	}
230
231	if( (r != Z_OK && r != Z_STREAM_END) ) {
232		xar_err_new(x);
233		xar_err_set_file(x, f);
234		xar_err_set_string(x, "Error compressing file");
235		xar_err_set_errno(x, r);
236		xar_err_callback(x, XAR_SEVERITY_FATAL, XAR_ERR_ARCHIVE_CREATION);
237		return -1;
238	}
239
240	free(*in);
241	*in = out;
242	GZIP_CONTEXT(context)->count += *inlen;
243	*inlen = offset;
244	return 0;
245}
246