zipios  2.1.0
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 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 
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
virtual FileEntry::pointer_t getEntry(std::string const &name, MatchPath matchpath=MatchPath::MATCH) const
Get an entry from this collection.
Define zipios::ZipInputStream.
virtual pointer_t clone() const override
Create a clone of this ZipFile.
Definition: zipfile.cpp:423
std::shared_ptr< FileCollection > pointer_t
size_t getCount() const
Retrieve the number of entries.
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
offset_t startOffset() const
Return the start offset.
Various exceptions used throughout the Zipios++ library, all based on zipios::Exception.
void zipRead(std::istream &is, uint32_t &value)
offset_t getOffset() const
Retrieve the offset of the Central Directory.
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.
virtual FileEntry::vector_t entries() const
Retrieve the array of entries.
Define the zipios::ZipOutputStream class.
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.
virtual void mustBeValid() const
Check whether the collection is valid.
void putNextEntry(FileEntry::pointer_t entry)
Add an entry to the output stream.
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 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.
void vseekg(std::istream &is, offset_t offset, std::ios::seekdir sd) const
Seek within the embedded file.
Declaration of the zipios::ZipEndOfCentralDirectory class.
std::streamoff offset_t
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
ssize_t readChunk(ssize_t &read_pointer)
Read a chunk of data.
Definition: backbuffer.cpp:140
std::streampos vtellg(std::istream &is) const
Current position within the sub-file.
std::shared_ptr< FileEntry > pointer_t
Definition: fileentry.hpp:77
A ZipOutputStream to allow for data to be compressed zlib.
size_t getCentralDirectorySize() const
Retrieve the size of the Central Directory in bytes.
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