LCOV - code coverage report
Current view: top level - src - zipcentraldirectoryentry.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 106 106 100.0 %
Date: 2015-04-12 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  Made to Order Software Corporation
       6             : 
       7             :   This library is free software; you can redistribute it and/or
       8             :   modify it under the terms of the GNU Lesser General Public
       9             :   License as published by the Free Software Foundation; either
      10             :   version 2 of the License, or (at your option) any later version.
      11             : 
      12             :   This library is distributed in the hope that it will be useful,
      13             :   but WITHOUT ANY WARRANTY; without even the implied warranty of
      14             :   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15             :   Lesser General Public License for more details.
      16             : 
      17             :   You should have received a copy of the GNU Lesser General Public
      18             :   License along with this library; if not, write to the Free Software
      19             :   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
      20             : */
      21             : 
      22             : /** \file
      23             :  * \brief Implementation of 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             : 
      34             : #include "dostime.h"
      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 endianess 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_dostime;
     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       69412 : ZipCentralDirectoryEntry::ZipCentralDirectoryEntry()
     145             :     //: ZipLocalEntry() -- auto-init
     146             : {
     147       69412 : }
     148             : 
     149             : 
     150             : /** \brief Initialize a ZipCentralDirectoryEntry.
     151             :  *
     152             :  * This function initializes a ZipCentralDirectoryEntry from a FileEntry.
     153             :  *
     154             :  * The funtion 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      133066 : ZipCentralDirectoryEntry::ZipCentralDirectoryEntry(FileEntry const& entry)
     160      133066 :     : ZipLocalEntry(entry)
     161             : {
     162      133066 : }
     163             : 
     164             : 
     165             : /** \brief Clean up the entry.
     166             :  *
     167             :  * The destructor makes sure the entry is fully cleaned up.
     168             :  */
     169      406464 : ZipCentralDirectoryEntry::~ZipCentralDirectoryEntry()
     170             : {
     171      406464 : }
     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      133064 : 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      133064 :          + m_filename.length() + (m_is_directory ? 1 : 0)
     193      133064 :          + m_extra_field.size()
     194      266128 :          + 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         754 : FileEntry::pointer_t ZipCentralDirectoryEntry::clone() const
     206             : {
     207         754 :     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       69412 : void ZipCentralDirectoryEntry::read(std::istream& is)
     236             : {
     237       69412 :     m_valid = false; // set back to true upon successful completion below.
     238             : 
     239             :     // verify the signature
     240             :     uint32_t signature;
     241       69412 :     zipRead(is, signature);
     242       69412 :     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       69402 :     uint16_t writer_version(0);
     249       69402 :     uint16_t compress_method(0);
     250       69402 :     uint32_t dostime(0);
     251       69402 :     uint32_t compressed_size(0);
     252       69402 :     uint32_t uncompressed_size(0);
     253       69402 :     uint32_t rel_offset_loc_head(0);
     254       69402 :     uint16_t filename_len(0);
     255       69402 :     uint16_t extra_field_len(0);
     256       69402 :     uint16_t file_comment_len(0);
     257       69402 :     uint16_t intern_file_attr(0);
     258       69402 :     uint32_t extern_file_attr(0);
     259       69402 :     uint16_t disk_num_start(0);
     260       69402 :     std::string filename;
     261             : 
     262             :     // read the header
     263       69402 :     zipRead(is, writer_version);                    // 16
     264       69402 :     zipRead(is, m_extract_version);                 // 16
     265       69402 :     zipRead(is, m_general_purpose_bitfield);        // 16
     266       69402 :     zipRead(is, compress_method);                   // 16
     267       69402 :     zipRead(is, dostime);                           // 32
     268       69402 :     zipRead(is, m_crc_32);                          // 32
     269       69402 :     zipRead(is, compressed_size);                   // 32
     270       69402 :     zipRead(is, uncompressed_size);                 // 32
     271       69402 :     zipRead(is, filename_len);                      // 16
     272       69402 :     zipRead(is, extra_field_len);                   // 16
     273       69402 :     zipRead(is, file_comment_len);                  // 16
     274       69402 :     zipRead(is, disk_num_start);                    // 16
     275       69402 :     zipRead(is, intern_file_attr);                  // 16
     276       69402 :     zipRead(is, extern_file_attr);                  // 32
     277       69402 :     zipRead(is, rel_offset_loc_head);               // 32
     278       69402 :     zipRead(is, filename, filename_len);            // string
     279       69402 :     zipRead(is, m_extra_field, extra_field_len);    // buffer
     280       69402 :     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       69402 :     m_is_directory = !filename.empty() && filename.back() == g_separator;
     288             : 
     289       69402 :     m_compress_method = static_cast<StorageMethod>(compress_method);
     290       69402 :     m_unix_time = dos2unixtime(dostime);
     291       69402 :     m_compressed_size = compressed_size;
     292       69402 :     m_uncompressed_size = uncompressed_size;
     293       69402 :     m_entry_offset = rel_offset_loc_head;
     294       69402 :     m_filename = FilePath(filename);
     295             : 
     296             :     // the zipRead() should throw if it is false...
     297       69402 :     m_valid = true;
     298       69402 : }
     299             : 
     300             : 
     301             : /** \brief Write a Central Directory Entry to the output stream.
     302             :  *
     303             :  * This function verifies that the data of the Central Directory entry
     304             :  * can be written to disk. If so, then it writes a block. The size of
     305             :  * the blocks varies depending on the filename, file comment, and extra
     306             :  * data. The current size can be determined using the getHeaderSize()
     307             :  * function.
     308             :  *
     309             :  * \warning
     310             :  * The function saves the filename with an ending separator in case
     311             :  * the entry is marked as a directory entry. Note that Zip only really
     312             :  * knows about the trailing slash as a way to detect a file as a
     313             :  * directory.
     314             :  *
     315             :  * \exception InvalidStateException
     316             :  * The function verifies whether the filename, extra field,
     317             :  * file comment, file data, or data offset are not too large.
     318             :  * If any one of these parameters is too large, then this
     319             :  * exception is raised.
     320             :  *
     321             :  * \param[in] os  The output stream where the data is written.
     322             :  *
     323             :  * \sa getHeaderSize()
     324             :  * \sa read()
     325             :  */
     326      133066 : void ZipCentralDirectoryEntry::write(std::ostream& os)
     327             : {
     328             :     /** \TODO add support for 64 bit entries
     329             :      *        (zip64 is available, just need to add a 64 bit header...)
     330             :      */
     331      266132 :     if(m_filename.length()  > 0x10000
     332      133066 :     || m_extra_field.size() > 0x10000
     333      266131 :     || m_comment.length()   > 0x10000)
     334             :     {
     335           2 :         throw InvalidStateException("ZipCentralDirectoryEntry::write(): file name, comment, or extra field too large to save in a Zip file.");
     336             :     }
     337             : 
     338             : // Solaris defines _ILP32 for 32 bit platforms
     339             : #if !defined(_ILP32)
     340      266128 :     if(m_compressed_size   >= 0x100000000ULL
     341      133064 :     || m_uncompressed_size >= 0x100000000ULL
     342      266128 :     || m_entry_offset      >= 0x100000000LL)
     343             :     {
     344             :         // This represents really large files which we do not test at this point
     345             :         throw InvalidStateException("ZipCentralDirectoryEntry::write(): The size of this file is too large to fit in a zip archive."); // LCOV_EXCL_LINE
     346             :     }
     347             : #endif
     348             : 
     349             :     // define version
     350      133064 :     uint16_t writer_version = g_zip_format_version;
     351             :     // including the "compatibility" code
     352             : #if defined(WIN32) || defined(_WIN32) || defined(__WIN32)
     353             :     // MS-Windows
     354             :     /** \TODO should we use g_msdos instead?
     355             :      */
     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      133064 :     writer_version |= g_unix;
     363             : #endif
     364             : 
     365      133064 :     std::string filename(m_filename);
     366      133064 :     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        6033 :         filename += g_separator;
     372             :     }
     373             : 
     374      133064 :     uint16_t compress_method(static_cast<uint8_t>(m_compress_method));
     375      133064 :     if(m_compression_level == COMPRESSION_LEVEL_NONE)
     376             :     {
     377        7077 :         compress_method = static_cast<uint8_t>(StorageMethod::STORED);
     378             :     }
     379             : 
     380      133064 :     uint32_t dostime(unix2dostime(m_unix_time));
     381      133064 :     uint32_t compressed_size(m_compressed_size);
     382      133064 :     uint32_t uncompressed_size(m_uncompressed_size);
     383      133064 :     uint16_t filename_len(filename.length());
     384      133064 :     uint16_t extra_field_len(m_extra_field.size());
     385      133064 :     uint16_t file_comment_len(m_comment.length());
     386      133064 :     uint16_t disk_num_start(0);
     387      133064 :     uint16_t intern_file_attr(0);
     388             :     /** \FIXME
     389             :      * The external_file_attr supports the standard Unix
     390             :      * permissions in the higher 16 bits defined as:
     391             :      *
     392             :      *    \<type> \<rwx> \<rwx> \<rwx>
     393             :      *
     394             :      * The \<type> is the top 4 bits and is set to either 8 or 4:
     395             :      *
     396             :      * \li 8 for regular files
     397             :      * \li 4 for directories
     398             :      *
     399             :      * The \<rwx> are the standard permission flags representing the
     400             :      * owner, group, and other read/write/execute permissions.
     401             :      *
     402             :      * The value also includes the special flags SUID, SGID and VTX.
     403             :      *
     404             :      * So to have a fix here we need to have a way to read those flags
     405             :      * from the file entry.
     406             :      */
     407      133064 :     uint32_t extern_file_attr(m_is_directory ? 0x41FD0010 : 0x81B40000);
     408      133064 :     uint32_t rel_offset_loc_head(m_entry_offset);
     409             : 
     410      133064 :     zipWrite(os, g_signature);                  // 32
     411      133064 :     zipWrite(os, writer_version);               // 16
     412      133064 :     zipWrite(os, m_extract_version);            // 16
     413      133064 :     zipWrite(os, m_general_purpose_bitfield);   // 16
     414      133064 :     zipWrite(os, compress_method);              // 16
     415      133064 :     zipWrite(os, dostime);                      // 32
     416      133064 :     zipWrite(os, m_crc_32);                     // 32
     417      133064 :     zipWrite(os, compressed_size);              // 32
     418      133064 :     zipWrite(os, uncompressed_size);            // 32
     419      133064 :     zipWrite(os, filename_len);                 // 16
     420      133064 :     zipWrite(os, extra_field_len);              // 16
     421      133064 :     zipWrite(os, file_comment_len);             // 16
     422      133064 :     zipWrite(os, disk_num_start);               // 16
     423      133064 :     zipWrite(os, intern_file_attr);             // 16
     424      133064 :     zipWrite(os, extern_file_attr);             // 32
     425      133064 :     zipWrite(os, rel_offset_loc_head);          // 32
     426      133064 :     zipWrite(os, filename);                     // string
     427      133064 :     zipWrite(os, m_extra_field);                // buffer
     428      133064 :     zipWrite(os, m_comment);                    // string
     429      133064 : }
     430             : 
     431             : 
     432           3 : } // zipios namespace
     433             : 
     434             : // Local Variables:
     435             : // mode: cpp
     436             : // indent-tabs-mode: nil
     437             : // c-basic-offset: 4
     438             : // tab-width: 4
     439             : // End:
     440             : 
     441             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.10