LCOV - code coverage report
Current view: top level - src - ziplocalentry.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 99 99 100.0 %
Date: 2019-04-24 14:10:30 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-2019  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.1 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  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             : #include "zipios/dosdatetime.hpp"
      33             : 
      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 endianness 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_dosdatetime;
      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      313973 : ZipLocalEntry::ZipLocalEntry()
     124      313973 :     : 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      313973 : }
     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      171467 : 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      171467 :     , m_is_directory(src.isDirectory())
     146             :     //, m_compressed_size(0) -- auto-init
     147             : {
     148      171467 : }
     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      486513 : ZipLocalEntry::~ZipLocalEntry()
     173             : {
     174      486513 : }
     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     1350672 : bool ZipLocalEntry::isDirectory() const
     186             : {
     187     1350672 :     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      109722 : bool ZipLocalEntry::isEqual(FileEntry const & file_entry) const
     209             : {
     210      109722 :     ZipLocalEntry const * const ze(dynamic_cast<ZipLocalEntry const * const>(&file_entry));
     211      109722 :     if(ze == nullptr)
     212             :     {
     213        1137 :         return false;
     214             :     }
     215      108585 :     return FileEntry::isEqual(file_entry)
     216      108575 :         && m_extract_version          == ze->m_extract_version
     217      108575 :         && m_general_purpose_bitfield == ze->m_general_purpose_bitfield
     218      217160 :         && m_is_directory             == ze->m_is_directory;
     219             :         //&& m_compressed_size          == ze->m_compressed_size -- ignore in comparison
     220             : }
     221             : 
     222             : 
     223             : /** \brief Retrieve 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       12482 : size_t ZipLocalEntry::getCompressedSize() const
     232             : {
     233       12482 :     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      171466 : 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      171466 :          + m_filename.length() + (m_is_directory ? 1 : 0)
     253      171466 :          + 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      171466 : void ZipLocalEntry::setCompressedSize(size_t size)
     268             : {
     269      171466 :     m_compressed_size = size;
     270      171466 : }
     271             : 
     272             : 
     273             : /** \brief Save the CRC of the entry.
     274             :  *
     275             :  * This function saves 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      171466 : void ZipLocalEntry::setCrc(uint32_t crc)
     283             : {
     284      171466 :     m_crc_32 = crc;
     285      171466 :     m_has_crc_32 = true;
     286      171466 : }
     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       96763 : bool ZipLocalEntry::hasTrailingDataDescriptor() const
     313             : {
     314       96763 :     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      205358 : void ZipLocalEntry::read(std::istream& is)
     336             : {
     337      205358 :     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      205358 :     zipRead(is, signature);                             // 32
     346      205358 :     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      205348 :     uint16_t compress_method(0);
     354      205348 :     uint32_t dosdatetime(0);
     355      205348 :     uint32_t compressed_size(0);
     356      205348 :     uint32_t uncompressed_size(0);
     357      205348 :     uint16_t filename_len(0);
     358      205348 :     uint16_t extra_field_len(0);
     359      410696 :     std::string filename;
     360             : 
     361             :     // See the ZipLocalEntryHeader for more details
     362      205348 :     zipRead(is, m_extract_version);                 // 16
     363      205348 :     zipRead(is, m_general_purpose_bitfield);        // 16
     364      205348 :     zipRead(is, compress_method);                   // 16
     365      205348 :     zipRead(is, dosdatetime);                       // 32
     366      205348 :     zipRead(is, m_crc_32);                          // 32
     367      205348 :     zipRead(is, compressed_size);                   // 32
     368      205348 :     zipRead(is, uncompressed_size);                 // 32
     369      205348 :     zipRead(is, filename_len);                      // 16
     370      205348 :     zipRead(is, extra_field_len);                   // 16
     371      205348 :     zipRead(is, filename, filename_len);            // string
     372      205348 :     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      205348 :     m_is_directory = !filename.empty() && filename.back() == g_separator;
     381             : 
     382      205348 :     m_compress_method = static_cast<StorageMethod>(compress_method);
     383      205348 :     DOSDateTime t;
     384      205348 :     t.setDOSDateTime(dosdatetime);
     385      205348 :     m_unix_time = t.getUnixTimestamp();
     386      205348 :     m_compressed_size = compressed_size;
     387      205348 :     m_uncompressed_size = uncompressed_size;
     388      205348 :     m_filename = FilePath(filename);
     389             : 
     390      205348 :     m_valid = true;
     391      205348 : }
     392             : 
     393             : 
     394             : /** \brief Write a ZipLocalEntry to \p os.
     395             :  *
     396             :  * This function writes this ZipLocalEntry header to the specified
     397             :  * output stream.
     398             :  *
     399             :  * \exception IOException
     400             :  * If an error occurs while writing to the output stream, the function
     401             :  * throws an IOException.
     402             :  *
     403             :  * \param[in] os  The output stream where the ZipLocalEntry is written.
     404             :  */
     405      342933 : void ZipLocalEntry::write(std::ostream& os)
     406             : {
     407      685866 :     if(m_filename.length()  > 0x10000
     408      342933 :     || m_extra_field.size() > 0x10000)
     409             :     {
     410           1 :         throw InvalidStateException("ZipLocalEntry::write(): file name or extra field too large to save in a Zip file.");
     411             :     }
     412             : 
     413             :     /** todo: add support for 64 bit zip archive
     414             :      */
     415             : // Solaris defines _ILP32 for 32 bit platforms
     416             : #if !defined(_ILP32)
     417      342932 :     if(m_compressed_size   >= 0x100000000UL
     418      342932 :     || m_uncompressed_size >= 0x100000000UL)
     419             :     {
     420             :         // these are really big files, we do not currently test such so ignore in coverage
     421             :         //
     422             :         // Note: The compressed size is known at the end, we seek back to
     423             :         //       this header and resave it with the info; thus the error
     424             :         //       is caught then if it was not out of bounds earlier.
     425             :         throw InvalidStateException("The size of this file is too large to fit in a zip archive."); // LCOV_EXCL_LINE
     426             :     }
     427             : #endif
     428             : 
     429      685864 :     std::string filename(m_filename);
     430      342932 :     if(m_is_directory)
     431             :     {
     432       20556 :         filename += g_separator;
     433             :     }
     434             : 
     435      342932 :     uint16_t compress_method(static_cast<uint8_t>(m_compress_method));
     436      342932 :     if(m_compression_level == COMPRESSION_LEVEL_NONE)
     437             :     {
     438       24108 :         compress_method = static_cast<uint8_t>(StorageMethod::STORED);
     439             :     }
     440             : 
     441      342932 :     DOSDateTime t;
     442      342932 :     t.setUnixTimestamp(m_unix_time);
     443      342932 :     uint32_t dosdatetime(t.getDOSDateTime());       // type could use DOSDateTime::dosdatetime_t
     444      342932 :     uint32_t compressed_size(m_compressed_size);
     445      342932 :     uint32_t uncompressed_size(m_uncompressed_size);
     446      342932 :     uint16_t filename_len(filename.length());
     447      342932 :     uint16_t extra_field_len(m_extra_field.size());
     448             : 
     449             :     // See the ZipLocalEntryHeader for more details
     450      342932 :     zipWrite(os, g_signature);                  // 32
     451      342932 :     zipWrite(os, m_extract_version);            // 16
     452      342932 :     zipWrite(os, m_general_purpose_bitfield);   // 16
     453      342932 :     zipWrite(os, compress_method);              // 16
     454      342932 :     zipWrite(os, dosdatetime);                  // 32
     455      342932 :     zipWrite(os, m_crc_32);                     // 32
     456      342932 :     zipWrite(os, compressed_size);              // 32
     457      342932 :     zipWrite(os, uncompressed_size);            // 32
     458      342932 :     zipWrite(os, filename_len);                 // 16
     459      342932 :     zipWrite(os, extra_field_len);              // 16
     460      342932 :     zipWrite(os, filename);                     // string
     461      342932 :     zipWrite(os, m_extra_field);                // buffer
     462      342932 : }
     463             : 
     464             : 
     465           3 : } // zipios namespace
     466             : 
     467             : // Local Variables:
     468             : // mode: cpp
     469             : // indent-tabs-mode: nil
     470             : // c-basic-offset: 4
     471             : // tab-width: 4
     472             : // End:
     473             : 
     474             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.12