zipios  2.1.1
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 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 
29 #include "zipios/zipfile.hpp"
30 
32 
33 #include "backbuffer.hpp"
36 #include "zipinputstream.hpp"
37 #include "zipoutputstream.hpp"
38 
39 #include <fstream>
40 
41 
53 namespace zipios
54 {
55 
56 
287 {
288  // open zipfile, read 4 last bytes close file
289  // create ZipFile object.
290  uint32_t start_offset;
291  {
292  std::ifstream ifs(name, std::ios::in | std::ios::binary);
293  ifs.seekg(-4, std::ios::end);
294  zipRead(ifs, start_offset);
295  // TODO: add support for 64 bit (files of more than 4Gb)
296  }
297  return ZipFile::pointer_t(new ZipFile(name, start_offset, 4));
298 }
299 
300 
310  //: m_vs(...) -- auto-init
311 {
312 }
313 
314 
333 ZipFile::ZipFile(std::string const& filename, offset_t s_off, offset_t e_off)
334  : FileCollection(filename)
335  , m_vs(s_off, e_off)
336 {
337  std::ifstream zipfile(m_filename, std::ios::in | std::ios::binary);
338  if(!zipfile)
339  {
340  throw IOException("Error opening Zip archive file for reading in binary mode.");
341  }
342 
343  // Find and read the End of Central Directory.
345  {
346  BackBuffer bb(zipfile, m_vs);
347  ssize_t read_p(-1);
348  for(;;)
349  {
350  if(read_p < 0)
351  {
352  if(!bb.readChunk(read_p))
353  {
354  throw FileCollectionException("Unable to find zip structure: End-of-central-directory");
355  }
356  }
357  // Note: this is pretty fast since it reads from 'bb' which
358  // caches the buffer the readChunk() function just read.
359  //
360  if(eocd.read(bb, read_p))
361  {
362  // found it!
363  break;
364  }
365  --read_p;
366  }
367  }
368 
369  // Position read pointer to start of first entry in central dir.
370  m_vs.vseekg(zipfile, eocd.getOffset(), std::ios::beg);
371 
372  // TBD -- is that ", 0" still necessary? (With VC2012 and better)
373  // Give the second argument in the next line to keep Visual C++ quiet
374  //m_entries.resize(eocd.totalCount(), 0);
375  m_entries.resize(eocd.getCount());
376 
377  size_t const max_entry(eocd.getCount());
378  for(size_t entry_num(0); entry_num < max_entry; ++entry_num)
379  {
381  m_entries[entry_num].get()->read(zipfile);
382  }
383 
384  // Consistency check #1:
385  // The virtual seeker position is exactly the start offset of the
386  // Central Directory plus the Central Directory size
387  //
388  offset_t const pos(m_vs.vtellg(zipfile));
389  if(static_cast<offset_t>(eocd.getOffset() + eocd.getCentralDirectorySize()) != pos)
390  {
391  throw FileCollectionException("Zip file consistency problem. Zip file data fields are inconsistent with zip file layout.");
392  }
393 
394  // Consistency check #2:
395  // Are local headers consistent with CD headers?
396  //
397  for(auto it = m_entries.begin(); it != m_entries.end(); ++it)
398  {
403  m_vs.vseekg(zipfile, (*it)->getEntryOffset(), std::ios::beg);
404  ZipLocalEntry zlh;
405  zlh.read(zipfile);
406  if(!zipfile || !zlh.isEqual(**it))
407  {
408  throw FileCollectionException("Zip file consistency problem. Zip file data fields are inconsistent with zip file layout.");
409  }
410  }
411 
412  // we are all good!
413  m_valid = true;
414 }
415 
416 
424 {
425  return FileCollection::pointer_t(new ZipFile(*this));
426 }
427 
428 
435 {
436  close();
437 }
438 
439 
474 ZipFile::stream_pointer_t ZipFile::getInputStream(std::string const& entry_name, MatchPath matchpath)
475 {
476  mustBeValid();
477 
478  FileEntry::pointer_t entry(getEntry(entry_name, matchpath));
479  if(entry)
480  {
481  stream_pointer_t zis(new ZipInputStream(m_filename, entry->getEntryOffset() + m_vs.startOffset()));
482  return zis;
483  }
484 
485  // no entry with that name (and match) available
486  return nullptr;
487 }
488 
489 
499 void ZipFile::saveCollectionToArchive(std::ostream & os, FileCollection & collection, std::string const & zip_comment)
500 {
501  try
502  {
503  ZipOutputStream output_stream(os);
504 
505  output_stream.setComment(zip_comment);
506 
507  FileEntry::vector_t entries(collection.entries());
508  for(auto it(entries.begin()); it != entries.end(); ++it)
509  {
510  output_stream.putNextEntry(*it);
511  // get an InputStream if available (i.e. directories do not have an input stream)
512  if(!(*it)->isDirectory())
513  {
514  FileCollection::stream_pointer_t is(collection.getInputStream((*it)->getName()));
515  if(is)
516  {
517  output_stream << is->rdbuf();
518  }
519  }
520  }
521 
522  // clean up mantually so we can get any exception
523  // (so we avoid having exceptions gobbled by the destructor)
524  output_stream.closeEntry();
525  output_stream.finish();
526  output_stream.close();
527  }
528  catch(...)
529  {
530  os.setstate(std::ios::failbit);
531  throw;
532  }
533 }
534 
535 
536 } // zipios namespace
537 
538 // Local Variables:
539 // mode: cpp
540 // indent-tabs-mode: nil
541 // c-basic-offset: 4
542 // tab-width: 4
543 // End:
544 
545 // vim: ts=4 sw=4 et
Define zipios::ZipInputStream.
virtual pointer_t clone() const override
Create a clone of this ZipFile.
Definition: zipfile.cpp:423
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
std::shared_ptr< FileCollection > pointer_t
static void saveCollectionToArchive(std::ostream &os, FileCollection &collection, std::string const &zip_comment="")
Create a Zip archive from the specified FileCollection.
Definition: zipfile.cpp:499
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.
An implementation of the FileEntry for Zip archives.
ZipFile()
Initialize a ZipFile object.
Definition: zipfile.cpp:309
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.
FileEntry::vector_t m_entries
Marker at the end of a Zip archive file.
VirtualSeeker m_vs
Definition: zipfile.hpp:61
virtual ~ZipFile() override
Clean up the ZipFile object.
Definition: zipfile.cpp:434
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.
static pointer_t openEmbeddedZipFile(std::string const &name)
Open a zip archive that was previously appened to another file.
Definition: zipfile.cpp:286
A specialization of ZipLocalEntry for.
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:474
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:77
A ZipOutputStream to allow for data to be compressed 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.
The ZipInputStream to read data from a Zip archive.
void finish()
Finish up the output by flushing anything left.
std::vector< pointer_t > vector_t
Definition: fileentry.hpp:78