LCOV - code coverage report
Current view: top level - src - zipcentraldirectoryentry.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 109 109 100.0 %
Date: 2024-06-15 08:26:09 Functions: 7 8 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-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 zipios::ZipCentralDirectoryEntry.
      24             :  *
      25             :  * This file includes the implementation of the zipios::ZipCentralDirectoryEntry
      26             :  * which is a zipios::FileEntry used when reading the central
      27             :  * directory of a Zip archive.
      28             :  */
      29             : 
      30             : #include "zipcentraldirectoryentry.hpp"
      31             : 
      32             : #include "zipios/zipiosexceptions.hpp"
      33             : #include "zipios/dosdatetime.hpp"
      34             : 
      35             : #include "zipios_common.hpp"
      36             : 
      37             : 
      38             : namespace zipios
      39             : {
      40             : 
      41             : 
      42             : 
      43             : namespace
      44             : {
      45             : 
      46             : 
      47             : /** \brief The signature of a ZipCentralDirectoryEntry.
      48             :  *
      49             :  * This value represents the signature of a Zip Central Directory Entry.
      50             :  *
      51             :  * The signature represents:
      52             :  *
      53             :  * \code
      54             :  * "PK 1.2"
      55             :  * \endcode
      56             :  */
      57             : uint32_t const  g_signature = 0x02014b50;
      58             : 
      59             : 
      60             : // The zip codes (values are pre-shifted)
      61             : uint16_t const   g_msdos         = 0x0000;
      62             : uint16_t const   g_amiga         = 0x0100;
      63             : uint16_t const   g_open_vms      = 0x0200;
      64             : uint16_t const   g_unix          = 0x0300;
      65             : uint16_t const   g_vm_cms        = 0x0400;
      66             : uint16_t const   g_atari_st      = 0x0500;
      67             : uint16_t const   g_os2_hpfs      = 0x0600;
      68             : uint16_t const   g_macintosh     = 0x0700;
      69             : uint16_t const   g_z_system      = 0x0800;
      70             : uint16_t const   g_cpm           = 0x0900;
      71             : uint16_t const   g_windows       = 0x0A00;
      72             : uint16_t const   g_mvs           = 0x0B00;
      73             : uint16_t const   g_vse           = 0x0C00;
      74             : uint16_t const   g_acorn_risc    = 0x0D00;
      75             : uint16_t const   g_vfat          = 0x0E00;
      76             : uint16_t const   g_alternate_vms = 0x0F00;
      77             : uint16_t const   g_beos          = 0x1000;
      78             : uint16_t const   g_tandem        = 0x1100;
      79             : uint16_t const   g_os400         = 0x1200;
      80             : uint16_t const   g_osx           = 0x1300;
      81             : 
      82             : 
      83             : /** \brief The header of a ZipCentralDirectoryEntry in a Zip archive.
      84             :  *
      85             :  * This structure shows how the header of the ZipCentralDirectoryEntry is defined.
      86             :  * Note that the file name, file comment, and extra field have a
      87             :  * variable size which is defined in three 16 bit values before
      88             :  * they appear.
      89             :  *
      90             :  * The filename cannot be empty, however, the file comment and the
      91             :  * extra field can (and usually are.)
      92             :  *
      93             :  * \note
      94             :  * This structure is NOT used directly only for its sizeof() and
      95             :  * documentation because that way zipios can work on little and big
      96             :  * endians without the need to know the endianness of your computer.
      97             :  *
      98             :  * \bug
      99             :  * This structure is aligned on 4 bytes since it includes some uint32_t
     100             :  * values and thus has a size of 48 bytes instead of 46.
     101             :  */
     102             : struct ZipCentralDirectoryEntryHeader
     103             : {
     104             :     uint32_t        m_signature;
     105             :     uint16_t        m_writer_version;
     106             :     uint16_t        m_extract_version;
     107             :     uint16_t        m_general_purpose_bitfield;
     108             :     uint16_t        m_compress_method;
     109             :     uint32_t        m_dosdatetime;
     110             :     uint32_t        m_crc_32;
     111             :     uint32_t        m_compressed_size;
     112             :     uint32_t        m_uncompressed_size;
     113             :     uint16_t        m_filename_len;
     114             :     uint16_t        m_extra_field_len;
     115             :     uint16_t        m_file_comment_len;
     116             :     uint16_t        m_disk_num_start;
     117             :     uint16_t        m_intern_file_attr;
     118             :     uint32_t        m_extern_file_attr;
     119             :     uint32_t        m_relative_offset_location_header;
     120             :     //uint8_t       m_filename[m_filename_len];
     121             :     //uint8_t       m_extra_field[m_extra_field_len];
     122             :     //uint8_t       m_file_comment[m_file_comment_len];
     123             : };
     124             : 
     125             : 
     126             : } // no name namespace
     127             : 
     128             : 
     129             : /** \class ZipCentralDirectoryEntry
     130             :  * \brief A specialization of ZipLocalEntry for
     131             :  *
     132             :  * Specialization of ZipLocalEntry, that add fields for storing the
     133             :  * extra information, that is only present in the entries in the zip
     134             :  * central directory and not in the local entry headers.
     135             :  */
     136             : 
     137             : 
     138             : /** \brief Initializes a default ZipCentralDirectoryEntry object.
     139             :  *
     140             :  * This function initializes a ZipCentralDirectoryEntry object that is
     141             :  * expected to be used to read data from an input stream representing
     142             :  * a Zip archive.
     143             :  */
     144       59201 : ZipCentralDirectoryEntry::ZipCentralDirectoryEntry()
     145             : {
     146       59201 : }
     147             : 
     148             : 
     149             : /** \brief Initialize a ZipCentralDirectoryEntry.
     150             :  *
     151             :  * This function initializes a ZipCentralDirectoryEntry from a FileEntry.
     152             :  *
     153             :  * The function copies all the data it may be interested in and then
     154             :  * ignores the FileEntry.
     155             :  *
     156             :  * \param[in] entry  The entry to transform in a ZipCentralDirectoryEntry.
     157             :  */
     158      121751 : ZipCentralDirectoryEntry::ZipCentralDirectoryEntry(FileEntry const & entry)
     159      121751 :     : ZipLocalEntry(entry)
     160             : {
     161      121751 : }
     162             : 
     163             : 
     164             : /** \brief Clean up the entry.
     165             :  *
     166             :  * The destructor makes sure the entry is fully cleaned up.
     167             :  */
     168      182028 : ZipCentralDirectoryEntry::~ZipCentralDirectoryEntry()
     169             : {
     170      182028 : }
     171             : 
     172             : 
     173             : /** \brief Compute and return the current header size.
     174             :  *
     175             :  * This function computes the size that this entry will take in the
     176             :  * Central Directory of the Zip archive.
     177             :  *
     178             :  * \return The total size of the Central Directory entry on disk.
     179             :  */
     180      121749 : size_t ZipCentralDirectoryEntry::getHeaderSize() const
     181             : {
     182             :     /** \todo
     183             :      * Add support for 64 bit Zip. At this time this function returns
     184             :      * an invalid size if the filename, extra field, or file comment
     185             :      * sizes are more than allowed in an older version of the Zip format.
     186             :      */
     187             :     // Note that the structure is 48 bytes because of an alignment
     188             :     // and attempting to use options to avoid the alignment would
     189             :     // not be portable so we use a hard coded value (yuck!)
     190             :     return 46 /* sizeof(ZipCentralDirectoryEntryHeader) */
     191      121749 :          + m_filename.length() + (m_is_directory ? 1 : 0)
     192      121749 :          + m_extra_field.size()
     193      121749 :          + m_comment.length();
     194             : }
     195             : 
     196             : 
     197             : /** \brief Create a clone of this Central Directory entry.
     198             :  *
     199             :  * This function allocates a new copy of this ZipCentralDirectoryEntry
     200             :  * object and returns a smart pointer to it.
     201             :  *
     202             :  * \return A smart pointer to the copy.
     203             :  */
     204        1076 : FileEntry::pointer_t ZipCentralDirectoryEntry::clone() const
     205             : {
     206        1076 :     return std::make_shared<ZipCentralDirectoryEntry>(*this);
     207             : }
     208             : 
     209             : 
     210             : /** \brief Read a Central Directory entry.
     211             :  *
     212             :  * This function reads one Central Directory entry from the specified
     213             :  * input stream. If anything goes wrong with the input stream, the read
     214             :  * function will throw an error.
     215             :  *
     216             :  * \note
     217             :  * While reading the entry is marked as invalid. If the read fails, the
     218             :  * entry will remain invalid. On success, the function restores the status
     219             :  * back to valid.
     220             :  *
     221             :  * \note
     222             :  * If the signature or some other parameter is found to be invalid, then
     223             :  * the input stream is marked as failed and an exception is thrown.
     224             :  *
     225             :  * \exception IOException
     226             :  * This exception is thrown if the signature read does not match the
     227             :  * signature of a Central Directory entry. This can only mean a bug
     228             :  * in a Zip writer or an invalid/corrupt file altogether.
     229             :  *
     230             :  * \param[in] is  The input stream to read from.
     231             :  *
     232             :  * \sa write()
     233             :  */
     234       59201 : void ZipCentralDirectoryEntry::read(std::istream & is)
     235             : {
     236       59201 :     m_valid = false; // set back to true upon successful completion below.
     237             : 
     238             :     // verify the signature
     239             :     uint32_t signature;
     240       59201 :     zipRead(is, signature);
     241       59201 :     if(g_signature != signature)
     242             :     {
     243          10 :         is.setstate(std::ios::failbit);
     244          10 :         throw IOException("ZipCentralDirectoryEntry::read(): Expected Central Directory entry signature not found");
     245             :     }
     246             : 
     247       59191 :     uint16_t writer_version(0);
     248       59191 :     uint16_t compress_method(0);
     249       59191 :     uint32_t dosdatetime(0);
     250       59191 :     uint32_t compressed_size(0);
     251       59191 :     uint32_t uncompressed_size(0);
     252       59191 :     uint32_t rel_offset_loc_head(0);
     253       59191 :     uint16_t filename_len(0);
     254       59191 :     uint16_t extra_field_len(0);
     255       59191 :     uint16_t file_comment_len(0);
     256       59191 :     uint16_t intern_file_attr(0);
     257       59191 :     uint32_t extern_file_attr(0);
     258       59191 :     uint16_t disk_num_start(0);
     259       59191 :     std::string filename;
     260             : 
     261             :     // read the header
     262       59191 :     zipRead(is, writer_version);                    // 16
     263       59191 :     zipRead(is, m_extract_version);                 // 16
     264       59191 :     zipRead(is, m_general_purpose_bitfield);        // 16
     265       59191 :     zipRead(is, compress_method);                   // 16
     266       59191 :     zipRead(is, dosdatetime);                       // 32
     267       59191 :     zipRead(is, m_crc_32);                          // 32
     268       59191 :     zipRead(is, compressed_size);                   // 32
     269       59191 :     zipRead(is, uncompressed_size);                 // 32
     270       59191 :     zipRead(is, filename_len);                      // 16
     271       59191 :     zipRead(is, extra_field_len);                   // 16
     272       59191 :     zipRead(is, file_comment_len);                  // 16
     273       59191 :     zipRead(is, disk_num_start);                    // 16
     274       59191 :     zipRead(is, intern_file_attr);                  // 16
     275       59191 :     zipRead(is, extern_file_attr);                  // 32
     276       59191 :     zipRead(is, rel_offset_loc_head);               // 32
     277       59191 :     zipRead(is, filename, filename_len);            // string
     278       59191 :     zipRead(is, m_extra_field, extra_field_len);    // buffer
     279       59191 :     zipRead(is, m_comment, file_comment_len);       // string
     280             :     /** \todo check whether this was a 64 bit header and make sure
     281             :      *        to read the 64 bit header too if so
     282             :      */
     283             : 
     284             :     // the FilePath() will remove the trailing slash so make sure
     285             :     // to defined the m_is_directory ahead of time!
     286       59191 :     m_is_directory = !filename.empty() && filename.back() == g_separator;
     287             : 
     288       59191 :     m_compress_method = static_cast<StorageMethod>(compress_method);
     289       59191 :     DOSDateTime t;
     290       59191 :     t.setDOSDateTime(dosdatetime);
     291       59191 :     m_unix_time = t.getUnixTimestamp();
     292       59191 :     m_compressed_size = compressed_size;
     293       59191 :     m_uncompressed_size = uncompressed_size;
     294       59191 :     m_entry_offset = rel_offset_loc_head;
     295       59191 :     m_filename = FilePath(filename);
     296             : 
     297             :     // the zipRead() should throw if it is false...
     298       59191 :     m_valid = true;
     299       59191 : }
     300             : 
     301             : 
     302             : /** \brief Write a Central Directory Entry to the output stream.
     303             :  *
     304             :  * This function verifies that the data of the Central Directory entry
     305             :  * can be written to disk. If so, then it writes a block. The size of
     306             :  * the blocks varies depending on the filename, file comment, and extra
     307             :  * data. The current size can be determined using the getHeaderSize()
     308             :  * function.
     309             :  *
     310             :  * \warning
     311             :  * The function saves the filename with an ending separator in case
     312             :  * the entry is marked as a directory entry. Note that Zip only really
     313             :  * knows about the trailing slash as a way to detect a file as a
     314             :  * directory.
     315             :  *
     316             :  * \exception InvalidStateException
     317             :  * The function verifies whether the filename, extra field,
     318             :  * file comment, file data, or data offset are not too large.
     319             :  * If any one of these parameters is too large, then this
     320             :  * exception is raised.
     321             :  *
     322             :  * \param[in] os  The output stream where the data is written.
     323             :  *
     324             :  * \sa getHeaderSize()
     325             :  * \sa read()
     326             :  */
     327      121751 : void ZipCentralDirectoryEntry::write(std::ostream & os)
     328             : {
     329             :     /** \todo add support for 64 bit entries
     330             :      *        (zip64 is available, just need to add a 64 bit header...)
     331             :      */
     332      121751 :     if(m_filename.length()  > 0x10000
     333      121751 :     || m_extra_field.size() > 0x10000
     334      243502 :     || m_comment.length()   > 0x10000)
     335             :     {
     336           2 :         throw InvalidStateException("ZipCentralDirectoryEntry::write(): file name, comment, or extra field too large to save in a Zip file.");
     337             :     }
     338             : 
     339             : // Solaris defines _ILP32 for 32 bit platforms
     340             : #if INTPTR_MAX != INT32_MAX
     341      243498 :     if(m_compressed_size   >= 0x100000000ULL
     342      121749 :     || m_uncompressed_size >= 0x100000000ULL
     343      243498 :     || m_entry_offset      >= 0x100000000LL)
     344             :     {
     345             :         // This represents really large files which we do not test at this point
     346             :         throw InvalidStateException("ZipCentralDirectoryEntry::write(): The size of this file is too large to fit in a zip archive."); // LCOV_EXCL_LINE
     347             :     }
     348             : #endif
     349             : 
     350             :     // define version
     351      121749 :     uint16_t writer_version = g_zip_format_version;
     352             :     // including the "compatibility" code
     353             : #if defined(WIN32) || defined(_WIN32) || defined(__WIN32)
     354             :     // MS-Windows
     355             :     // TBD: should we use g_msdos instead?
     356             :     writer_version |= g_windows;
     357             : #elif defined(__APPLE__) && defined(__MACH__)
     358             :     // OS/X
     359             :     writer_version |= g_osx;
     360             : #else
     361             :     // Other Unices
     362      121749 :     writer_version |= g_unix;
     363             : #endif
     364             : 
     365      121749 :     std::string filename(m_filename);
     366      121749 :     if(m_is_directory)
     367             :     {
     368             :         // add a trailing separator for directories
     369             :         // (this is VERY important for zip files which do not otherwise
     370             :         // indicate that a file is a directory)
     371        4815 :         filename += g_separator;
     372             :     }
     373             : 
     374      121749 :     uint16_t compress_method(static_cast<uint8_t>(m_compress_method));
     375      121749 :     if(m_compression_level == COMPRESSION_LEVEL_NONE)
     376             :     {
     377        6583 :         compress_method = static_cast<uint8_t>(StorageMethod::STORED);
     378             :     }
     379             : 
     380      121749 :     DOSDateTime t;
     381      121749 :     t.setUnixTimestamp(m_unix_time);
     382      121749 :     uint32_t dosdatetime(t.getDOSDateTime());   // type could be set to DOSDateTime::dosdatetime_t
     383      121749 :     uint32_t compressed_size(m_compressed_size);
     384      121749 :     uint32_t uncompressed_size(m_uncompressed_size);
     385      121749 :     uint16_t filename_len(filename.length());
     386      121749 :     uint16_t extra_field_len(m_extra_field.size());
     387      121749 :     uint16_t file_comment_len(m_comment.length());
     388      121749 :     uint16_t disk_num_start(0);
     389      121749 :     uint16_t intern_file_attr(0);
     390             :     /** \FIXME
     391             :      * The external_file_attr supports the standard Unix
     392             :      * permissions in the higher 16 bits defined as:
     393             :      *
     394             :      *    \<type> \<rwx> \<rwx> \<rwx>
     395             :      *
     396             :      * The \<type> is the top 4 bits and is set to either 8 or 4:
     397             :      *
     398             :      * \li 8 for regular files
     399             :      * \li 4 for directories
     400             :      *
     401             :      * The \<rwx> are the standard permission flags representing the
     402             :      * owner, group, and other read/write/execute permissions.
     403             :      *
     404             :      * The value also includes the special flags SUID, SGID and VTX.
     405             :      *
     406             :      * So to have a fix here we need to have a way to read those flags
     407             :      * from the file entry.
     408             :      */
     409      121749 :     uint32_t extern_file_attr(m_is_directory ? 0x41FD0010 : 0x81B40000);
     410      121749 :     uint32_t rel_offset_loc_head(m_entry_offset);
     411             : 
     412      121749 :     zipWrite(os, g_signature);                  // 32
     413      121749 :     zipWrite(os, writer_version);               // 16
     414      121749 :     zipWrite(os, m_extract_version);            // 16
     415      121749 :     zipWrite(os, m_general_purpose_bitfield);   // 16
     416      121749 :     zipWrite(os, compress_method);              // 16
     417      121749 :     zipWrite(os, dosdatetime);                  // 32
     418      121749 :     zipWrite(os, m_crc_32);                     // 32
     419      121749 :     zipWrite(os, compressed_size);              // 32
     420      121749 :     zipWrite(os, uncompressed_size);            // 32
     421      121749 :     zipWrite(os, filename_len);                 // 16
     422      121749 :     zipWrite(os, extra_field_len);              // 16
     423      121749 :     zipWrite(os, file_comment_len);             // 16
     424      121749 :     zipWrite(os, disk_num_start);               // 16
     425      121749 :     zipWrite(os, intern_file_attr);             // 16
     426      121749 :     zipWrite(os, extern_file_attr);             // 32
     427      121749 :     zipWrite(os, rel_offset_loc_head);          // 32
     428      121749 :     zipWrite(os, filename);                     // string
     429      121749 :     zipWrite(os, m_extra_field);                // buffer
     430      121749 :     zipWrite(os, m_comment);                    // string
     431      121749 : }
     432             : 
     433             : 
     434             : } // zipios namespace
     435             : 
     436             : // Local Variables:
     437             : // mode: cpp
     438             : // indent-tabs-mode: nil
     439             : // c-basic-offset: 4
     440             : // tab-width: 4
     441             : // End:
     442             : 
     443             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.14

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