zipios++  2.0.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 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 
261 {
262  // open zipfile, read 4 last bytes close file
263  // create ZipFile object.
264  uint32_t start_offset;
265  {
266  std::ifstream ifs(name, std::ios::in | std::ios::binary);
267  ifs.seekg(-4, std::ios::end);
268  zipRead(ifs, start_offset);
269  // TODO: add support for 64 bit (files of more than 4Gb)
270  }
271  return ZipFile::pointer_t(new ZipFile(name, start_offset, 4));
272 }
273 
274 
284  //: m_vs(...) -- auto-init
285 {
286 }
287 
288 
307 ZipFile::ZipFile(std::string const& filename, offset_t s_off, offset_t e_off)
308  : FileCollection(filename)
309  , m_vs(s_off, e_off)
310 {
311  std::ifstream zipfile(m_filename, std::ios::in | std::ios::binary);
312  if(!zipfile)
313  {
314  throw IOException("Error opening Zip archive file for reading in binary mode.");
315  }
316 
317  // Find and read the End of Central Directory.
319  {
320  BackBuffer bb(zipfile, m_vs);
321  ssize_t read_p(-1);
322  for(;;)
323  {
324  if(read_p < 0)
325  {
326  if(!bb.readChunk(read_p))
327  {
328  throw FileCollectionException("Unable to find zip structure: End-of-central-directory");
329  }
330  }
331  // Note: this is pretty fast since it reads from 'bb' which
332  // caches the buffer the readChunk() function just read.
333  //
334  if(eocd.read(bb, read_p))
335  {
336  // found it!
337  break;
338  }
339  --read_p;
340  }
341  }
342 
343  // Position read pointer to start of first entry in central dir.
344  m_vs.vseekg(zipfile, eocd.getOffset(), std::ios::beg);
345 
346  // TBD -- is that ", 0" still necessary? (With VC2012 and better)
347  // Give the second argument in the next line to keep Visual C++ quiet
348  //m_entries.resize(eocd.totalCount(), 0);
349  m_entries.resize(eocd.getCount());
350 
351  size_t const max_entry(eocd.getCount());
352  for(size_t entry_num(0); entry_num < max_entry; ++entry_num)
353  {
355  m_entries[entry_num].get()->read(zipfile);
356  }
357 
358  // Consistency check #1:
359  // The virtual seeker position is exactly the start offset of the
360  // Central Directory plus the Central Directory size
361  //
362  offset_t const pos(m_vs.vtellg(zipfile));
363  if(static_cast<offset_t>(eocd.getOffset() + eocd.getCentralDirectorySize()) != pos)
364  {
365  throw FileCollectionException("Zip file consistency problem. Zip file data fields are inconsistent with zip file layout.");
366  }
367 
368  // Consistency check #2:
369  // Are local headers consistent with CD headers?
370  //
371  for(auto it = m_entries.begin(); it != m_entries.end(); ++it)
372  {
377  m_vs.vseekg(zipfile, (*it)->getEntryOffset(), std::ios::beg);
378  ZipLocalEntry zlh;
379  zlh.read(zipfile);
380  if(!zipfile || !zlh.isEqual(**it))
381  {
382  throw FileCollectionException("Zip file consistency problem. Zip file data fields are inconsistent with zip file layout.");
383  }
384  }
385 
386  // we are all good!
387  m_valid = true;
388 }
389 
390 
398 {
399  return FileCollection::pointer_t(new ZipFile(*this));
400 }
401 
402 
409 {
410  close();
411 }
412 
413 
448 ZipFile::stream_pointer_t ZipFile::getInputStream(std::string const& entry_name, MatchPath matchpath)
449 {
450  mustBeValid();
451 
452  FileEntry::pointer_t entry(getEntry(entry_name, matchpath));
453  if(entry)
454  {
455  stream_pointer_t zis(new ZipInputStream(m_filename, entry->getEntryOffset() + m_vs.startOffset()));
456  return zis;
457  }
458 
459  // no entry with that name (and match) available
460  return nullptr;
461 }
462 
463 
473 void ZipFile::saveCollectionToArchive(std::ostream & os, FileCollection & collection, std::string const & zip_comment)
474 {
475  try
476  {
477  ZipOutputStream output_stream(os);
478 
479  output_stream.setComment(zip_comment);
480 
481  FileEntry::vector_t entries(collection.entries());
482  for(auto it(entries.begin()); it != entries.end(); ++it)
483  {
484  output_stream.putNextEntry(*it);
485  // get an InputStream if available (i.e. directories do not have an input stream)
486  if(!(*it)->isDirectory())
487  {
488  FileCollection::stream_pointer_t is(collection.getInputStream((*it)->getName()));
489  if(is)
490  {
491  output_stream << is->rdbuf();
492  }
493  }
494  }
495 
496  // clean up mantually so we can get any exception
497  // (so we avoid having exceptions gobbled by the destructor)
498  output_stream.closeEntry();
499  output_stream.finish();
500  output_stream.close();
501  }
502  catch(...)
503  {
504  os.setstate(std::ios::failbit);
505  throw;
506  }
507 }
508 
509 
510 } // zipios namespace
511 
512 // Local Variables:
513 // mode: cpp
514 // indent-tabs-mode: nil
515 // c-basic-offset: 4
516 // tab-width: 4
517 // End:
518 
519 // 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:397
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:473
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:283
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:408
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:260
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:448
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