LCOV - code coverage report
Current view: top level - src - zipcentraldirectoryentry.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 110 110 100.0 %
Date: 2019-04-24 14:10:30 Functions: 10 10 100.0 %
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 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      108615 : ZipCentralDirectoryEntry::ZipCentralDirectoryEntry()
     145             :     //: ZipLocalEntry() -- auto-init
     146             : {
     147      108615 : }
     148             : 
     149             : 
     150             : /** \brief Initialize a ZipCentralDirectoryEntry.
     151             :  *
     152             :  * This function initializes a ZipCentralDirectoryEntry from a FileEntry.
     153             :  *
     154             :  * The function copies all the data it may be interested in and then
     155             :  * ignores the FileEntry.
     156             :  *
     157             :  * \param[in] entry  The entry to transform in a ZipCentralDirectoryEntry.
     158             :  */
     159      171467 : ZipCentralDirectoryEntry::ZipCentralDirectoryEntry(FileEntry const& entry)
     160      171467 :     : ZipLocalEntry(entry)
     161             : {
     162      171467 : }
     163             : 
     164             : 
     165             : /** \brief Clean up the entry.
     166             :  *
     167             :  * The destructor makes sure the entry is fully cleaned up.
     168             :  */
     169      562310 : ZipCentralDirectoryEntry::~ZipCentralDirectoryEntry()
     170             : {
     171      562310 : }
     172             : 
     173             : 
     174             : /** \brief Compute and return the current header size.
     175             :  *
     176             :  * This function computes the size that this entry will take in the
     177             :  * Central Directory of the Zip archive.
     178             :  *
     179             :  * \return The total size of the Central Directory entry on disk.
     180             :  */
     181      171465 : size_t ZipCentralDirectoryEntry::getHeaderSize() const
     182             : {
     183             :     /** \todo
     184             :      * Add support for 64 bit Zip. At this time this function returns
     185             :      * an invalid size if the filename, extra field, or file comment
     186             :      * sizes are more than allowed in an older version of the Zip format.
     187             :      */
     188             :     // Note that the structure is 48 bytes because of an alignment
     189             :     // and attempting to use options to avoid the alignment would
     190             :     // not be portable so we use a hard coded value (yuck!)
     191             :     return 46 /* sizeof(ZipCentralDirectoryEntryHeader) */
     192      171465 :          + m_filename.length() + (m_is_directory ? 1 : 0)
     193      171465 :          + m_extra_field.size()
     194      342930 :          + m_comment.length();
     195             : }
     196             : 
     197             : 
     198             : /** \brief Create a clone of this Central Directory entry.
     199             :  *
     200             :  * This function allocates a new copy of this ZipCentralDirectoryEntry
     201             :  * object and returns a smart pointer to it.
     202             :  *
     203             :  * \return A smart pointer to the copy.
     204             :  */
     205        1073 : FileEntry::pointer_t ZipCentralDirectoryEntry::clone() const
     206             : {
     207        1073 :     return FileEntry::pointer_t(new ZipCentralDirectoryEntry(*this));
     208             : }
     209             : 
     210             : 
     211             : /** \brief Read a Central Directory entry.
     212             :  *
     213             :  * This function reads one Central Directory entry from the specified
     214             :  * input stream. If anything goes wrong with the input stream, the read
     215             :  * function will throw an error.
     216             :  *
     217             :  * \note
     218             :  * While reading the entry is marked as invalid. If the read fails, the
     219             :  * entry will remain invalid. On success, the function restores the status
     220             :  * back to valid.
     221             :  *
     222             :  * \note
     223             :  * If the signature or some other parameter is found to be invalid, then
     224             :  * the input stream is marked as failed and an exception is thrown.
     225             :  *
     226             :  * \exception IOException
     227             :  * This exception is thrown if the signature read does not match the
     228             :  * signature of a Central Directory entry. This can only mean a bug
     229             :  * in a Zip writer or an invalid/corrupt file altogether.
     230             :  *
     231             :  * \param[in] is  The input stream to read from.
     232             :  *
     233             :  * \sa write()
     234             :  */
     235      108615 : void ZipCentralDirectoryEntry::read(std::istream& is)
     236             : {
     237      108615 :     m_valid = false; // set back to true upon successful completion below.
     238             : 
     239             :     // verify the signature
     240             :     uint32_t signature;
     241      108615 :     zipRead(is, signature);
     242      108615 :     if(g_signature != signature)
     243             :     {
     244          10 :         is.setstate(std::ios::failbit);
     245          10 :         throw IOException("ZipCentralDirectoryEntry::read(): Expected Central Directory entry signature not found");
     246             :     }
     247             : 
     248      108605 :     uint16_t writer_version(0);
     249      108605 :     uint16_t compress_method(0);
     250      108605 :     uint32_t dosdatetime(0);
     251      108605 :     uint32_t compressed_size(0);
     252      108605 :     uint32_t uncompressed_size(0);
     253      108605 :     uint32_t rel_offset_loc_head(0);
     254      108605 :     uint16_t filename_len(0);
     255      108605 :     uint16_t extra_field_len(0);
     256      108605 :     uint16_t file_comment_len(0);
     257      108605 :     uint16_t intern_file_attr(0);
     258      108605 :     uint32_t extern_file_attr(0);
     259      108605 :     uint16_t disk_num_start(0);
     260      217210 :     std::string filename;
     261             : 
     262             :     // read the header
     263      108605 :     zipRead(is, writer_version);                    // 16
     264      108605 :     zipRead(is, m_extract_version);                 // 16
     265      108605 :     zipRead(is, m_general_purpose_bitfield);        // 16
     266      108605 :     zipRead(is, compress_method);                   // 16
     267      108605 :     zipRead(is, dosdatetime);                       // 32
     268      108605 :     zipRead(is, m_crc_32);                          // 32
     269      108605 :     zipRead(is, compressed_size);                   // 32
     270      108605 :     zipRead(is, uncompressed_size);                 // 32
     271      108605 :     zipRead(is, filename_len);                      // 16
     272      108605 :     zipRead(is, extra_field_len);                   // 16
     273      108605 :     zipRead(is, file_comment_len);                  // 16
     274      108605 :     zipRead(is, disk_num_start);                    // 16
     275      108605 :     zipRead(is, intern_file_attr);                  // 16
     276      108605 :     zipRead(is, extern_file_attr);                  // 32
     277      108605 :     zipRead(is, rel_offset_loc_head);               // 32
     278      108605 :     zipRead(is, filename, filename_len);            // string
     279      108605 :     zipRead(is, m_extra_field, extra_field_len);    // buffer
     280      108605 :     zipRead(is, m_comment, file_comment_len);       // string
     281             :     /** \todo check whether this was a 64 bit header and make sure
     282             :      *        to read the 64 bit header too if so
     283             :      */
     284             : 
     285             :     // the FilePath() will remove the trailing slash so make sure
     286             :     // to defined the m_is_directory ahead of time!
     287      108605 :     m_is_directory = !filename.empty() && filename.back() == g_separator;
     288             : 
     289      108605 :     m_compress_method = static_cast<StorageMethod>(compress_method);
     290      108605 :     DOSDateTime t;
     291      108605 :     t.setDOSDateTime(dosdatetime);
     292      108605 :     m_unix_time = t.getUnixTimestamp();
     293      108605 :     m_compressed_size = compressed_size;
     294      108605 :     m_uncompressed_size = uncompressed_size;
     295      108605 :     m_entry_offset = rel_offset_loc_head;
     296      108605 :     m_filename = FilePath(filename);
     297             : 
     298             :     // the zipRead() should throw if it is false...
     299      108605 :     m_valid = true;
     300      108605 : }
     301             : 
     302             : 
     303             : /** \brief Write a Central Directory Entry to the output stream.
     304             :  *
     305             :  * This function verifies that the data of the Central Directory entry
     306             :  * can be written to disk. If so, then it writes a block. The size of
     307             :  * the blocks varies depending on the filename, file comment, and extra
     308             :  * data. The current size can be determined using the getHeaderSize()
     309             :  * function.
     310             :  *
     311             :  * \warning
     312             :  * The function saves the filename with an ending separator in case
     313             :  * the entry is marked as a directory entry. Note that Zip only really
     314             :  * knows about the trailing slash as a way to detect a file as a
     315             :  * directory.
     316             :  *
     317             :  * \exception InvalidStateException
     318             :  * The function verifies whether the filename, extra field,
     319             :  * file comment, file data, or data offset are not too large.
     320             :  * If any one of these parameters is too large, then this
     321             :  * exception is raised.
     322             :  *
     323             :  * \param[in] os  The output stream where the data is written.
     324             :  *
     325             :  * \sa getHeaderSize()
     326             :  * \sa read()
     327             :  */
     328      171467 : void ZipCentralDirectoryEntry::write(std::ostream& os)
     329             : {
     330             :     /** \todo add support for 64 bit entries
     331             :      *        (zip64 is available, just need to add a 64 bit header...)
     332             :      */
     333      342934 :     if(m_filename.length()  > 0x10000
     334      171467 :     || m_extra_field.size() > 0x10000
     335      342933 :     || m_comment.length()   > 0x10000)
     336             :     {
     337           2 :         throw InvalidStateException("ZipCentralDirectoryEntry::write(): file name, comment, or extra field too large to save in a Zip file.");
     338             :     }
     339             : 
     340             : // Solaris defines _ILP32 for 32 bit platforms
     341             : #if !defined(_ILP32)
     342      342930 :     if(m_compressed_size   >= 0x100000000ULL
     343      171465 :     || m_uncompressed_size >= 0x100000000ULL
     344      342930 :     || m_entry_offset      >= 0x100000000LL)
     345             :     {
     346             :         // This represents really large files which we do not test at this point
     347             :         throw InvalidStateException("ZipCentralDirectoryEntry::write(): The size of this file is too large to fit in a zip archive."); // LCOV_EXCL_LINE
     348             :     }
     349             : #endif
     350             : 
     351             :     // define version
     352      171465 :     uint16_t writer_version = g_zip_format_version;
     353             :     // including the "compatibility" code
     354             : #if defined(WIN32) || defined(_WIN32) || defined(__WIN32)
     355             :     // MS-Windows
     356             :     // TBD: should we use g_msdos instead?
     357             :     writer_version |= g_windows;
     358             : #elif defined(__APPLE__) && defined(__MACH__)
     359             :     // OS/X
     360             :     writer_version |= g_osx;
     361             : #else
     362             :     // Other Unices
     363      171465 :     writer_version |= g_unix;
     364             : #endif
     365             : 
     366      342930 :     std::string filename(m_filename);
     367      171465 :     if(m_is_directory)
     368             :     {
     369             :         // add a trailing separator for directories
     370             :         // (this is VERY important for zip files which do not otherwise
     371             :         // indicate that a file is a directory)
     372       10278 :         filename += g_separator;
     373             :     }
     374             : 
     375      171465 :     uint16_t compress_method(static_cast<uint8_t>(m_compress_method));
     376      171465 :     if(m_compression_level == COMPRESSION_LEVEL_NONE)
     377             :     {
     378       12054 :         compress_method = static_cast<uint8_t>(StorageMethod::STORED);
     379             :     }
     380             : 
     381      171465 :     DOSDateTime t;
     382      171465 :     t.setUnixTimestamp(m_unix_time);
     383      171465 :     uint32_t dosdatetime(t.getDOSDateTime());   // type could be set to DOSDateTime::dosdatetime_t
     384      171465 :     uint32_t compressed_size(m_compressed_size);
     385      171465 :     uint32_t uncompressed_size(m_uncompressed_size);
     386      171465 :     uint16_t filename_len(filename.length());
     387      171465 :     uint16_t extra_field_len(m_extra_field.size());
     388      171465 :     uint16_t file_comment_len(m_comment.length());
     389      171465 :     uint16_t disk_num_start(0);
     390      171465 :     uint16_t intern_file_attr(0);
     391             :     /** \FIXME
     392             :      * The external_file_attr supports the standard Unix
     393             :      * permissions in the higher 16 bits defined as:
     394             :      *
     395             :      *    \<type> \<rwx> \<rwx> \<rwx>
     396             :      *
     397             :      * The \<type> is the top 4 bits and is set to either 8 or 4:
     398             :      *
     399             :      * \li 8 for regular files
     400             :      * \li 4 for directories
     401             :      *
     402             :      * The \<rwx> are the standard permission flags representing the
     403             :      * owner, group, and other read/write/execute permissions.
     404             :      *
     405             :      * The value also includes the special flags SUID, SGID and VTX.
     406             :      *
     407             :      * So to have a fix here we need to have a way to read those flags
     408             :      * from the file entry.
     409             :      */
     410      171465 :     uint32_t extern_file_attr(m_is_directory ? 0x41FD0010 : 0x81B40000);
     411      171465 :     uint32_t rel_offset_loc_head(m_entry_offset);
     412             : 
     413      171465 :     zipWrite(os, g_signature);                  // 32
     414      171465 :     zipWrite(os, writer_version);               // 16
     415      171465 :     zipWrite(os, m_extract_version);            // 16
     416      171465 :     zipWrite(os, m_general_purpose_bitfield);   // 16
     417      171465 :     zipWrite(os, compress_method);              // 16
     418      171465 :     zipWrite(os, dosdatetime);                  // 32
     419      171465 :     zipWrite(os, m_crc_32);                     // 32
     420      171465 :     zipWrite(os, compressed_size);              // 32
     421      171465 :     zipWrite(os, uncompressed_size);            // 32
     422      171465 :     zipWrite(os, filename_len);                 // 16
     423      171465 :     zipWrite(os, extra_field_len);              // 16
     424      171465 :     zipWrite(os, file_comment_len);             // 16
     425      171465 :     zipWrite(os, disk_num_start);               // 16
     426      171465 :     zipWrite(os, intern_file_attr);             // 16
     427      171465 :     zipWrite(os, extern_file_attr);             // 32
     428      171465 :     zipWrite(os, rel_offset_loc_head);          // 32
     429      171465 :     zipWrite(os, filename);                     // string
     430      171465 :     zipWrite(os, m_extra_field);                // buffer
     431      171465 :     zipWrite(os, m_comment);                    // string
     432      171465 : }
     433             : 
     434             : 
     435           3 : } // zipios namespace
     436             : 
     437             : // Local Variables:
     438             : // mode: cpp
     439             : // indent-tabs-mode: nil
     440             : // c-basic-offset: 4
     441             : // tab-width: 4
     442             : // End:
     443             : 
     444             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.12