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