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: 2024-06-15 08:26:09 Functions: 12 13 92.3 %
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-2022  Made to Order Software Corp.  All Rights Reserved
       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      171443 : ZipLocalEntry::ZipLocalEntry()
     124      171443 :     : FileEntry(FilePath(""))
     125             : {
     126      171443 : }
     127             : 
     128             : 
     129             : /** \brief Copy of the ZipLocalEntry from any kind of FileEntry object.
     130             :  *
     131             :  * This function is used when copying a DirectoryEntry to a
     132             :  * ZipCentralDirectoryEntry object when creating a copy while
     133             :  * saving a Zip archive.
     134             :  *
     135             :  * \param[in] src  The source to copy in this new ZipLocalEntry.
     136             :  */
     137      121751 : ZipLocalEntry::ZipLocalEntry(FileEntry const & src)
     138             :     : FileEntry(src)
     139      121751 :     , m_is_directory(src.isDirectory())
     140             : {
     141      121751 : }
     142             : 
     143             : 
     144             : /** \brief Create a clone of a ZipLocalEntry object.
     145             :  *
     146             :  * This function allocates a new ZipLocalEntry on the heap and returns
     147             :  * a copy of this ZipLocalEntry object in it.
     148             :  *
     149             :  * \return A new ZipLocalEntry which is a clone of this ZipLocalEntry object.
     150             :  */
     151             : FileEntry::pointer_t ZipLocalEntry::clone() const // LCOV_EXCL_LINE
     152             : {
     153             :     // It makes sense to keep the clone() function for this class
     154             :     // but since it is internal and never allocated as is (we use
     155             :     // the ZipCentralDirectoryEntry instead) it is marked as
     156             :     // non-reachable by the coverage tests
     157             :     return std::make_shared<ZipLocalEntry>(*this); // LCOV_EXCL_LINE
     158             : }
     159             : 
     160             : 
     161             : /** \brief Clean up a ZipLocalEntry object.
     162             :  *
     163             :  * This function ensures proper clean up of a ZipLocationEntry object.
     164             :  */
     165      294270 : ZipLocalEntry::~ZipLocalEntry()
     166             : {
     167      294270 : }
     168             : 
     169             : 
     170             : /** \brief Check whether the filename represents a directory.
     171             :  *
     172             :  * This function checks the last character of the filename, if it
     173             :  * is a separator ('/') then the function returns true meaning
     174             :  * that the file represents a directory.
     175             :  *
     176             :  * \return true if the entry represents a directory.
     177             :  */
     178      749041 : bool ZipLocalEntry::isDirectory() const
     179             : {
     180      749041 :     return m_is_directory;
     181             : }
     182             : 
     183             : 
     184             : /** \brief Compare two file entries for equality.
     185             :  *
     186             :  * This function compares most of the fields between two file
     187             :  * entries to see whether they are equal or not.
     188             :  *
     189             :  * \note
     190             :  * This function calls the base class isEqual() and also verifies
     191             :  * that the ZipLocalEntry fields are equal.
     192             :  *
     193             :  * \note
     194             :  * This function is also used to compare ZipCDirEntry since none
     195             :  * of the additional field participate in the comparison.
     196             :  *
     197             :  * \param[in] file_entry  The file entry to compare this against.
     198             :  *
     199             :  * \return true if both FileEntry objects are considered equal.
     200             :  */
     201       59909 : bool ZipLocalEntry::isEqual(FileEntry const & file_entry) const
     202             : {
     203       59909 :     ZipLocalEntry const * const ze(dynamic_cast<ZipLocalEntry const * const>(&file_entry));
     204       59909 :     if(ze == nullptr)
     205             :     {
     206         738 :         return false;
     207             :     }
     208       59171 :     return FileEntry::isEqual(file_entry)
     209       59161 :         && m_extract_version          == ze->m_extract_version
     210       59161 :         && m_general_purpose_bitfield == ze->m_general_purpose_bitfield
     211      118332 :         && m_is_directory             == ze->m_is_directory;
     212             :         //&& m_compressed_size          == ze->m_compressed_size -- ignore in comparison
     213             : }
     214             : 
     215             : 
     216             : /** \brief Retrieve the size of the file when compressed.
     217             :  *
     218             :  * This function returns the compressed size of the entry. If the
     219             :  * entry is not stored in a compressed format, the uncompressed
     220             :  * size is returned.
     221             :  *
     222             :  * \return The compressed size of the entry.
     223             :  */
     224        7349 : size_t ZipLocalEntry::getCompressedSize() const
     225             : {
     226        7349 :     return m_compressed_size;
     227             : }
     228             : 
     229             : 
     230             : /** \brief Retrieve the size of the header.
     231             :  *
     232             :  * This function dynamically determines the size of the Zip archive
     233             :  * header necessary for this FileEntry.
     234             :  *
     235             :  * This function returns the size of the Zip archive header.
     236             :  *
     237             :  * \return The size of the header in bytes.
     238             :  */
     239      121750 : size_t ZipLocalEntry::getHeaderSize() const
     240             : {
     241             :     // Note that the structure is 32 bytes because of an alignment
     242             :     // and attempting to use options to avoid the alignment would
     243             :     // not be portable so we use a hard coded value (yuck!)
     244             :     return 30 /* sizeof(ZipLocalEntryHeader) */
     245      121750 :          + m_filename.length() + (m_is_directory ? 1 : 0)
     246      121750 :          + m_extra_field.size();
     247             : }
     248             : 
     249             : 
     250             : /** \brief Set the size when the file is compressed.
     251             :  *
     252             :  * This function saves the compressed size of the entry in this object.
     253             :  *
     254             :  * By default the compressed size is viewed as the same as the
     255             :  * uncompressed size (i.e. as if STORED was used for the compression
     256             :  * method.)
     257             :  *
     258             :  * \param[in] size  Value to set the compressed size field of the entry to.
     259             :  */
     260      121750 : void ZipLocalEntry::setCompressedSize(size_t size)
     261             : {
     262      121750 :     m_compressed_size = size;
     263      121750 : }
     264             : 
     265             : 
     266             : /** \brief Save the CRC of the entry.
     267             :  *
     268             :  * This function saves the CRC field in this FileEntry field.
     269             :  *
     270             :  * This function has the side of setting the m_has_crc_32 flag
     271             :  * to true meaning that the CRC was defined as expected.
     272             :  *
     273             :  * \param[in] crc  Value to set the CRC field to.
     274             :  */
     275      121750 : void ZipLocalEntry::setCrc(uint32_t crc)
     276             : {
     277      121750 :     m_crc_32 = crc;
     278      121750 :     m_has_crc_32 = true;
     279      121750 : }
     280             : 
     281             : 
     282             : /** \brief Is there a trailing data descriptor?
     283             :  *
     284             :  * This function checks the bit in the General Purpose Flags to know
     285             :  * whether the local entry header includes the compressed and uncompressed
     286             :  * sizes or whether these are defined after the compressed data.
     287             :  *
     288             :  * The trailing data buffer looks like this:
     289             :  *
     290             :  * \code
     291             :  *      signature (PK 8.7) -- OPTIONAL  -- 32 bit
     292             :  *      CRC 32                          -- 32 bit
     293             :  *      compressed size                 -- 32 or 64 bit
     294             :  *      uncompressed size               -- 32 or 64 bit
     295             :  * \endcode
     296             :  *
     297             :  * When a trailing data buffer is defined, the header has the compressed
     298             :  * and uncompressed sizes set to zero.
     299             :  *
     300             :  * \note
     301             :  * Zipios does not support such a scheme.
     302             :  *
     303             :  * \return true if this file makes use of a trailing data buffer.
     304             :  */
     305       53061 : bool ZipLocalEntry::hasTrailingDataDescriptor() const
     306             : {
     307       53061 :     return (m_general_purpose_bitfield & g_trailing_data_descriptor) != 0;
     308             : }
     309             : 
     310             : 
     311             : /** \brief Read one local entry from \p is.
     312             :  *
     313             :  * This function verifies that the input stream starts with a local entry
     314             :  * signature. If so, it reads the input stream for a complete local entry.
     315             :  *
     316             :  * Calling this function first marks the FileEntry object as invalid. If
     317             :  * the read succeeds in full, then the entry is again marked as valid.
     318             :  *
     319             :  * If a read fails, the function throws an exception as defined in
     320             :  * the various zipRead() functions.
     321             :  *
     322             :  * \note
     323             :  * Some of the data found in the local entry on disk are not kept in
     324             :  * this class because there is nothing we can do with it.
     325             :  *
     326             :  * \param[in] is  The input stream to read from.
     327             :  */
     328      112242 : void ZipLocalEntry::read(std::istream & is)
     329             : {
     330      112242 :     m_valid = false; // set to true upon successful completion.
     331             : 
     332             :     //    // Before reading anything we record the position in the stream
     333             :     //    // This is a field in the central directory entry, but not
     334             :     //    // in the local entry. After all, we know where we are, anyway.
     335             :     //    zlh.rel_offset_loc_head  = is.tellg() ;
     336             : 
     337      112242 :     uint32_t signature(0);
     338      112242 :     zipRead(is, signature);                             // 32
     339      112242 :     if(g_signature != signature)
     340             :     {
     341             :         // put stream in error state and return
     342          10 :         is.setstate(std::ios::failbit);
     343          10 :         throw IOException("ZipLocalEntry::read() expected a signature but got some other data");
     344             :     }
     345             : 
     346      112232 :     uint16_t compress_method(0);
     347      112232 :     uint32_t dosdatetime(0);
     348      112232 :     uint32_t compressed_size(0);
     349      112232 :     uint32_t uncompressed_size(0);
     350      112232 :     uint16_t filename_len(0);
     351      112232 :     uint16_t extra_field_len(0);
     352      112232 :     std::string filename;
     353             : 
     354             :     // See the ZipLocalEntryHeader for more details
     355      112232 :     zipRead(is, m_extract_version);                 // 16
     356      112232 :     zipRead(is, m_general_purpose_bitfield);        // 16
     357      112232 :     zipRead(is, compress_method);                   // 16
     358      112232 :     zipRead(is, dosdatetime);                       // 32
     359      112232 :     zipRead(is, m_crc_32);                          // 32
     360      112232 :     zipRead(is, compressed_size);                   // 32
     361      112232 :     zipRead(is, uncompressed_size);                 // 32
     362      112232 :     zipRead(is, filename_len);                      // 16
     363      112232 :     zipRead(is, extra_field_len);                   // 16
     364      112232 :     zipRead(is, filename, filename_len);            // string
     365      112232 :     zipRead(is, m_extra_field, extra_field_len);    // buffer
     366             :     /** \todo add support for zip64, some of those parameters
     367             :      *        may be 0xFFFFF...FFFF in which case the 64 bit
     368             :      *        header should be read
     369             :      */
     370             : 
     371             :     // the FilePath() will remove the trailing slash so make sure
     372             :     // to defined the m_is_directory ahead of time!
     373      112232 :     m_is_directory = !filename.empty() && filename.back() == g_separator;
     374             : 
     375      112232 :     m_compress_method = static_cast<StorageMethod>(compress_method);
     376      112232 :     DOSDateTime t;
     377      112232 :     t.setDOSDateTime(dosdatetime);
     378      112232 :     m_unix_time = t.getUnixTimestamp();
     379      112232 :     m_compressed_size = compressed_size;
     380      112232 :     m_uncompressed_size = uncompressed_size;
     381      112232 :     m_filename = FilePath(filename);
     382             : 
     383      112232 :     m_valid = true;
     384      112232 : }
     385             : 
     386             : 
     387             : /** \brief Write a ZipLocalEntry to \p os.
     388             :  *
     389             :  * This function writes this ZipLocalEntry header to the specified
     390             :  * output stream.
     391             :  *
     392             :  * \exception IOException
     393             :  * If an error occurs while writing to the output stream, the function
     394             :  * throws an IOException.
     395             :  *
     396             :  * \param[in] os  The output stream where the ZipLocalEntry is written.
     397             :  */
     398      243501 : void ZipLocalEntry::write(std::ostream & os)
     399             : {
     400      243501 :     if(m_filename.length()  > 0x10000
     401      243501 :     || m_extra_field.size() > 0x10000)
     402             :     {
     403           1 :         throw InvalidStateException("ZipLocalEntry::write(): file name or extra field too large to save in a Zip file.");
     404             :     }
     405             : 
     406             :     /** todo: add support for 64 bit zip archive
     407             :      */
     408             : #if INTPTR_MAX != INT32_MAX
     409      243500 :     if(m_compressed_size   >= 0x100000000UL
     410      243500 :     || m_uncompressed_size >= 0x100000000UL)
     411             :     {
     412             :         // these are really big files, we do not currently test such so ignore in coverage
     413             :         //
     414             :         // Note: The compressed size is known at the end, we seek back to
     415             :         //       this header and re-save it with the info; thus the error
     416             :         //       is caught then if it was not out of bounds earlier.
     417             :         throw InvalidStateException("The size of this file is too large to fit in a 32 bit zip archive."); // LCOV_EXCL_LINE
     418             :     }
     419             : #endif
     420             : 
     421      243500 :     std::string filename(m_filename);
     422      243500 :     if(m_is_directory)
     423             :     {
     424        9630 :         filename += g_separator;
     425             :     }
     426             : 
     427      243500 :     std::uint16_t compress_method(static_cast<uint8_t>(m_compress_method));
     428      243500 :     if(m_compression_level == COMPRESSION_LEVEL_NONE)
     429             :     {
     430       13166 :         compress_method = static_cast<uint8_t>(StorageMethod::STORED);
     431             :     }
     432             : 
     433      243500 :     DOSDateTime t;
     434      243500 :     t.setUnixTimestamp(m_unix_time);
     435      243500 :     std::uint32_t dosdatetime(t.getDOSDateTime());       // type could use DOSDateTime::dosdatetime_t
     436      243500 :     std::uint32_t compressed_size(m_compressed_size);
     437      243500 :     std::uint32_t uncompressed_size(m_uncompressed_size);
     438      243500 :     std::uint16_t filename_len(filename.length());
     439      243500 :     std::uint16_t extra_field_len(m_extra_field.size());
     440             : 
     441             :     // See the ZipLocalEntryHeader for more details
     442      243500 :     zipWrite(os, g_signature);                  // 32
     443      243500 :     zipWrite(os, m_extract_version);            // 16
     444      243500 :     zipWrite(os, m_general_purpose_bitfield);   // 16
     445      243500 :     zipWrite(os, compress_method);              // 16
     446      243500 :     zipWrite(os, dosdatetime);                  // 32
     447      243500 :     zipWrite(os, m_crc_32);                     // 32
     448      243500 :     zipWrite(os, compressed_size);              // 32
     449      243500 :     zipWrite(os, uncompressed_size);            // 32
     450      243500 :     zipWrite(os, filename_len);                 // 16
     451      243500 :     zipWrite(os, extra_field_len);              // 16
     452      243500 :     zipWrite(os, filename);                     // string
     453      243500 :     zipWrite(os, m_extra_field);                // buffer
     454      243500 : }
     455             : 
     456             : 
     457             : } // zipios namespace
     458             : 
     459             : // Local Variables:
     460             : // mode: cpp
     461             : // indent-tabs-mode: nil
     462             : // c-basic-offset: 4
     463             : // tab-width: 4
     464             : // End:
     465             : 
     466             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.14

Snap C++ | List of projects | List of versions