Logo Search packages:      
Sourcecode: inkscape version File versions

jar.cpp

/*
 * Copyright (C) 1999, 2000  Bryan Burns
 * Copyright (C) 2004 Johan Ceuppens
 *
 * Released under GNU GPL, read the file 'COPYING' for more information
 */

/*
 * TODO/FIXME: 
 * - configure #ifdefs should be enabled
 * - move to cstdlib instead of stdlib.h etc.
 * - remove exit functions
 * - move to clean C++ code
 * - windowsify
 * - remove a few g_free/g_mallocs
 * - unseekable files
 * - move to LGPL by rewriting macros
 * - crcs for compressed files
 * - put in eof
 */

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif


//#ifdef STDC_HEADERS
//#endif

//#ifdef HAVE_UNISTD_H
//#endif

//#ifdef HAVE_SYS_PARAM_H
//#else
//#define MAXPATHLEN 1024
//#endif

//#ifdef HAVE_DIRENT_H
//#endif

//#ifdef HAVE_FCNTL_H
#include <fcntl.h>
//#endif

#include <cstring>
#include <string>
#include <cstdlib>
#include <glib.h>
#include <zlib.h>

#include "jar.h"

#include <fstream>
#ifdef WORDS_BIGENDIAN

#define L2BI(l) ((l & 0xff000000) >> 24) | \
((l & 0x00ff0000) >> 8)  | \
((l & 0x0000ff00) << 8)  | \
((l & 0x000000ff) << 24);

#define L2BS(l) ((l & 0xff00) >> 8) | ((l & 0x00ff) << 8);

#endif

namespace Inkjar {

JarFile::JarFile(gchar const*new_filename)
{
    _filename = strdup(new_filename);
    _last_filename = NULL;
    fd = -1;
}

//fixme: the following should probably just return a const gchar* and not 
//       use strdup
gchar *JarFile::get_last_filename() const
{
    return (_last_filename != NULL ? strdup(_last_filename) : NULL);
}

JarFile::~JarFile()
{ 
    if (_filename != NULL)
      g_free(_filename); 
    if (_last_filename != NULL)
      g_free(_last_filename); 
}

bool JarFile::init_inflation()
{
    memset(&_zs, 0, sizeof(z_stream));
    
    _zs.zalloc = Z_NULL;
    _zs.zfree = Z_NULL;
    _zs.opaque = Z_NULL;
    
    if(inflateInit2(&_zs, -15) != Z_OK) {
      fprintf(stderr,"error initializing inflation!\n");
      return false;
    }

    return true;
}

bool JarFile::open()
{
    if ((fd = ::open(_filename, O_RDONLY)) < 0) {
      fprintf(stderr, "open failed.\n");
      return false;
    }
    if (!init_inflation())
      return false;

    return true;
}

bool JarFile::close()
{
    if (fd >= 0 && !::close(fd)) {
      inflateEnd(&_zs);
      return true;
    }
    return false;
}

bool JarFile::read_signature()
{
    guint8 *bytes = (guint8 *)g_malloc(sizeof(guint8) * 4);
    if (!read(bytes, 4)) {
      g_free(bytes);
      return false;
    }

    guint32 signature = UNPACK_UB4(bytes, 0);
    g_free(bytes);

#ifdef DEBUG
    std::printf("signature is %x\n", signature);
#endif

    if (signature == 0x08074b50) {
      //skip data descriptor
      bytes = (guint8 *)malloc(sizeof(guint8) * 12);
      if (!read(bytes, 12)) {
          g_free(bytes);
          return false;
      }
    } else if (signature == 0x02014b50 || signature == 0x04034b50) {
      return true;
    } else {
      return false;
    }
    return false;
}

guint32 JarFile::get_crc(guint8 *bytes, guint16 flags)
{
    guint32 crc = 0;
    //no data descriptor
    if (!(flags & 0x0008)) {
      crc = UNPACK_UB4(bytes, LOC_CRC);
      
#ifdef DEBUG
      std::printf("CRC from file is %x\n", crc);
#endif
    }
    
    return crc;
}

guint8 *JarFile::read_filename(guint16 filename_length)
{
    guint8 *filename = (guint8 *)g_malloc(sizeof(guint8)
                                * (filename_length+1));
    if (!read(filename, filename_length)) {
      g_free(filename);
      return NULL;
    }
    filename[filename_length] = '\0';

#ifdef DEBUG
    std::printf("Filename is %s\n", filename);
#endif

    return filename;
}

bool JarFile::check_compression_method(guint16 method, guint16 flags)
{
    return !(method != 8 && flags & 0x0008);
}

GByteArray *JarFile::get_next_file_contents()
{
    guint8 *bytes;
    GByteArray *gba = g_byte_array_new();

    read_signature();
    
    //get compressed size
    bytes = (guint8 *)g_malloc(sizeof(guint8) * 30);
    if (!read(bytes+4, 26)) {
      g_free(bytes);
      return NULL;
    }
    guint32 compressed_size = UNPACK_UB4(bytes, LOC_CSIZE);
    guint16 filename_length = UNPACK_UB2(bytes, LOC_FNLEN);
    guint16 eflen = UNPACK_UB2(bytes, LOC_EFLEN);
    guint16 flags = UNPACK_UB2(bytes, LOC_EXTRA);
    guint16 method = UNPACK_UB2(bytes, LOC_COMP);

    if (filename_length == 0) {
      g_byte_array_free(gba, TRUE);
      if (_last_filename != NULL)
          g_free(_last_filename);
      _last_filename = NULL;
      return NULL;
    }


#ifdef DEBUG    
    std::printf("Compressed size is %u\n", compressed_size);
    std::printf("Filename length is %hu\n", filename_length);
    std::printf("Extra field length is %hu\n", eflen);
    std::printf("Flags are %#hx\n", flags);
    std::printf("Compression method is %#hx\n", method);
#endif
    
    guint32 crc = get_crc(bytes, flags);
    
    gchar *filename = (gchar *)read_filename(filename_length);
    g_free(bytes);
    
    if (filename == NULL) 
      return NULL;
   
    if (_last_filename != NULL)
      g_free(_last_filename);
    _last_filename = filename;

    //check if this is a directory and skip
    
    char *c_ptr;
    if ((c_ptr = std::strrchr(filename, '/')) != NULL) {
      if (*(++c_ptr) == '\0') {
          return NULL;
      }
    }
   
    if (!check_compression_method(method, flags)) {
      std::fprintf(stderr, "error in jar file\n");
      return NULL;
    }    
    
    if (method == 8 || flags & 0x0008) {
      unsigned int file_length = 0;//uncompressed file length
      lseek(fd, eflen, SEEK_CUR);
      guint8 *file_data = get_compressed_file(compressed_size, file_length, 
                                    crc, flags);
      if (file_data == NULL) {
          g_byte_array_free(gba, FALSE);
          return NULL;
      }
      g_byte_array_append(gba, file_data, file_length);
    } else if (method == 0) {
      guint8 *file_data = get_uncompressed_file(compressed_size, crc, 
                                      eflen, flags); 

      if (file_data == NULL) {
          g_byte_array_free(gba, TRUE);
          return NULL;
      }
      g_byte_array_append(gba, file_data, compressed_size);
    } else {
      lseek(fd, compressed_size+eflen, SEEK_CUR);
      g_byte_array_free(gba, FALSE);
      return NULL;
    }
        
    
    return gba;
}

guint8 *JarFile::get_uncompressed_file(guint32 compressed_size, guint32 crc, 
                               guint16 eflen, guint16 flags)
{
    GByteArray *gba = g_byte_array_new();
    unsigned int out_a = 0;
    unsigned int in_a = compressed_size;
    guint8 *bytes;
    guint32 crc2 = 0;
    
    crc2 = crc32(crc2, NULL, 0);
    
    bytes = (guint8 *)g_malloc(sizeof(guint8) * RDSZ);
    while(out_a < compressed_size){
      unsigned int nbytes = (in_a > RDSZ ? RDSZ : in_a);
      
      if (!(nbytes = read(bytes, nbytes))) {
          g_free(bytes);
          return NULL;
      }
      
      crc2 = crc32(crc2, (Bytef*)bytes, nbytes);
          
      g_byte_array_append (gba, bytes, nbytes);
      out_a += nbytes;
      in_a -= nbytes;
          
#ifdef DEBUG    
      std::printf("%d bytes written\n", out_a);
#endif
    }
    lseek(fd, eflen, SEEK_CUR);
    g_free(bytes);

    if (!check_crc(crc, crc2, flags)) {
        bytes = gba->data;
        g_byte_array_free(gba, FALSE);//FALSE argument does not free actual data
      return NULL;
    }
    
    return bytes;
}

int JarFile::read(guint8 *buf, int count)
{
    int nbytes;
    if ((nbytes = ::read(fd, buf, count)) != count) {
      fprintf(stderr, "read error\n");
      exit(1);
      return 0;
    }
    return nbytes;
}

/* FIXME: this could probably use ZlibBuffer */
guint8 *JarFile::get_compressed_file(guint32 compressed_size, 
                             unsigned int& file_length,
                             guint32 oldcrc, guint16 flags)
{
    if (compressed_size == 0)
      return NULL;
    
    guint8 in_buffer[RDSZ];
    guint8 out_buffer[RDSZ];
    int nbytes;
    unsigned int leftover_in = compressed_size;
    GByteArray *gba = g_byte_array_new();
    
    _zs.avail_in = 0;
    guint32 crc = crc32(0, Z_NULL, 0);
    
    do {
            
      if (!_zs.avail_in) {
      
          if ((nbytes = ::read(fd, in_buffer, 
                         (leftover_in < RDSZ ? leftover_in : RDSZ))) 
            < 0) {
            fprintf(stderr, "jarfile read error");
          }
          _zs.avail_in = nbytes;
          _zs.next_in = in_buffer;
          crc = crc32(crc, in_buffer, _zs.avail_in);
          leftover_in -= RDSZ;
      }
      _zs.next_out = out_buffer;
      _zs.avail_out = RDSZ;
      
      int ret = inflate(&_zs, Z_NO_FLUSH);
      if (RDSZ != _zs.avail_out) {
          unsigned int tmp_len = RDSZ - _zs.avail_out;
          guint8 *tmp_bytes = (guint8 *)g_malloc(sizeof(guint8) 
                                       * tmp_len);
          memcpy(tmp_bytes, out_buffer, tmp_len);
          g_byte_array_append(gba, tmp_bytes, tmp_len);
      }
      
      if (ret == Z_STREAM_END) {
          break;
      }
      if (ret != Z_OK)
          std::printf("decompression error %d\n", ret);
    } while (_zs.total_in < compressed_size);
    
    file_length = _zs.total_out;
#ifdef DEBUG
    std::printf("done inflating\n");
    std::printf("%d bytes left over\n", _zs.avail_in);
    std::printf("CRC is %x\n", crc);
#endif
    
    guint8 *ret_bytes;
    if (check_crc(oldcrc, crc, flags) && gba->len > 0)
      ret_bytes = gba->data;
    else 
      ret_bytes = NULL;
    g_byte_array_free(gba, FALSE);

    inflateReset(&_zs); 
    return ret_bytes;
}

bool JarFile::check_crc(guint32 oldcrc, guint32 crc, guint16 flags)
{
    //fixme: does not work yet
      
    if(flags & 0x0008) {
      guint8 *bytes = (guint8 *)g_malloc(sizeof(guint8) * 16);
      if (!read(bytes, 16)) {
          g_free(bytes);
          return false;
      }
      
      guint32 signature = UNPACK_UB4(bytes, 0);
      g_free(bytes);
      if(signature != 0x08074b50) {
          fprintf(stderr, "missing data descriptor!\n");
      }
      
      crc = UNPACK_UB4(bytes, 4);
      
    }
    if (oldcrc != crc) {
#ifdef DEBUG
      std::fprintf(stderr, "Error! CRCs do not match! Got %x, expected %x\n",
                 oldcrc, crc);
#endif
    }
    return true;
}

JarFile::JarFile(JarFile const& rhs)
{
    *this = rhs;
}

JarFile& JarFile::operator=(JarFile const& rhs)
{
    if (this == &rhs)
      return *this;

    _zs = rhs._zs;//fixme
    if (_filename == NULL)
      _filename = NULL;
    else
      _filename = strdup(rhs._filename);
    if (_last_filename == NULL)
      _last_filename = NULL;
    else 
      _last_filename = strdup(rhs._last_filename);
    fd = rhs.fd;
    
    return *this;
}


/////////////////////////
//      JarFileReader  //
/////////////////////////

GByteArray *JarFileReader::get_next_file()
{
    if (_state == CLOSED) {
      _jarfile.open();
      _state = OPEN;
    }
    
    return _jarfile.get_next_file_contents();
}

JarFileReader& JarFileReader::operator=(JarFileReader const& rhs)
{
    if (&rhs == this)
      return *this;

    _jarfile = rhs._jarfile;
    _state = rhs._state;
    
    return *this;
}

/*
 * If the filename gets reset, a jarfile object gets generated again,
 * ready to be opened for reading.
 */
void JarFileReader::set_filename(gchar const *new_filename)
{
    _jarfile.close();
    _jarfile = JarFile(new_filename);
}

void JarFileReader::set_jarfile(JarFile const& new_jarfile)
{
    _jarfile = new_jarfile;
}

JarFileReader::JarFileReader(JarFileReader const& rhs)
{
    *this = rhs;
}

} // namespace Inkjar


#if 0 //testing code
#include "jar.h"
/*
 * This program writes all the files from a jarfile to stdout and inflates
 * where needed.
 */
int main(int argc, char *argv[])
{
    gchar *filename;
    if (argc < 2) {
      filename = "./ide.jar\0";
    } else {
      filename = argv[1];
    }

    Inkjar::JarFileReader jar_file_reader(filename);
    
    for (;;) {
      GByteArray *gba = jar_file_reader.get_next_file();
      if (gba == NULL) {
          char *c_ptr;
          gchar *last_filename = jar_file_reader.get_last_filename();
          if (last_filename == NULL)
            break;
          if ((c_ptr = std::strrchr(last_filename, '/')) != NULL) {
            if (*(++c_ptr) == '\0') {
                g_free(last_filename);
                continue;
            }
          }
      } else if (gba->len > 0)
          ::write(1, gba->data, gba->len);
      else
          break;
    }
    return 0;
}
#endif

Generated by  Doxygen 1.6.0   Back to index