Code

excise never-used code and stale comments
[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
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));
89     
90     _zs.zalloc = Z_NULL;
91     _zs.zfree = Z_NULL;
92     _zs.opaque = Z_NULL;
93     
94     if(inflateInit2(&_zs, -15) != Z_OK) {
95         fprintf(stderr,"error initializing inflation!\n");
96         return false;
97     }
99     return true;
102 bool JarFile::open()
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;
114 bool JarFile::close()
116     if (fd >= 0 && !::close(fd)) {
117         inflateEnd(&_zs);
118         return true;
119     }
120     return false;
123 bool JarFile::read_signature()
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;
153 guint32 JarFile::get_crc(guint8 *bytes, guint16 flags)
155     guint32 crc = 0;
156     //no data descriptor
157     if (!(flags & 0x0008)) {
158         crc = UNPACK_UB4(bytes, LOC_CRC);
159         
160 #ifdef DEBUG
161         std::printf("CRC from file is %x\n", crc);
162 #endif
163     }
164     
165     return crc;
168 guint8 *JarFile::read_filename(guint16 filename_length)
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;
185 bool JarFile::check_compression_method(guint16 method, guint16 flags)
187     return !(method != 8 && flags & 0x0008);
190 GByteArray *JarFile::get_next_file_contents()
192     guint8 *bytes;
193     GByteArray *gba = g_byte_array_new();
195     read_signature();
196     
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
225     
226     guint32 crc = get_crc(bytes, flags);
227     
228     gchar *filename = (gchar *)read_filename(filename_length);
229     g_free(bytes);
230     
231     if (filename == NULL) 
232         return NULL;
233    
234     if (_last_filename != NULL)
235         g_free(_last_filename);
236     _last_filename = filename;
238     //check if this is a directory and skip
239     
240     char *c_ptr;
241     if ((c_ptr = std::strrchr(filename, '/')) != NULL) {
242         if (*(++c_ptr) == '\0') {
243             return NULL;
244         }
245     }
246    
247     if (!check_compression_method(method, flags)) {
248         std::fprintf(stderr, "error in jar file\n");
249         return NULL;
250     }    
251     
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     }
276         
277     
278     return gba;
281 guint8 *JarFile::get_uncompressed_file(guint32 compressed_size, guint32 crc, 
282                                        guint16 eflen, guint16 flags)
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;
289     
290     crc2 = crc32(crc2, NULL, 0);
291     
292     bytes = (guint8 *)g_malloc(sizeof(guint8) * RDSZ);
293     while(out_a < compressed_size){
294         unsigned int nbytes = (in_a > RDSZ ? RDSZ : in_a);
295         
296         if (!(nbytes = read(bytes, nbytes))) {
297             g_free(bytes);
298             return NULL;
299         }
300         
301         crc2 = crc32(crc2, (Bytef*)bytes, nbytes);
302             
303         g_byte_array_append (gba, bytes, nbytes);
304         out_a += nbytes;
305         in_a -= nbytes;
306             
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     }
319     
320     return bytes;
323 int JarFile::read(guint8 *buf, int count)
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;
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)
339     if (compressed_size == 0)
340         return NULL;
341     
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();
347     
348     _zs.avail_in = 0;
349     guint32 crc = crc32(0, Z_NULL, 0);
350     
351     do {
352                 
353         if (!_zs.avail_in) {
354         
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;
367         
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         }
376         
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);
383     
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
390     
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;
402 bool JarFile::check_crc(guint32 oldcrc, guint32 crc, guint16 flags)
404     //fixme: does not work yet
405         
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         }
412         
413         guint32 signature = UNPACK_UB4(bytes, 0);
414         g_free(bytes);
415         if(signature != 0x08074b50) {
416             fprintf(stderr, "missing data descriptor!\n");
417         }
418         
419         crc = UNPACK_UB4(bytes, 4);
420         
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;
431 JarFile::JarFile(JarFile const& rhs)
433     *this = rhs;
436 JarFile& JarFile::operator=(JarFile const& rhs)
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;
451     
452     return *this;
456 /////////////////////////
457 //      JarFileReader  //
458 /////////////////////////
460 GByteArray *JarFileReader::get_next_file()
462     if (_state == CLOSED) {
463         _jarfile.open();
464         _state = OPEN;
465     }
466     
467     return _jarfile.get_next_file_contents();
470 JarFileReader& JarFileReader::operator=(JarFileReader const& rhs)
472     if (&rhs == this)
473         return *this;
475     _jarfile = rhs._jarfile;
476     _state = rhs._state;
477     
478     return *this;
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)
487     _jarfile.close();
488     _jarfile = JarFile(new_filename);
491 void JarFileReader::set_jarfile(JarFile const& new_jarfile)
493     _jarfile = new_jarfile;
496 JarFileReader::JarFileReader(JarFileReader const& rhs)
498     *this = rhs;
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[])
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);
520     
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;
541 #endif