Code

Patches 3-11 from 488862 for a number of memleaks and allocation/deallocation issues.
[inkscape.git] / src / inkjar / jar.cpp
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 "jar.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));
92     
93     _zs.zalloc = Z_NULL;
94     _zs.zfree = Z_NULL;
95     _zs.opaque = Z_NULL;
96     
97     if(inflateInit2(&_zs, -15) != Z_OK) {
98         fprintf(stderr,"error initializing inflation!\n");
99         return false;
100     }
102     return true;
105 bool JarFile::open()
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;
117 bool JarFile::close()
119     if (fd >= 0 && !::close(fd)) {
120         inflateEnd(&_zs);
121         return true;
122     }
123     return false;
126 bool JarFile::read_signature()
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;
156 guint32 JarFile::get_crc(guint8 *bytes, guint16 flags)
158     guint32 crc = 0;
159     //no data descriptor
160     if (!(flags & 0x0008)) {
161         crc = UNPACK_UB4(bytes, LOC_CRC);
162         
163 #ifdef DEBUG
164         std::printf("CRC from file is %x\n", crc);
165 #endif
166     }
167     
168     return crc;
171 guint8 *JarFile::read_filename(guint16 filename_length)
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;
188 bool JarFile::check_compression_method(guint16 method, guint16 flags)
190     return !(method != 8 && flags & 0x0008);
193 GByteArray *JarFile::get_next_file_contents()
195     guint8 *bytes;
196     GByteArray *gba = g_byte_array_new();
198     read_signature();
199     
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
228     
229     guint32 crc = get_crc(bytes, flags);
230     
231     gchar *filename = (gchar *)read_filename(filename_length);
232     g_free(bytes);
233     
234     if (filename == NULL) 
235         return NULL;
236    
237     if (_last_filename != NULL)
238         g_free(_last_filename);
239     _last_filename = filename;
241     //check if this is a directory and skip
242     
243     char *c_ptr;
244     if ((c_ptr = std::strrchr(filename, '/')) != NULL) {
245         if (*(++c_ptr) == '\0') {
246             return NULL;
247         }
248     }
249    
250     if (!check_compression_method(method, flags)) {
251         std::fprintf(stderr, "error in jar file\n");
252         return NULL;
253     }    
254     
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     }
279         
280     
281     return gba;
284 guint8 *JarFile::get_uncompressed_file(guint32 compressed_size, guint32 crc, 
285                                        guint16 eflen, guint16 flags)
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;
292     
293     crc2 = crc32(crc2, NULL, 0);
294     
295     bytes = (guint8 *)g_malloc(sizeof(guint8) * RDSZ);
296     while(out_a < compressed_size){
297         unsigned int nbytes = (in_a > RDSZ ? RDSZ : in_a);
298         
299         if (!(nbytes = read(bytes, nbytes))) {
300             g_free(bytes);
301             return NULL;
302         }
303         
304         crc2 = crc32(crc2, (Bytef*)bytes, nbytes);
305             
306         g_byte_array_append (gba, bytes, nbytes);
307         out_a += nbytes;
308         in_a -= nbytes;
309             
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     }
322     
323     return bytes;
326 int JarFile::read(guint8 *buf, int count)
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;
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)
342     if (compressed_size == 0)
343         return NULL;
344     
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();
350     
351     _zs.avail_in = 0;
352     guint32 crc = crc32(0, Z_NULL, 0);
353     
354     do {
355                 
356         if (!_zs.avail_in) {
357         
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;
370         
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         }
379         
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);
386     
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
393     
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;
405 bool JarFile::check_crc(guint32 oldcrc, guint32 crc, guint16 flags)
407     //fixme: does not work yet
408         
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         }
415         
416         guint32 signature = UNPACK_UB4(bytes, 0);
417         g_free(bytes);
418         if(signature != 0x08074b50) {
419             fprintf(stderr, "missing data descriptor!\n");
420         }
421         
422         crc = UNPACK_UB4(bytes, 4);
423         
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;
434 JarFile::JarFile(JarFile const& rhs)
436     *this = rhs;
439 JarFile& JarFile::operator=(JarFile const& rhs)
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;
454     
455     return *this;
459 /////////////////////////
460 //      JarFileReader  //
461 /////////////////////////
463 GByteArray *JarFileReader::get_next_file()
465     if (_state == CLOSED) {
466         _jarfile.open();
467         _state = OPEN;
468     }
469     
470     return _jarfile.get_next_file_contents();
473 JarFileReader& JarFileReader::operator=(JarFileReader const& rhs)
475     if (&rhs == this)
476         return *this;
478     _jarfile = rhs._jarfile;
479     _state = rhs._state;
480     
481     return *this;
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)
490     _jarfile.close();
491     _jarfile = JarFile(new_filename);
494 void JarFileReader::set_jarfile(JarFile const& new_jarfile)
496     _jarfile = new_jarfile;
499 JarFileReader::JarFileReader(JarFileReader const& rhs)
501     *this = rhs;
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[])
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);
523     
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;
544 #endif