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
46 #include <glib.h>
48 #include "jar.h"
50 #include <fstream>
51 #ifdef WORDS_BIGENDIAN
53 #define L2BI(l) ((l & 0xff000000) >> 24) | \
54 ((l & 0x00ff0000) >> 8) | \
55 ((l & 0x0000ff00) << 8) | \
56 ((l & 0x000000ff) << 24);
58 #define L2BS(l) ((l & 0xff00) >> 8) | ((l & 0x00ff) << 8);
60 #endif
62 namespace Inkjar {
64 JarFile::JarFile(gchar const*new_filename)
65 {
66 _filename = strdup(new_filename);
67 _last_filename = NULL;
68 fd = -1;
69 }
71 //fixme: the following should probably just return a const gchar* and not
72 // use strdup
73 gchar *JarFile::get_last_filename() const
74 {
75 return (_last_filename != NULL ? strdup(_last_filename) : NULL);
76 }
78 JarFile::~JarFile()
79 {
80 if (_filename != NULL)
81 g_free(_filename);
82 if (_last_filename != NULL)
83 g_free(_last_filename);
84 }
86 bool JarFile::init_inflation()
87 {
88 memset(&_zs, 0, sizeof(z_stream));
90 _zs.zalloc = Z_NULL;
91 _zs.zfree = Z_NULL;
92 _zs.opaque = Z_NULL;
94 if(inflateInit2(&_zs, -15) != Z_OK) {
95 fprintf(stderr,"error initializing inflation!\n");
96 return false;
97 }
99 return true;
100 }
102 bool JarFile::open()
103 {
104 if ((fd = ::open(_filename, O_RDONLY)) < 0) {
105 fprintf(stderr, "open failed.\n");
106 return false;
107 }
108 if (!init_inflation())
109 return false;
111 return true;
112 }
114 bool JarFile::close()
115 {
116 if (fd >= 0 && !::close(fd)) {
117 inflateEnd(&_zs);
118 return true;
119 }
120 return false;
121 }
123 bool JarFile::read_signature()
124 {
125 guint8 *bytes = (guint8 *)g_malloc(sizeof(guint8) * 4);
126 if (!read(bytes, 4)) {
127 g_free(bytes);
128 return false;
129 }
131 guint32 signature = UNPACK_UB4(bytes, 0);
132 g_free(bytes);
134 #ifdef DEBUG
135 std::printf("signature is %x\n", signature);
136 #endif
138 if (signature == 0x08074b50) {
139 //skip data descriptor
140 bytes = (guint8 *)malloc(sizeof(guint8) * 12);
141 if (!read(bytes, 12)) {
142 g_free(bytes);
143 return false;
144 }
145 } else if (signature == 0x02014b50 || signature == 0x04034b50) {
146 return true;
147 } else {
148 return false;
149 }
150 return false;
151 }
153 guint32 JarFile::get_crc(guint8 *bytes, guint16 flags)
154 {
155 guint32 crc = 0;
156 //no data descriptor
157 if (!(flags & 0x0008)) {
158 crc = UNPACK_UB4(bytes, LOC_CRC);
160 #ifdef DEBUG
161 std::printf("CRC from file is %x\n", crc);
162 #endif
163 }
165 return crc;
166 }
168 guint8 *JarFile::read_filename(guint16 filename_length)
169 {
170 guint8 *filename = (guint8 *)g_malloc(sizeof(guint8)
171 * (filename_length+1));
172 if (!read(filename, filename_length)) {
173 g_free(filename);
174 return NULL;
175 }
176 filename[filename_length] = '\0';
178 #ifdef DEBUG
179 std::printf("Filename is %s\n", filename);
180 #endif
182 return filename;
183 }
185 bool JarFile::check_compression_method(guint16 method, guint16 flags)
186 {
187 return !(method != 8 && flags & 0x0008);
188 }
190 GByteArray *JarFile::get_next_file_contents()
191 {
192 guint8 *bytes;
193 GByteArray *gba = g_byte_array_new();
195 read_signature();
197 //get compressed size
198 bytes = (guint8 *)g_malloc(sizeof(guint8) * 30);
199 if (!read(bytes+4, 26)) {
200 g_free(bytes);
201 return NULL;
202 }
203 guint32 compressed_size = UNPACK_UB4(bytes, LOC_CSIZE);
204 guint16 filename_length = UNPACK_UB2(bytes, LOC_FNLEN);
205 guint16 eflen = UNPACK_UB2(bytes, LOC_EFLEN);
206 guint16 flags = UNPACK_UB2(bytes, LOC_EXTRA);
207 guint16 method = UNPACK_UB2(bytes, LOC_COMP);
209 if (filename_length == 0) {
210 g_byte_array_free(gba, TRUE);
211 if (_last_filename != NULL)
212 g_free(_last_filename);
213 _last_filename = NULL;
214 return NULL;
215 }
218 #ifdef DEBUG
219 std::printf("Compressed size is %u\n", compressed_size);
220 std::printf("Filename length is %hu\n", filename_length);
221 std::printf("Extra field length is %hu\n", eflen);
222 std::printf("Flags are %#hx\n", flags);
223 std::printf("Compression method is %#hx\n", method);
224 #endif
226 guint32 crc = get_crc(bytes, flags);
228 gchar *filename = (gchar *)read_filename(filename_length);
229 g_free(bytes);
231 if (filename == NULL)
232 return NULL;
234 if (_last_filename != NULL)
235 g_free(_last_filename);
236 _last_filename = filename;
238 //check if this is a directory and skip
240 char *c_ptr;
241 if ((c_ptr = std::strrchr(filename, '/')) != NULL) {
242 if (*(++c_ptr) == '\0') {
243 return NULL;
244 }
245 }
247 if (!check_compression_method(method, flags)) {
248 std::fprintf(stderr, "error in jar file\n");
249 return NULL;
250 }
252 if (method == 8 || flags & 0x0008) {
253 unsigned int file_length = 0;//uncompressed file length
254 lseek(fd, eflen, SEEK_CUR);
255 guint8 *file_data = get_compressed_file(compressed_size, file_length,
256 crc, flags);
257 if (file_data == NULL) {
258 g_byte_array_free(gba, FALSE);
259 return NULL;
260 }
261 g_byte_array_append(gba, file_data, file_length);
262 } else if (method == 0) {
263 guint8 *file_data = get_uncompressed_file(compressed_size, crc,
264 eflen, flags);
266 if (file_data == NULL) {
267 g_byte_array_free(gba, TRUE);
268 return NULL;
269 }
270 g_byte_array_append(gba, file_data, compressed_size);
271 } else {
272 lseek(fd, compressed_size+eflen, SEEK_CUR);
273 g_byte_array_free(gba, FALSE);
274 return NULL;
275 }
278 return gba;
279 }
281 guint8 *JarFile::get_uncompressed_file(guint32 compressed_size, guint32 crc,
282 guint16 eflen, guint16 flags)
283 {
284 GByteArray *gba = g_byte_array_new();
285 unsigned int out_a = 0;
286 unsigned int in_a = compressed_size;
287 guint8 *bytes;
288 guint32 crc2 = 0;
290 crc2 = crc32(crc2, NULL, 0);
292 bytes = (guint8 *)g_malloc(sizeof(guint8) * RDSZ);
293 while(out_a < compressed_size){
294 unsigned int nbytes = (in_a > RDSZ ? RDSZ : in_a);
296 if (!(nbytes = read(bytes, nbytes))) {
297 g_free(bytes);
298 return NULL;
299 }
301 crc2 = crc32(crc2, (Bytef*)bytes, nbytes);
303 g_byte_array_append (gba, bytes, nbytes);
304 out_a += nbytes;
305 in_a -= nbytes;
307 #ifdef DEBUG
308 std::printf("%d bytes written\n", out_a);
309 #endif
310 }
311 lseek(fd, eflen, SEEK_CUR);
312 g_free(bytes);
314 if (!check_crc(crc, crc2, flags)) {
315 bytes = gba->data;
316 g_byte_array_free(gba, FALSE);//FALSE argument does not free actual data
317 return NULL;
318 }
320 return bytes;
321 }
323 int JarFile::read(guint8 *buf, int count)
324 {
325 int nbytes;
326 if ((nbytes = ::read(fd, buf, count)) != count) {
327 fprintf(stderr, "read error\n");
328 exit(1);
329 return 0;
330 }
331 return nbytes;
332 }
334 /* FIXME: this could probably use ZlibBuffer */
335 guint8 *JarFile::get_compressed_file(guint32 compressed_size,
336 unsigned int& file_length,
337 guint32 oldcrc, guint16 flags)
338 {
339 if (compressed_size == 0)
340 return NULL;
342 guint8 in_buffer[RDSZ];
343 guint8 out_buffer[RDSZ];
344 int nbytes;
345 unsigned int leftover_in = compressed_size;
346 GByteArray *gba = g_byte_array_new();
348 _zs.avail_in = 0;
349 guint32 crc = crc32(0, Z_NULL, 0);
351 do {
353 if (!_zs.avail_in) {
355 if ((nbytes = ::read(fd, in_buffer,
356 (leftover_in < RDSZ ? leftover_in : RDSZ)))
357 < 0) {
358 fprintf(stderr, "jarfile read error");
359 }
360 _zs.avail_in = nbytes;
361 _zs.next_in = in_buffer;
362 crc = crc32(crc, in_buffer, _zs.avail_in);
363 leftover_in -= RDSZ;
364 }
365 _zs.next_out = out_buffer;
366 _zs.avail_out = RDSZ;
368 int ret = inflate(&_zs, Z_NO_FLUSH);
369 if (RDSZ != _zs.avail_out) {
370 unsigned int tmp_len = RDSZ - _zs.avail_out;
371 guint8 *tmp_bytes = (guint8 *)g_malloc(sizeof(guint8)
372 * tmp_len);
373 memcpy(tmp_bytes, out_buffer, tmp_len);
374 g_byte_array_append(gba, tmp_bytes, tmp_len);
375 }
377 if (ret == Z_STREAM_END) {
378 break;
379 }
380 if (ret != Z_OK)
381 std::printf("decompression error %d\n", ret);
382 } while (_zs.total_in < compressed_size);
384 file_length = _zs.total_out;
385 #ifdef DEBUG
386 std::printf("done inflating\n");
387 std::printf("%d bytes left over\n", _zs.avail_in);
388 std::printf("CRC is %x\n", crc);
389 #endif
391 guint8 *ret_bytes;
392 if (check_crc(oldcrc, crc, flags) && gba->len > 0)
393 ret_bytes = gba->data;
394 else
395 ret_bytes = NULL;
396 g_byte_array_free(gba, FALSE);
398 inflateReset(&_zs);
399 return ret_bytes;
400 }
402 bool JarFile::check_crc(guint32 oldcrc, guint32 crc, guint16 flags)
403 {
404 //fixme: does not work yet
406 if(flags & 0x0008) {
407 guint8 *bytes = (guint8 *)g_malloc(sizeof(guint8) * 16);
408 if (!read(bytes, 16)) {
409 g_free(bytes);
410 return false;
411 }
413 guint32 signature = UNPACK_UB4(bytes, 0);
414 g_free(bytes);
415 if(signature != 0x08074b50) {
416 fprintf(stderr, "missing data descriptor!\n");
417 }
419 crc = UNPACK_UB4(bytes, 4);
421 }
422 if (oldcrc != crc) {
423 #ifdef DEBUG
424 std::fprintf(stderr, "Error! CRCs do not match! Got %x, expected %x\n",
425 oldcrc, crc);
426 #endif
427 }
428 return true;
429 }
431 JarFile::JarFile(JarFile const& rhs)
432 {
433 *this = rhs;
434 }
436 JarFile& JarFile::operator=(JarFile const& rhs)
437 {
438 if (this == &rhs)
439 return *this;
441 _zs = rhs._zs;//fixme
442 if (_filename == NULL)
443 _filename = NULL;
444 else
445 _filename = strdup(rhs._filename);
446 if (_last_filename == NULL)
447 _last_filename = NULL;
448 else
449 _last_filename = strdup(rhs._last_filename);
450 fd = rhs.fd;
452 return *this;
453 }
456 /////////////////////////
457 // JarFileReader //
458 /////////////////////////
460 GByteArray *JarFileReader::get_next_file()
461 {
462 if (_state == CLOSED) {
463 _jarfile.open();
464 _state = OPEN;
465 }
467 return _jarfile.get_next_file_contents();
468 }
470 JarFileReader& JarFileReader::operator=(JarFileReader const& rhs)
471 {
472 if (&rhs == this)
473 return *this;
475 _jarfile = rhs._jarfile;
476 _state = rhs._state;
478 return *this;
479 }
481 /*
482 * If the filename gets reset, a jarfile object gets generated again,
483 * ready to be opened for reading.
484 */
485 void JarFileReader::set_filename(gchar const *new_filename)
486 {
487 _jarfile.close();
488 _jarfile = JarFile(new_filename);
489 }
491 void JarFileReader::set_jarfile(JarFile const& new_jarfile)
492 {
493 _jarfile = new_jarfile;
494 }
496 JarFileReader::JarFileReader(JarFileReader const& rhs)
497 {
498 *this = rhs;
499 }
501 } // namespace Inkjar
504 #if 0 //testing code
505 #include "jar.h"
506 /*
507 * This program writes all the files from a jarfile to stdout and inflates
508 * where needed.
509 */
510 int main(int argc, char *argv[])
511 {
512 gchar *filename;
513 if (argc < 2) {
514 filename = "./ide.jar\0";
515 } else {
516 filename = argv[1];
517 }
519 Inkjar::JarFileReader jar_file_reader(filename);
521 for (;;) {
522 GByteArray *gba = jar_file_reader.get_next_file();
523 if (gba == NULL) {
524 char *c_ptr;
525 gchar *last_filename = jar_file_reader.get_last_filename();
526 if (last_filename == NULL)
527 break;
528 if ((c_ptr = std::strrchr(last_filename, '/')) != NULL) {
529 if (*(++c_ptr) == '\0') {
530 g_free(last_filename);
531 continue;
532 }
533 }
534 } else if (gba->len > 0)
535 ::write(1, gba->data, gba->len);
536 else
537 break;
538 }
539 return 0;
540 }
541 #endif