zipios  2.3.2
Zipios -- a small C++ library that provides easy access to .zip files.
zipfile.cpp
Go to the documentation of this file.
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 
29 #include "zipios/zipfile.hpp"
30 
31 #include "zipios/streamentry.hpp"
33 
34 #include "backbuffer.hpp"
37 #include "zipinputstream.hpp"
38 #include "zipoutputstream.hpp"
39 
40 #include <fstream>
41 
42 
54 namespace zipios
55 {
56 
57 
305 {
306  // open zipfile, read 4 last bytes close file
307  uint32_t start_offset;
308  {
309  std::ifstream ifs(filename, std::ios::in | std::ios::binary);
310  ifs.seekg(-4, std::ios::end);
311  zipRead(ifs, start_offset);
312  // todo: add support for 64 bit (files of more than 4Gb)
313  }
314 
315  // create ZipFile object from embedded data
316  return std::make_shared<ZipFile>(filename, start_offset, 4);
317 }
318 
319 
329 {
330 }
331 
332 
355 ZipFile::ZipFile(std::string const & filename, offset_t s_off, offset_t e_off)
356  : FileCollection(filename)
357  , m_vs(s_off, e_off)
358 {
359  std::ifstream zipfile(m_filename, std::ios::in | std::ios::binary);
360  if(!zipfile)
361  {
362  throw IOException("Error opening Zip archive file for reading in binary mode.");
363  }
364 
365  init(zipfile);
366 }
367 
368 
391 ZipFile::ZipFile(std::istream & is, offset_t s_off, offset_t e_off)
392  : m_vs(s_off, e_off)
393 {
394  init(is);
395 }
396 
397 
409 void ZipFile::init(std::istream & is)
410 {
411  // Find and read the End of Central Directory.
413  {
414  BackBuffer bb(is, m_vs);
415  ssize_t read_p(-1);
416  for(;;)
417  {
418  if(read_p < 0)
419  {
420  if(!bb.readChunk(read_p))
421  {
422  throw FileCollectionException("Unable to find zip structure: End-of-central-directory");
423  }
424  }
425  // Note: this is pretty fast since it reads from 'bb' which
426  // caches the buffer the readChunk() function just read.
427  //
428  if(eocd.read(bb, read_p))
429  {
430  // found it!
431  break;
432  }
433  --read_p;
434  }
435  }
436 
437  // Position read pointer to start of first entry in central dir.
438  m_vs.vseekg(is, eocd.getOffset(), std::ios::beg);
439 
440  // TBD -- is that ", 0" still necessary? (With VC2012 and better)
441  // Give the second argument in the next line to keep Visual C++ quiet
442  //m_entries.resize(eocd.getCount(), 0);
443  m_entries.resize(eocd.getCount());
444 
445  size_t const max_entry(eocd.getCount());
446  for(size_t entry_num(0); entry_num < max_entry; ++entry_num)
447  {
448  m_entries[entry_num] = std::make_shared<ZipCentralDirectoryEntry>();
449  m_entries[entry_num].get()->read(is);
450  }
451 
452  // Consistency check #1:
453  // The virtual seeker position is exactly the start offset of the
454  // Central Directory plus the Central Directory size
455  //
456  offset_t const pos(m_vs.vtellg(is));
457  if(static_cast<offset_t>(eocd.getOffset() + eocd.getCentralDirectorySize()) != pos)
458  {
459  throw FileCollectionException("Zip file consistency problem. Zip file data fields are inconsistent with zip file layout.");
460  }
461 
462  // Consistency check #2:
463  // Are local headers consistent with CD headers?
464  //
465  for(auto it = m_entries.begin(); it != m_entries.end(); ++it)
466  {
473  m_vs.vseekg(is, (*it)->getEntryOffset(), std::ios::beg);
474  ZipLocalEntry zlh;
475  zlh.read(is);
476  if(!is || !zlh.isEqual(**it))
477  {
478  throw FileCollectionException("Zip file consistency problem. Zip file data fields are inconsistent with zip file layout.");
479  }
480  }
481 
482  // we are all good!
483  m_valid = true;
484 }
485 
486 
494 {
495  return FileCollection::pointer_t(std::make_shared<ZipFile>(*this));
496 }
497 
498 
505 {
506  close();
507 }
508 
509 
544 ZipFile::stream_pointer_t ZipFile::getInputStream(std::string const & entry_name, MatchPath matchpath)
545 {
546  mustBeValid();
547 
548  // TODO: see whether we could make the handling of the StreamEntry
549  // non-special
550  //
551  FileEntry::pointer_t entry(getEntry(entry_name, matchpath));
552  StreamEntry::pointer_t stream(std::dynamic_pointer_cast<StreamEntry>(entry));
553  if(stream != nullptr)
554  {
555  stream_pointer_t zis(std::make_shared<ZipInputStream>(stream->getStream()));
556  return zis;
557  }
558  else if(entry != nullptr)
559  {
560  stream_pointer_t zis(std::make_shared<ZipInputStream>(m_filename, entry->getEntryOffset() + m_vs.startOffset()));
561  return zis;
562  }
563 
564  // no entry with that name (and match) available
565  return nullptr;
566 }
567 
568 
579  std::ostream & os
580  , FileCollection & collection
581  , std::string const & zip_comment)
582 {
583  try
584  {
585  ZipOutputStream output_stream(os);
586 
587  output_stream.setComment(zip_comment);
588 
589  FileEntry::vector_t entries(collection.entries());
590  for(auto it(entries.begin()); it != entries.end(); ++it)
591  {
592  output_stream.putNextEntry(*it);
593 
594  // next we need to include the data of that file in the
595  // output buffer if it is not a directory and the file is
596  // not an empty file
597  //
598  if(!(*it)->isDirectory()
599  && (*it)->getSize() > 0)
600  {
601  // get an InputStream
602  //
603  FileCollection::stream_pointer_t is(collection.getInputStream((*it)->getName()));
604  if(is != nullptr
605  && is->good())
606  {
607  // copy the file content to the output
608  //
609  output_stream << is->rdbuf();
610  }
611  }
612  }
613 
614  // clean up manually so we can get any exception
615  // (so we avoid having exceptions gobbled by the destructor)
616  output_stream.closeEntry();
617  output_stream.finish();
618  output_stream.close();
619  }
620  catch(...)
621  {
622  os.setstate(std::ios::failbit);
623  throw;
624  }
625 }
626 
627 
628 } // zipios namespace
629 
630 // Local Variables:
631 // mode: cpp
632 // indent-tabs-mode: nil
633 // c-basic-offset: 4
634 // tab-width: 4
635 // End:
636 
637 // vim: ts=4 sw=4 et
Define zipios::ZipInputStream.
virtual pointer_t clone() const override
Create a clone of this ZipFile.
Definition: zipfile.cpp:493
virtual FileEntry::pointer_t getEntry(std::string const &name, MatchPath matchpath=MatchPath::MATCH) const
Get an entry from this collection.
The zipios namespace includes the Zipios library definitions.
Definition: backbuffer.cpp:35
static void saveCollectionToArchive(std::ostream &os, FileCollection &collection, std::string const &zip_comment=std::string())
Create a Zip archive from the specified FileCollection.
Definition: zipfile.cpp:578
std::shared_ptr< FileCollection > pointer_t
Various exceptions used throughout the Zipios library, all based on zipios::Exception.
void zipRead(std::istream &is, uint32_t &value)
size_t getCount() const
Retrieve the number of entries.
void setComment(std::string const &comment)
Set the global comment.
std::shared_ptr< std::istream > stream_pointer_t
A shared pointer to an input stream.
void vseekg(std::istream &is, offset_t offset, std::ios::seekdir sd) const
Seek within the embedded file.
Define the zipios::ZipOutputStream class.
virtual FileEntry::vector_t entries() const
Retrieve the array of entries.
void init(std::istream &is)
Initialize the ZipFile from the specified input stream.
Definition: zipfile.cpp:409
An implementation of the FileEntry for Zip archives.
ZipFile()
Initialize a ZipFile object.
Definition: zipfile.cpp:328
void close()
Close the current stream.
virtual stream_pointer_t getInputStream(std::string const &entry_name, MatchPath matchpath=MatchPath::MATCH)=0
Retrieve pointer to an istream.
Define the zipios::StreamEntry class.
FileEntry::vector_t m_entries
static pointer_t openEmbeddedZipFile(std::string const &filename)
Open a zip archive that was previously appended to another file.
Definition: zipfile.cpp:304
Marker at the end of a Zip archive file.
VirtualSeeker m_vs
Definition: zipfile.hpp:69
virtual ~ZipFile() override
Clean up the ZipFile object.
Definition: zipfile.cpp:504
An IOException is used to signal an I/O error.
virtual void close()
Close the current FileEntry of this FileCollection.
void putNextEntry(FileEntry::pointer_t entry)
Add an entry to the output stream.
std::streampos vtellg(std::istream &is) const
Current position within the sub-file.
std::shared_ptr< StreamEntry > pointer_t
Definition: streamentry.hpp:45
virtual void mustBeValid() const
Check whether the collection is valid.
virtual void read(std::istream &is) override
Read one local entry from is.
Declaration of the zipios::ZipCentralDirectoryEntry, which represents a directory Zip archive entry...
FileCollectionException is used to signal a FileCollection problem.
The header file for zipios::BackBuffer.
Define the zipios::ZipFile class.
Declaration of the zipios::ZipEndOfCentralDirectory class.
std::streamoff offset_t
offset_t startOffset() const
Return the start offset.
Base class for various file collections.
virtual bool isEqual(FileEntry const &file_entry) const override
Compare two file entries for equality.
virtual stream_pointer_t getInputStream(std::string const &entry_name, MatchPath matchpath=MatchPath::MATCH) override
Retrieve a pointer to a file in the Zip archive.
Definition: zipfile.cpp:544
size_t getCentralDirectorySize() const
Retrieve the size of the Central Directory in bytes.
ssize_t readChunk(ssize_t &read_pointer)
Read a chunk of data.
Definition: backbuffer.cpp:141
std::shared_ptr< FileEntry > pointer_t
Definition: fileentry.hpp:78
A ZipOutputStream to allow for data to be compressed with zlib.
offset_t getOffset() const
Retrieve the offset of the Central Directory.
To read a file by chunk from the end.
Definition: backbuffer.hpp:41
bool read(::zipios::buffer_t const &buf, size_t pos)
Attempt to read an ZipEndOfCentralDirectory structure.
void finish()
Finish up the output by flushing anything left.
std::vector< pointer_t > vector_t
Definition: fileentry.hpp:79