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 <errno.h>
48#ifdef HAVE_LIBBZ2
49#include <bzlib.h>
50#endif
51#include "xar.h"
52#include "filetree.h"
53#include "io.h"
54
55#ifdef HAVE_LIBBZ2
56
57struct _bzip_context{
58	uint8_t		bzipcompressed;
59	bz_stream	bz;
60};
61
62
63#define BZIP2_CONTEXT(x) ((struct _bzip_context *)(*x))
64#endif
65
66int xar_bzip_fromheap_done(xar_t x, xar_file_t f, xar_prop_t p, void **context) {
67#ifdef HAVE_LIBBZ2
68
69	if( !context || !BZIP2_CONTEXT(context) )
70		return 0;
71
72	if( BZIP2_CONTEXT(context)->bzipcompressed){
73		BZ2_bzDecompressEnd(&BZIP2_CONTEXT(context)->bz);
74	}
75
76	/* free the context */
77	free(BZIP2_CONTEXT(context));
78	*context = NULL;
79
80#endif /* HAVE_LIBBZ2 */
81	return 0;
82}
83
84int xar_bzip_fromheap_in(xar_t x, xar_file_t f, xar_prop_t p, void **in, size_t *inlen, void **context) {
85	const char *opt;
86	xar_prop_t tmpp;
87#ifdef HAVE_LIBBZ2
88	void *out = NULL;
89	size_t outlen, offset = 0;
90	int r;
91
92	/* on first run, we init the context and check the compression type */
93	if( !BZIP2_CONTEXT(context) ) {
94		*context = calloc(1,sizeof(struct _bzip_context));
95
96		opt = NULL;
97		tmpp = xar_prop_pget(p, "encoding");
98		if( tmpp )
99			opt = xar_attr_pget(f, tmpp, "style");
100		if( !opt ) return 0;
101		if( strcmp(opt, "application/x-bzip2") != 0 ) return 0;
102
103		BZ2_bzDecompressInit(&BZIP2_CONTEXT(context)->bz, 0, 0);
104		BZIP2_CONTEXT(context)->bzipcompressed = 1;
105		if( *inlen == 0 )
106			return 0;
107	}else if( !(BZIP2_CONTEXT(context)->bzipcompressed) ){
108		/* once the context has been initialized, then we have already
109		   checked the compression type, so we need only check if we
110		   actually are compressed */
111		return 0;
112	}
113
114	outlen = *inlen;
115
116	BZIP2_CONTEXT(context)->bz.next_in = *in;
117	BZIP2_CONTEXT(context)->bz.avail_in = *inlen;
118	BZIP2_CONTEXT(context)->bz.next_out = out;
119	BZIP2_CONTEXT(context)->bz.avail_out = 0;
120
121	while( BZIP2_CONTEXT(context)->bz.avail_in != 0 ) {
122		outlen = outlen * 2;
123		out = realloc(out, outlen);
124		if( out == NULL ) abort();
125
126		BZIP2_CONTEXT(context)->bz.next_out = ((char *)out) + offset;
127		BZIP2_CONTEXT(context)->bz.avail_out = outlen - offset;
128
129		r = BZ2_bzDecompress(&BZIP2_CONTEXT(context)->bz);
130		if( (r != BZ_OK) && (r != BZ_STREAM_END) ) {
131			xar_err_new(x);
132			xar_err_set_file(x, f);
133			xar_err_set_string(x, "Error decompressing file");
134			xar_err_callback(x, XAR_SEVERITY_FATAL, XAR_ERR_ARCHIVE_EXTRACTION);
135			return -1;
136		}
137		offset += outlen - offset - BZIP2_CONTEXT(context)->bz.avail_out;
138		if( (r == BZ_STREAM_END) && (offset == 0) )
139			break;
140	}
141
142	free(*in);
143	*in = out;
144	*inlen = offset;
145#else
146	opt = NULL;
147	tmpp = xar_prop_pget(p, "encoding");
148	if( tmpp )
149		opt = xar_attr_pget(f, tmpp, "style");
150	if( !opt ) return 0;
151	if( strcmp(opt, "application/x-bzip2") != 0 ) return 0;
152	xar_err_new(x);
153	xar_err_set_file(x, f);
154	xar_err_set_errno(x, 0);
155	xar_err_set_string(x, "bzip2 support not compiled in.");
156	xar_err_callback(x, XAR_SEVERITY_FATAL, XAR_ERR_ARCHIVE_EXTRACTION);
157
158#endif /* HAVE_LIBBZ2 */
159	return 0;
160}
161
162int xar_bzip_toheap_done(xar_t x, xar_file_t f, xar_prop_t p, void **context) {
163#ifdef HAVE_LIBBZ2
164	xar_prop_t tmpp;
165
166	if( BZIP2_CONTEXT(context)->bzipcompressed){
167		BZ2_bzCompressEnd(&BZIP2_CONTEXT(context)->bz);
168
169		tmpp = xar_prop_pset(f, p, "encoding", NULL);
170		if( tmpp )
171			xar_attr_pset(f, tmpp, "style", "application/x-bzip2");
172	}
173
174	/* free the context */
175	free(BZIP2_CONTEXT(context));
176	*context = NULL;
177
178#endif /* HAVE_LIBBZ2 */
179	return 0;
180}
181
182int32_t xar_bzip_toheap_in(xar_t x, xar_file_t f, xar_prop_t p, void **in, size_t *inlen, void **context) {
183	const char *opt;
184#ifdef HAVE_LIBBZ2
185	void *out = NULL;
186	size_t outlen, offset = 0;
187	int r;
188
189	/* on first run, we init the context and check the compression type */
190	if( !BZIP2_CONTEXT(context) ) {
191		int level = 9;
192		*context = calloc(1,sizeof(struct _bzip_context));
193
194		opt = xar_opt_get(x, XAR_OPT_COMPRESSION);
195		if( !opt )
196			return 0;
197
198		if( strcmp(opt, XAR_OPT_VAL_BZIP) != 0 )
199			return 0;
200
201		opt = xar_opt_get(x, XAR_OPT_COMPRESSIONARG);
202		if( opt ) {
203			int tmp;
204			errno = 0;
205			tmp = strtol(opt, NULL, 10);
206			if( errno == 0 ) {
207				if( (level >= 0) && (level <= 9) )
208					level = tmp;
209			}
210		}
211
212		BZ2_bzCompressInit(&BZIP2_CONTEXT(context)->bz, level, 0, 30);
213		BZIP2_CONTEXT(context)->bzipcompressed = 1;
214	}else if( !BZIP2_CONTEXT(context)->bzipcompressed ){
215		/* once the context has been initialized, then we have already
216		checked the compression type, so we need only check if we
217		actually are compressed */
218		return 0;
219	}
220
221	outlen = *inlen/2;
222	if(outlen == 0) outlen = 1024;
223	BZIP2_CONTEXT(context)->bz.next_in = *in;
224	BZIP2_CONTEXT(context)->bz.avail_in = *inlen;
225	BZIP2_CONTEXT(context)->bz.next_out = out;
226	BZIP2_CONTEXT(context)->bz.avail_out = 0;
227
228	if( *inlen != 0 ) {
229		do {
230			outlen *= 2;
231			out = realloc(out, outlen);
232			if( out == NULL ) abort();
233
234			BZIP2_CONTEXT(context)->bz.next_out = ((char *)out) + offset;
235			BZIP2_CONTEXT(context)->bz.avail_out = outlen - offset;
236
237			r = BZ2_bzCompress(&BZIP2_CONTEXT(context)->bz, BZ_RUN);
238			offset = outlen - BZIP2_CONTEXT(context)->bz.avail_out;
239		} while( r == BZ_RUN_OK && BZIP2_CONTEXT(context)->bz.avail_in != 0 );
240	} else {
241		do {
242			outlen *= 2;
243			out = realloc(out, outlen);
244			if( out == NULL ) abort();
245
246			BZIP2_CONTEXT(context)->bz.next_out = ((char *)out) + offset;
247			BZIP2_CONTEXT(context)->bz.avail_out = outlen - offset;
248
249			r = BZ2_bzCompress(&BZIP2_CONTEXT(context)->bz, BZ_FINISH);
250			offset = outlen - BZIP2_CONTEXT(context)->bz.avail_out;
251		} while( (r == BZ_FINISH_OK) && (r != BZ_STREAM_END /* no-op */) );
252	}
253
254	if( (r != BZ_RUN_OK && r != BZ_STREAM_END && r != BZ_SEQUENCE_ERROR) ) {
255		xar_err_new(x);
256		xar_err_set_file(x, f);
257		xar_err_set_string(x, "Error compressing file");
258		xar_err_set_errno(x, r);
259		xar_err_callback(x, XAR_SEVERITY_FATAL, XAR_ERR_ARCHIVE_CREATION);
260		return -1;
261	}
262
263	free(*in);
264	*in = out;
265	*inlen = offset;
266#else
267	opt = xar_opt_get(x, XAR_OPT_COMPRESSION);
268	if( !opt )
269		return 0;
270	if( strcmp(opt, XAR_OPT_VAL_BZIP) != 0 )
271		return 0;
272	xar_err_new(x);
273	xar_err_set_file(x, f);
274	xar_err_set_errno(x, 0);
275	xar_err_set_string(x, "bzip2 support not compiled in.");
276	xar_err_callback(x, XAR_SEVERITY_FATAL, XAR_ERR_ARCHIVE_CREATION);
277#endif /* HAVE_LIBBZ2 */
278	return 0;
279}
280