1 /*
2 * Copyright (C) 1999, 2000 Bryan Burns
3 * Copyright (C) 2004 Johan Ceuppens
4 *
5 * Released under GNU GPL, read the file 'COPYING' for more information
6 */
8 /*
9 * TODO/FIXME:
10 * - configure #ifdefs should be enabled
11 * - move to cstdlib instead of stdlib.h etc.
12 * - remove exit functions
13 * - move to clean C++ code
14 * - windowsify
15 * - remove a few g_free/g_mallocs
16 * - unseekable files
17 * - move to LGPL by rewriting macros
18 * - crcs for compressed files
19 * - put in eof
20 */
22 #ifdef HAVE_CONFIG_H
23 # include "config.h"
24 #endif
27 //#ifdef STDC_HEADERS
28 //#endif
30 //#ifdef HAVE_UNISTD_H
31 //#endif
33 //#ifdef HAVE_SYS_PARAM_H
34 //#else
35 //#define MAXPATHLEN 1024
36 //#endif
38 //#ifdef HAVE_DIRENT_H
39 //#endif
41 //#ifdef HAVE_FCNTL_H
42 #include <fcntl.h>
43 //#endif
45 #include <cstring>
46 #include <string>
47 #include <cstdlib>
48 #include <glib.h>
49 #include <zlib.h>
51 #include "inkjar.h"
53 #include <fstream>
54 #ifdef WORDS_BIGENDIAN
56 #define L2BI(l) ((l & 0xff000000) >> 24) | \
57 ((l & 0x00ff0000) >> 8) | \
58 ((l & 0x0000ff00) << 8) | \
59 ((l & 0x000000ff) << 24);
61 #define L2BS(l) ((l & 0xff00) >> 8) | ((l & 0x00ff) << 8);
63 #endif
65 namespace Inkjar {
67 JarFile::JarFile(gchar const*new_filename)
68 {
69 _filename = g_strdup(new_filename);
70 _last_filename = NULL;
71 fd = -1;
72 }
74 //fixme: the following should probably just return a const gchar* and not
75 // use strdup
76 gchar *JarFile::get_last_filename() const
77 {
78 return (_last_filename != NULL ? g_strdup(_last_filename) : NULL);
79 }
81 JarFile::~JarFile()
82 {
83 if (_filename != NULL)
84 g_free(_filename);
85 if (_last_filename != NULL)
86 g_free(_last_filename);
87 }
89 bool JarFile::init_inflation()
90 {
91 memset(&_zs, 0, sizeof(z_stream));
93 _zs.zalloc = Z_NULL;
94 _zs.zfree = Z_NULL;
95 _zs.opaque = Z_NULL;
97 if(inflateInit2(&_zs, -15) != Z_OK) {
98 fprintf(stderr,"error initializing inflation!\n");
99 return false;
100 }
102 return true;
103 }
105 bool JarFile::open()
106 {
107 if ((fd = ::open(_filename, O_RDONLY)) < 0) {
108 fprintf(stderr, "open failed.\n");
109 return false;
110 }
111 if (!init_inflation())
112 return false;
114 return true;
115 }
117 bool JarFile::close()
118 {
119 if (fd >= 0 && !::close(fd)) {
120 inflateEnd(&_zs);
121 return true;
122 }
123 return false;
124 }
126 bool JarFile::read_signature()
127 {
128 guint8 *bytes = (guint8 *)g_malloc(sizeof(guint8) * 4);
129 if (!read(bytes, 4)) {
130 g_free(bytes);
131 return false;
132 }
134 guint32 signature = UNPACK_UB4(bytes, 0);
135 g_free(bytes);
137 #ifdef DEBUG
138 std::printf("signature is %x\n", signature);
139 #endif
141 if (signature == 0x08074b50) {
142 //skip data descriptor
143 bytes = (guint8 *)malloc(sizeof(guint8) * 12);
144 if (!read(bytes, 12)) {
145 g_free(bytes);
146 return false;
147 }
148 } else if (signature == 0x02014b50 || signature == 0x04034b50) {
149 return true;
150 } else {
151 return false;
152 }
153 return false;
154 }
156 guint32 JarFile::get_crc(guint8 *bytes, guint16 flags)
157 {
158 guint32 crc = 0;
159 //no data descriptor
160 if (!(flags & 0x0008)) {
161 crc = UNPACK_UB4(bytes, LOC_CRC);
163 #ifdef DEBUG
164 std::printf("CRC from file is %x\n", crc);
165 #endif
166 }
168 return crc;
169 }
171 guint8 *JarFile::read_filename(guint16 filename_length)
172 {
173 guint8 *filename = (guint8 *)g_malloc(sizeof(guint8)
174 * (filename_length+1));
175 if (!read(filename, filename_length)) {
176 g_free(filename);
177 return NULL;
178 }
179 filename[filename_length] = '\0';
181 #ifdef DEBUG
182 std::printf("Filename is %s\n", filename);
183 #endif
185 return filename;
186 }
188 bool JarFile::check_compression_method(guint16 method, guint16 flags)
189 {
190 return !(method != 8 && flags & 0x0008);
191 }
193 GByteArray *JarFile::get_next_file_contents()
194 {
195 guint8 *bytes;
196 GByteArray *gba = g_byte_array_new();
198 read_signature();
200 //get compressed size
201 bytes = (guint8 *)g_malloc(sizeof(guint8) * 30);
202 if (!read(bytes+4, 26)) {
203 g_free(bytes);
204 return NULL;
205 }
206 guint32 compressed_size = UNPACK_UB4(bytes, LOC_CSIZE);
207 guint16 filename_length = UNPACK_UB2(bytes, LOC_FNLEN);
208 guint16 eflen = UNPACK_UB2(bytes, LOC_EFLEN);
209 guint16 flags = UNPACK_UB2(bytes, LOC_EXTRA);
210 guint16 method = UNPACK_UB2(bytes, LOC_COMP);
212 if (filename_length == 0) {
213 g_byte_array_free(gba, TRUE);
214 if (_last_filename != NULL)
215 g_free(_last_filename);
216 _last_filename = NULL;
217 return NULL;
218 }
221 #ifdef DEBUG
222 std::printf("Compressed size is %u\n", compressed_size);
223 std::printf("Filename length is %hu\n", filename_length);
224 std::printf("Extra field length is %hu\n", eflen);
225 std::printf("Flags are %#hx\n", flags);
226 std::printf("Compression method is %#hx\n", method);
227 #endif
229 guint32 crc = get_crc(bytes, flags);
231 gchar *filename = (gchar *)read_filename(filename_length);
232 g_free(bytes);
234 if (filename == NULL)
235 return NULL;
237 if (_last_filename != NULL)
238 g_free(_last_filename);
239 _last_filename = filename;
241 //check if this is a directory and skip
243 char *c_ptr;
244 if ((c_ptr = std::strrchr(filename, '/')) != NULL) {
245 if (*(++c_ptr) == '\0') {
246 return NULL;
247 }
248 }
250 if (!check_compression_method(method, flags)) {
251 std::fprintf(stderr, "error in jar file\n");
252 return NULL;
253 }
255 if (method == 8 || flags & 0x0008) {
256 unsigned int file_length = 0;//uncompressed file length
257 lseek(fd, eflen, SEEK_CUR);
258 guint8 *file_data = get_compressed_file(compressed_size, file_length,
259 crc, flags);
260 if (file_data == NULL) {
261 g_byte_array_free(gba, FALSE);
262 return NULL;
263 }
264 g_byte_array_append(gba, file_data, file_length);
265 } else if (method == 0) {
266 guint8 *file_data = get_uncompressed_file(compressed_size, crc,
267 eflen, flags);
269 if (file_data == NULL) {
270 g_byte_array_free(gba, TRUE);
271 return NULL;
272 }
273 g_byte_array_append(gba, file_data, compressed_size);
274 } else {
275 lseek(fd, compressed_size+eflen, SEEK_CUR);
276 g_byte_array_free(gba, FALSE);
277 return NULL;
278 }
281 return gba;
282 }
284 guint8 *JarFile::get_uncompressed_file(guint32 compressed_size, guint32 crc,
285 guint16 eflen, guint16 flags)
286 {
287 GByteArray *gba = g_byte_array_new();
288 unsigned int out_a = 0;
289 unsigned int in_a = compressed_size;
290 guint8 *bytes;
291 guint32 crc2 = 0;
293 crc2 = crc32(crc2, NULL, 0);
295 bytes = (guint8 *)g_malloc(sizeof(guint8) * RDSZ);
296 while(out_a < compressed_size){
297 unsigned int nbytes = (in_a > RDSZ ? RDSZ : in_a);
299 if (!(nbytes = read(bytes, nbytes))) {
300 g_free(bytes);
301 return NULL;
302 }
304 crc2 = crc32(crc2, (Bytef*)bytes, nbytes);
306 g_byte_array_append (gba, bytes, nbytes);
307 out_a += nbytes;
308 in_a -= nbytes;
310 #ifdef DEBUG
311 std::printf("%d bytes written\n", out_a);
312 #endif
313 }
314 lseek(fd, eflen, SEEK_CUR);
315 g_free(bytes);
317 if (!check_crc(crc, crc2, flags)) {
318 bytes = gba->data;
319 g_byte_array_free(gba, FALSE);//FALSE argument does not free actual data
320 return NULL;
321 }
323 return bytes;
324 }
326 int JarFile::read(guint8 *buf, int count)
327 {
328 int nbytes;
329 if ((nbytes = ::read(fd, buf, count)) != count) {
330 fprintf(stderr, "read error\n");
331 exit(1);
332 return 0;
333 }
334 return nbytes;
335 }
337 /* FIXME: this could probably use ZlibBuffer */
338 guint8 *JarFile::get_compressed_file(guint32 compressed_size,
339 unsigned int& file_length,
340 guint32 oldcrc, guint16 flags)
341 {
342 if (compressed_size == 0)
343 return NULL;
345 guint8 in_buffer[RDSZ];
346 guint8 out_buffer[RDSZ];
347 int nbytes;
348 unsigned int leftover_in = compressed_size;
349 GByteArray *gba = g_byte_array_new();
351 _zs.avail_in = 0;
352 guint32 crc = crc32(0, Z_NULL, 0);
354 do {
356 if (!_zs.avail_in) {
358 if ((nbytes = ::read(fd, in_buffer,
359 (leftover_in < RDSZ ? leftover_in : RDSZ)))
360 < 0) {
361 fprintf(stderr, "jarfile read error");
362 }
363 _zs.avail_in = nbytes;
364 _zs.next_in = in_buffer;
365 crc = crc32(crc, in_buffer, _zs.avail_in);
366 leftover_in -= RDSZ;
367 }
368 _zs.next_out = out_buffer;
369 _zs.avail_out = RDSZ;
371 int ret = inflate(&_zs, Z_NO_FLUSH);
372 if (RDSZ != _zs.avail_out) {
373 unsigned int tmp_len = RDSZ - _zs.avail_out;
374 guint8 *tmp_bytes = (guint8 *)g_malloc(sizeof(guint8)
375 * tmp_len);
376 memcpy(tmp_bytes, out_buffer, tmp_len);
377 g_byte_array_append(gba, tmp_bytes, tmp_len);
378 }
380 if (ret == Z_STREAM_END) {
381 break;
382 }
383 if (ret != Z_OK)
384 std::printf("decompression error %d\n", ret);
385 } while (_zs.total_in < compressed_size);
387 file_length = _zs.total_out;
388 #ifdef DEBUG
389 std::printf("done inflating\n");
390 std::printf("%d bytes left over\n", _zs.avail_in);
391 std::printf("CRC is %x\n", crc);
392 #endif
394 guint8 *ret_bytes;
395 if (check_crc(oldcrc, crc, flags) && gba->len > 0)
396 ret_bytes = gba->data;
397 else
398 ret_bytes = NULL;
399 g_byte_array_free(gba, FALSE);
401 inflateReset(&_zs);
402 return ret_bytes;
403 }
405 bool JarFile::check_crc(guint32 oldcrc, guint32 crc, guint16 flags)
406 {
407 //fixme: does not work yet
409 if(flags & 0x0008) {
410 guint8 *bytes = (guint8 *)g_malloc(sizeof(guint8) * 16);
411 if (!read(bytes, 16)) {
412 g_free(bytes);
413 return false;
414 }
416 guint32 signature = UNPACK_UB4(bytes, 0);
417 g_free(bytes);
418 if(signature != 0x08074b50) {
419 fprintf(stderr, "missing data descriptor!\n");
420 }
422 crc = UNPACK_UB4(bytes, 4);
424 }
425 if (oldcrc != crc) {
426 #ifdef DEBUG
427 std::fprintf(stderr, "Error! CRCs do not match! Got %x, expected %x\n",
428 oldcrc, crc);
429 #endif
430 }
431 return true;
432 }
434 JarFile::JarFile(JarFile const& rhs)
435 {
436 *this = rhs;
437 }
439 JarFile& JarFile::operator=(JarFile const& rhs)
440 {
441 if (this == &rhs)
442 return *this;
444 _zs = rhs._zs;//fixme
445 if (_filename == NULL)
446 _filename = NULL;
447 else
448 _filename = g_strdup(rhs._filename);
449 if (_last_filename == NULL)
450 _last_filename = NULL;
451 else
452 _last_filename = g_strdup(rhs._last_filename);
453 fd = rhs.fd;
455 return *this;
456 }
459 /////////////////////////
460 // JarFileReader //
461 /////////////////////////
463 GByteArray *JarFileReader::get_next_file()
464 {
465 if (_state == CLOSED) {
466 _jarfile.open();
467 _state = OPEN;
468 }
470 return _jarfile.get_next_file_contents();
471 }
473 JarFileReader& JarFileReader::operator=(JarFileReader const& rhs)
474 {
475 if (&rhs == this)
476 return *this;
478 _jarfile = rhs._jarfile;
479 _state = rhs._state;
481 return *this;
482 }
484 /*
485 * If the filename gets reset, a jarfile object gets generated again,
486 * ready to be opened for reading.
487 */
488 void JarFileReader::set_filename(gchar const *new_filename)
489 {
490 _jarfile.close();
491 _jarfile = JarFile(new_filename);
492 }
494 void JarFileReader::set_jarfile(JarFile const& new_jarfile)
495 {
496 _jarfile = new_jarfile;
497 }
499 JarFileReader::JarFileReader(JarFileReader const& rhs)
500 {
501 *this = rhs;
502 }
504 } // namespace Inkjar
507 #if 0 //testing code
508 #include "jar.h"
509 /*
510 * This program writes all the files from a jarfile to stdout and inflates
511 * where needed.
512 */
513 int main(int argc, char *argv[])
514 {
515 gchar *filename;
516 if (argc < 2) {
517 filename = "./ide.jar\0";
518 } else {
519 filename = argv[1];
520 }
522 Inkjar::JarFileReader jar_file_reader(filename);
524 for (;;) {
525 GByteArray *gba = jar_file_reader.get_next_file();
526 if (gba == NULL) {
527 char *c_ptr;
528 gchar *last_filename = jar_file_reader.get_last_filename();
529 if (last_filename == NULL)
530 break;
531 if ((c_ptr = std::strrchr(last_filename, '/')) != NULL) {
532 if (*(++c_ptr) == '\0') {
533 g_free(last_filename);
534 continue;
535 }
536 }
537 } else if (gba->len > 0)
538 ::write(1, gba->data, gba->len);
539 else
540 break;
541 }
542 return 0;
543 }
544 #endif