LCOV - code coverage report
Current view: top level - src - ziplocalentry.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 95 95 100.0 %
Date: 2015-04-12 Functions: 14 16 87.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   Zipios++ - a small C++ library that provides easy access to .zip files.
       3             : 
       4             :   Copyright (C) 2000-2007  Thomas Sondergaard
       5             :   Copyright (C) 2015  Made to Order Software Corporation
       6             : 
       7             :   This library is free software; you can redistribute it and/or
       8             :   modify it under the terms of the GNU Lesser General Public
       9             :   License as published by the Free Software Foundation; either
      10             :   version 2 of the License, or (at your option) any later version.
      11             : 
      12             :   This library is distributed in the hope that it will be useful,
      13             :   but WITHOUT ANY WARRANTY; without even the implied warranty of
      14             :   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15             :   Lesser General Public License for more details.
      16             : 
      17             :   You should have received a copy of the GNU Lesser General Public
      18             :   License along with this library; if not, write to the Free Software
      19             :   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
      20             : */
      21             : 
      22             : /** \file
      23             :  * \brief Implementation of the zipios::ZipLocalEntry class.
      24             :  *
      25             :  * This file is the implementation of the zipios::ZipLocalEntry class
      26             :  * which handles zipios::FileEntry's found in Zip archives.
      27             :  */
      28             : 
      29             : #include "ziplocalentry.hpp"
      30             : 
      31             : #include "zipios++/zipiosexceptions.hpp"
      32             : 
      33             : #include "dostime.h"
      34             : #include "zipios_common.hpp"
      35             : 
      36             : 
      37             : namespace zipios
      38             : {
      39             : 
      40             : 
      41             : /** \brief Various definitions for local blocks.
      42             :  *
      43             :  * The ZipLocalEntry needs a signature, a flag, and makes use
      44             :  * of a structure declaration (although we cannot really use
      45             :  * that structure.)
      46             :  */
      47             : namespace
      48             : {
      49             : 
      50             : /** \brief The signature of a local entry.
      51             :  *
      52             :  * This value represents the signature of a Zip archive block defining
      53             :  * a ZipLocalEntry.
      54             :  *
      55             :  * \code
      56             :  * "PK 3.4"
      57             :  * \endcode
      58             :  */
      59             : uint32_t const      g_signature = 0x04034b50;
      60             : 
      61             : 
      62             : /** \brief A bit in the general purpose flags.
      63             :  *
      64             :  * This mask is used to know whether the size and CRC are saved in
      65             :  * the header or after the header. At this time Zipios++ does not
      66             :  * support such trailing data as it makes use of the compressed
      67             :  * and uncompressed sizes to properly stream the output data.
      68             :  *
      69             :  * This is bit 3. (see point 4.4.4 in doc/zip-format.txt)
      70             :  */
      71             : uint16_t const      g_trailing_data_descriptor = 1 << 3;
      72             : 
      73             : 
      74             : /** \brief ZipLocalEntry Header
      75             :  *
      76             :  * This structure shows how the header of the ZipLocalEntry is defined.
      77             :  * Note that the file name and extra field have a variable size which
      78             :  * is defined in two 16 bit values just before they appear.
      79             :  *
      80             :  * The filename cannot be empty, however, the extra field can (and
      81             :  * usually is).
      82             :  *
      83             :  * \note
      84             :  * This structure is NOT used directly only for its sizeof() and
      85             :  * documentation because that way zipios can work on little and big
      86             :  * endians without the need to know the endianess of your computer.
      87             :  */
      88             : struct ZipLocalEntryHeader
      89             : {
      90             :     uint32_t            m_signature;
      91             :     uint16_t            m_extract_version;
      92             :     uint16_t            m_general_purpose_bitfield;
      93             :     uint16_t            m_compress_method;
      94             :     uint32_t            m_dostime;
      95             :     uint32_t            m_crc_32;
      96             :     uint32_t            m_compressed_size;
      97             :     uint32_t            m_uncompressed_size;
      98             :     uint16_t            m_filename_len;
      99             :     uint16_t            m_extra_field_len;
     100             :     //uint8_t             m_filename[m_filename_len];
     101             :     //uint8_t             m_extra_field[m_extra_field_len];
     102             : };
     103             : 
     104             : 
     105             : } // no name namespace
     106             : 
     107             : 
     108             : 
     109             : /** \class ZipLocalEntry
     110             :  * \brief An implementation of the FileEntry for Zip archives.
     111             :  *
     112             :  * A concrete implementation of the abstract FileEntry base class for
     113             :  * ZipFile entries, specifically for representing the information
     114             :  * present in the local headers of file entries in a zip file.
     115             :  */
     116             : 
     117             : 
     118             : 
     119             : /** \brief Create a default ZipLocalEntry objects.
     120             :  *
     121             :  * This constructor is used to create a default ZipLocalEntry object.
     122             :  */
     123      199901 : ZipLocalEntry::ZipLocalEntry()
     124      199901 :     : FileEntry(FilePath(""))
     125             :     //, m_extract_version(g_zip_format_version) -- auto-init
     126             :     //, m_general_purpose_bitfield(0) -- auto-init
     127             :     //, m_is_directory(false)
     128             :     //, m_compressed_size(0) -- auto-init
     129             : {
     130      199901 : }
     131             : 
     132             : 
     133             : /** \brief Copy of the ZipLocalEntry from any kind of FileEntry object.
     134             :  *
     135             :  * This function is used when copying a DirectoryEntry to a
     136             :  * ZipCentralDirectoryEntry object when creating a copy while
     137             :  * saving a Zip archive.
     138             :  *
     139             :  * \param[in] src  The source to copy in this new ZipLocalEntry.
     140             :  */
     141      133066 : ZipLocalEntry::ZipLocalEntry(FileEntry const & src)
     142             :     : FileEntry(src)
     143             :     //, m_extract_version(g_zip_format_version) -- auto-init
     144             :     //, m_general_purpose_bitfield(0) -- auto-init
     145      133066 :     , m_is_directory(src.isDirectory())
     146             :     //, m_compressed_size(0) -- auto-init
     147             : {
     148      133066 : }
     149             : 
     150             : 
     151             : /** \brief Create a clone of a ZipLocalEntry object.
     152             :  *
     153             :  * This function allocates a new ZipLocalEntry on the heap and returns
     154             :  * a copy of this ZipLocalEntry object in it.
     155             :  *
     156             :  * \return A new ZipLocalEntry which is a clone of this ZipLocalEntry object.
     157             :  */
     158             : FileEntry::pointer_t ZipLocalEntry::clone() const // LCOV_EXCL_LINE
     159             : {
     160             :     // It makes sense to keep the clone() function for this class
     161             :     // but since it is internal and never allocated as is (we use
     162             :     // the ZipCentralDirectoryEntry instead) it is marked as none
     163             :     // reachable by the coverage tests
     164             :     return FileEntry::pointer_t(new ZipLocalEntry(*this)); // LCOV_EXCL_LINE
     165             : }
     166             : 
     167             : 
     168             : /** \brief Clean up a ZipLocalEntry object.
     169             :  *
     170             :  * This function ensures proper clean up of a ZipLocationEntry object.
     171             :  */
     172      333721 : ZipLocalEntry::~ZipLocalEntry()
     173             : {
     174      333721 : }
     175             : 
     176             : 
     177             : /** \brief Check whether the filename represents a directory.
     178             :  *
     179             :  * This function checks the last character of the filename, if it
     180             :  * is a separator ('/') then the function returns true meaning
     181             :  * that the file represents a directory.
     182             :  *
     183             :  * \return true if the entry represents a directory.
     184             :  */
     185      863241 : bool ZipLocalEntry::isDirectory() const
     186             : {
     187      863241 :     return m_is_directory;
     188             : }
     189             : 
     190             : 
     191             : /** \brief Compare two file entries for equality.
     192             :  *
     193             :  * This function compares most of the fields between two file
     194             :  * entries to see whether they are equal or not.
     195             :  *
     196             :  * \note
     197             :  * This function calls the base class isEqual() and also verifies
     198             :  * that the ZipLocalEntry fields are equal.
     199             :  *
     200             :  * \note
     201             :  * This function is also used to compare ZipCDirEntry since none
     202             :  * of the additional field participate in the comparison.
     203             :  *
     204             :  * \param[in] file_entry  The file entry to compare this against.
     205             :  *
     206             :  * \return true if both FileEntry objects are considered equal.
     207             :  */
     208       70479 : bool ZipLocalEntry::isEqual(FileEntry const & file_entry) const
     209             : {
     210       70479 :     ZipLocalEntry const * const ze(dynamic_cast<ZipLocalEntry const * const>(&file_entry));
     211       70479 :     if(!ze)
     212             :     {
     213        1097 :         return false;
     214             :     }
     215       69382 :     return FileEntry::isEqual(file_entry)
     216       69372 :         && m_extract_version          == ze->m_extract_version
     217       69372 :         && m_general_purpose_bitfield == ze->m_general_purpose_bitfield
     218      138754 :         && m_is_directory             == ze->m_is_directory;
     219             :         //&& m_compressed_size          == ze->m_compressed_size -- ignore in comparison
     220             : }
     221             : 
     222             : 
     223             : /** \brief Retrive the size of the file when compressed.
     224             :  *
     225             :  * This function returns the compressed size of the entry. If the
     226             :  * entry is not stored in a compressed format, the uncompressed
     227             :  * size is returned.
     228             :  *
     229             :  * \return The compressed size of the entry.
     230             :  */
     231        7421 : size_t ZipLocalEntry::getCompressedSize() const
     232             : {
     233        7421 :     return m_compressed_size;
     234             : }
     235             : 
     236             : 
     237             : /** \brief Retrieve the size of the header.
     238             :  *
     239             :  * This function dynamically determines the size of the Zip archive
     240             :  * header necessary for this FileEntry.
     241             :  *
     242             :  * This function returns the size of the Zip archive header.
     243             :  *
     244             :  * \return The size of the header in bytes.
     245             :  */
     246      133065 : size_t ZipLocalEntry::getHeaderSize() const
     247             : {
     248             :     // Note that the structure is 32 bytes because of an alignment
     249             :     // and attempting to use options to avoid the alignment would
     250             :     // not be portable so we use a hard coded value (yuck!)
     251             :     return 30 /* sizeof(ZipLocalEntryHeader) */
     252      133065 :          + m_filename.length() + (m_is_directory ? 1 : 0)
     253      133065 :          + m_extra_field.size();
     254             : }
     255             : 
     256             : 
     257             : /** \brief Set the size when the file is compressed.
     258             :  *
     259             :  * This function saves the compressed size of the entry in this object.
     260             :  *
     261             :  * By default the compressed size is viewed as the same as the
     262             :  * uncompressed size (i.e. as if STORED was used for the compression
     263             :  * method.)
     264             :  *
     265             :  * \param[in] size  Value to set the compressed size field of the entry to.
     266             :  */
     267      133065 : void ZipLocalEntry::setCompressedSize(size_t size)
     268             : {
     269      133065 :     m_compressed_size = size;
     270      133065 : }
     271             : 
     272             : 
     273             : /** \brief Save the CRC of the entry.
     274             :  *
     275             :  * This funciton savees the CRC field in this FileEntry field.
     276             :  *
     277             :  * This function has the side of setting the m_has_crc_32 flag
     278             :  * to true meaning that the CRC was defined as expected.
     279             :  *
     280             :  * \param[in] crc  Value to set the CRC field to.
     281             :  */
     282      133065 : void ZipLocalEntry::setCrc(uint32_t crc)
     283             : {
     284      133065 :     m_crc_32 = crc;
     285      133065 :     m_has_crc_32 = true;
     286      133065 : }
     287             : 
     288             : 
     289             : /** \brief Is there a trailing data descriptor?
     290             :  *
     291             :  * This function checks the bit in the General Purpose Flags to know
     292             :  * whether the local entry header includes the compressed and uncompressed
     293             :  * sizes or whether these are defined after the compressed data.
     294             :  *
     295             :  * The trailing data buffer looks like this:
     296             :  *
     297             :  * \code
     298             :  *      signature (PK 8.7) -- OPTIONAL  -- 32 bit
     299             :  *      CRC 32                          -- 32 bit
     300             :  *      compressed size                 -- 32 or 64 bit
     301             :  *      uncompressed size               -- 32 or 64 bit
     302             :  * \endcode
     303             :  *
     304             :  * When a trailing data buffer is defined, the header has the compressed
     305             :  * and uncompressed sizes set to zero.
     306             :  *
     307             :  * \note
     308             :  * Zipios++ does not support such a scheme.
     309             :  *
     310             :  * \return true if this file makes use of a trailing data buffer.
     311             :  */
     312       61097 : bool ZipLocalEntry::hasTrailingDataDescriptor() const
     313             : {
     314       61097 :     return (m_general_purpose_bitfield & g_trailing_data_descriptor) != 0;
     315             : }
     316             : 
     317             : 
     318             : /** \brief Read one local entry from \p is.
     319             :  *
     320             :  * This function verifies that the input stream starts with a local entry
     321             :  * signature. If so, it reads the input stream for a complete local entry.
     322             :  *
     323             :  * Calling this function first marks the FileEntry object as invalid. If
     324             :  * the read succeeds in full, then the entry is again marked as valid.
     325             :  *
     326             :  * If a read fails, the function throws an exception as defined in
     327             :  * the various zipRead() functions.
     328             :  *
     329             :  * \note
     330             :  * Some of the data found in the local entry on disk are not kept in
     331             :  * this class because there is nothing we can do with it.
     332             :  *
     333             :  * \param[in] is  The input stream to read from.
     334             :  */
     335      130489 : void ZipLocalEntry::read(std::istream& is)
     336             : {
     337      130489 :     m_valid = false; // set to true upon successful completion.
     338             : 
     339             :     //    // Before reading anything we record the position in the stream
     340             :     //    // This is a field in the central directory entry, but not
     341             :     //    // in the local entry. After all, we know where we are, anyway.
     342             :     //    zlh.rel_offset_loc_head  = is.tellg() ;
     343             : 
     344             :     uint32_t signature;
     345      130489 :     zipRead(is, signature);                             // 32
     346      130489 :     if(g_signature != signature)
     347             :     {
     348             :         // put stream in error state and return
     349          10 :         is.setstate(std::ios::failbit);
     350          10 :         throw IOException("ZipLocalEntry::read() expected a signature but got some other data");
     351             :     }
     352             : 
     353      130479 :     uint16_t compress_method(0);
     354      130479 :     uint32_t dostime(0);
     355      130479 :     uint32_t compressed_size(0);
     356      130479 :     uint32_t uncompressed_size(0);
     357      130479 :     uint16_t filename_len(0);
     358      130479 :     uint16_t extra_field_len(0);
     359      130479 :     std::string filename;
     360             : 
     361             :     // See the ZipLocalEntryHeader for more details
     362      130479 :     zipRead(is, m_extract_version);                 // 16
     363      130479 :     zipRead(is, m_general_purpose_bitfield);        // 16
     364      130479 :     zipRead(is, compress_method);                   // 16
     365      130479 :     zipRead(is, dostime);                           // 32
     366      130479 :     zipRead(is, m_crc_32);                          // 32
     367      130479 :     zipRead(is, compressed_size);                   // 32
     368      130479 :     zipRead(is, uncompressed_size);                 // 32
     369      130479 :     zipRead(is, filename_len);                      // 16
     370      130479 :     zipRead(is, extra_field_len);                   // 16
     371      130479 :     zipRead(is, filename, filename_len);            // string
     372      130479 :     zipRead(is, m_extra_field, extra_field_len);    // buffer
     373             :     /** \TODO add support for zip64, some of those parameters
     374             :      *        may be 0xFFFFF...FFFF in which case the 64 bit
     375             :      *        header should be read
     376             :      */
     377             : 
     378             :     // the FilePath() will remove the trailing slash so make sure
     379             :     // to defined the m_is_directory ahead of time!
     380      130479 :     m_is_directory = !filename.empty() && filename.back() == g_separator;
     381             : 
     382      130479 :     m_compress_method = static_cast<StorageMethod>(compress_method);
     383      130479 :     m_unix_time = dos2unixtime(dostime);
     384      130479 :     m_compressed_size = compressed_size;
     385      130479 :     m_uncompressed_size = uncompressed_size;
     386      130479 :     m_filename = FilePath(filename);
     387             : 
     388      130479 :     m_valid = true;
     389      130479 : }
     390             : 
     391             : 
     392             : /** \brief Write a ZipLocalEntry to \p os.
     393             :  *
     394             :  * This function writes this ZipLocalEntry header to the specified
     395             :  * output stream.
     396             :  *
     397             :  * \exception IOException
     398             :  * If an error occurs while writing to the output stream, the function
     399             :  * throws an IOException.
     400             :  *
     401             :  * \param[in] os  The output stream where the ZipLocalEntry is written.
     402             :  */
     403      266131 : void ZipLocalEntry::write(std::ostream& os)
     404             : {
     405      532262 :     if(m_filename.length()  > 0x10000
     406      266131 :     || m_extra_field.size() > 0x10000)
     407             :     {
     408           1 :         throw InvalidStateException("ZipLocalEntry::write(): file name or extra field too large to save in a Zip file.");
     409             :     }
     410             : 
     411             :     /** TODO: add support for 64 bit zip archive
     412             :      */
     413             : // Solaris defines _ILP32 for 32 bit platforms
     414             : #if !defined(_ILP32)
     415      266130 :     if(m_compressed_size   >= 0x100000000UL
     416      266130 :     || m_uncompressed_size >= 0x100000000UL)
     417             :     {
     418             :         // these are really big files, we do not currently test such so ignore in coverage
     419             :         //
     420             :         // Note: The compressed size is known at the end, we seek back to
     421             :         //       this header and resave it with the info; thus the error
     422             :         //       is caught then if it was not out of bounds earlier.
     423             :         throw InvalidStateException("The size of this file is too large to fit in a zip archive."); // LCOV_EXCL_LINE
     424             :     }
     425             : #endif
     426             : 
     427      266130 :     std::string filename(m_filename);
     428      266130 :     if(m_is_directory)
     429             :     {
     430       12066 :         filename += g_separator;
     431             :     }
     432             : 
     433      266130 :     uint16_t compress_method(static_cast<uint8_t>(m_compress_method));
     434      266130 :     if(m_compression_level == COMPRESSION_LEVEL_NONE)
     435             :     {
     436       14154 :         compress_method = static_cast<uint8_t>(StorageMethod::STORED);
     437             :     }
     438             : 
     439      266130 :     uint32_t dostime(unix2dostime(m_unix_time));
     440      266130 :     uint32_t compressed_size(m_compressed_size);
     441      266130 :     uint32_t uncompressed_size(m_uncompressed_size);
     442      266130 :     uint16_t filename_len(filename.length());
     443      266130 :     uint16_t extra_field_len(m_extra_field.size());
     444             : 
     445             :     // See the ZipLocalEntryHeader for more details
     446      266130 :     zipWrite(os, g_signature);                  // 32
     447      266130 :     zipWrite(os, m_extract_version);            // 16
     448      266130 :     zipWrite(os, m_general_purpose_bitfield);   // 16
     449      266130 :     zipWrite(os, compress_method);              // 16
     450      266130 :     zipWrite(os, dostime);                      // 32
     451      266130 :     zipWrite(os, m_crc_32);                     // 32
     452      266130 :     zipWrite(os, compressed_size);              // 32
     453      266130 :     zipWrite(os, uncompressed_size);            // 32
     454      266130 :     zipWrite(os, filename_len);                 // 16
     455      266130 :     zipWrite(os, extra_field_len);              // 16
     456      266130 :     zipWrite(os, filename);                     // string
     457      266130 :     zipWrite(os, m_extra_field);                // buffer
     458      266130 : }
     459             : 
     460             : 
     461           3 : } // zipios namespace
     462             : 
     463             : // Local Variables:
     464             : // mode: cpp
     465             : // indent-tabs-mode: nil
     466             : // c-basic-offset: 4
     467             : // tab-width: 4
     468             : // End:
     469             : 
     470             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.10