Base: [skip ci] Add function to read the CD of a zip file from a std::istream
This commit is contained in:
@@ -277,6 +277,7 @@ SET(FreeCADBase_CPP_SRCS
|
||||
ViewProj.cpp
|
||||
Writer.cpp
|
||||
XMLTools.cpp
|
||||
ZipHeader.cpp
|
||||
)
|
||||
|
||||
SET(SWIG_HEADERS
|
||||
@@ -339,6 +340,7 @@ SET(FreeCADBase_HPP_SRCS
|
||||
ViewProj.h
|
||||
Writer.h
|
||||
XMLTools.h
|
||||
ZipHeader.h
|
||||
)
|
||||
|
||||
SET(FreeCADBase_SRCS
|
||||
|
||||
171
src/Base/ZipHeader.cpp
Normal file
171
src/Base/ZipHeader.cpp
Normal file
@@ -0,0 +1,171 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2022 Werner Mayer <wmayer[at]users.sourceforge.net> *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#include "PreCompiled.h"
|
||||
|
||||
#include "ZipHeader.h"
|
||||
#include <zipios++/zipinputstream.h>
|
||||
|
||||
using zipios::ConstEntryPointer;
|
||||
using zipios::FileCollection;
|
||||
using zipios::ZipHeader;
|
||||
|
||||
|
||||
ZipHeader::ZipHeader(std::istream &inp, int s_off, int e_off)
|
||||
: _input(inp)
|
||||
, _vs(s_off, e_off)
|
||||
{
|
||||
init(_input);
|
||||
}
|
||||
|
||||
/** Create a copy of this instance. */
|
||||
FileCollection *ZipHeader::clone() const
|
||||
{
|
||||
return new ZipHeader(*this);
|
||||
}
|
||||
|
||||
void ZipHeader::close()
|
||||
{
|
||||
_valid = false;
|
||||
}
|
||||
|
||||
std::istream *ZipHeader::getInputStream(const ConstEntryPointer &entry)
|
||||
{
|
||||
if (!_valid)
|
||||
throw zipios::InvalidStateException("Attempt to use an invalid FileCollection");
|
||||
return getInputStream(entry->getName());
|
||||
}
|
||||
|
||||
std::istream *ZipHeader::getInputStream(const std::string &entry_name, MatchPath matchpath)
|
||||
{
|
||||
if (!_valid)
|
||||
throw zipios::InvalidStateException("Attempt to use an invalid ZipHeader");
|
||||
|
||||
zipios::ConstEntryPointer ent = getEntry(entry_name, matchpath);
|
||||
|
||||
if (!ent)
|
||||
return nullptr;
|
||||
else
|
||||
return new zipios::ZipInputStream(_input,
|
||||
static_cast<const zipios::ZipCDirEntry *>(ent.get())->getLocalHeaderOffset() + _vs.startOffset());
|
||||
}
|
||||
|
||||
bool ZipHeader::init(std::istream &_zipfile)
|
||||
{
|
||||
// Check stream error state
|
||||
if (!_zipfile) {
|
||||
setError("Error reading from file");
|
||||
return false;
|
||||
}
|
||||
|
||||
_valid = readCentralDirectory(_zipfile);
|
||||
return _valid;
|
||||
}
|
||||
|
||||
bool ZipHeader::readCentralDirectory(std::istream &_zipfile)
|
||||
{
|
||||
// Find and read eocd.
|
||||
if (!readEndOfCentralDirectory(_zipfile))
|
||||
throw zipios::FCollException("Unable to find zip structure: End-of-central-directory");
|
||||
|
||||
// Position read pointer to start of first entry in central dir.
|
||||
_vs.vseekg(_zipfile, _eocd.offset(), std::ios::beg);
|
||||
|
||||
int entry_num = 0;
|
||||
// Giving the default argument in the next line to keep Visual C++ quiet
|
||||
_entries.resize(_eocd.totalCount(), nullptr);
|
||||
while ((entry_num < _eocd.totalCount())) {
|
||||
zipios::ZipCDirEntry *ent = new zipios::ZipCDirEntry;
|
||||
_entries[entry_num] = ent;
|
||||
_zipfile >> *ent;
|
||||
if (!_zipfile) {
|
||||
if (_zipfile.bad())
|
||||
throw zipios::IOException("Error reading zip file while reading zip file central directory");
|
||||
else if (_zipfile.fail())
|
||||
throw zipios::FCollException("Zip file consistency problem. Failure while reading zip file central directory");
|
||||
else if (_zipfile.eof())
|
||||
throw zipios::IOException("Premature end of file while reading zip file central directory");
|
||||
}
|
||||
++entry_num;
|
||||
}
|
||||
|
||||
// Consistency check. eocd should start here
|
||||
|
||||
int pos = _vs.vtellg(_zipfile);
|
||||
_vs.vseekg(_zipfile, 0, std::ios::end);
|
||||
int remaining = static_cast<int>(_vs.vtellg(_zipfile)) - pos;
|
||||
if (remaining != _eocd.eocdOffSetFromEnd())
|
||||
throw zipios::FCollException("Zip file consistency problem. Zip file data fields are inconsistent with zip file layout");
|
||||
|
||||
// Consistency check 2, are local headers consistent with
|
||||
// cd headers
|
||||
if (!confirmLocalHeaders(_zipfile))
|
||||
throw zipios::FCollException("Zip file consistency problem. Zip file data fields are inconsistent with zip file layout");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ZipHeader::readEndOfCentralDirectory(std::istream &_zipfile)
|
||||
{
|
||||
zipios::BackBuffer bb(_zipfile, _vs);
|
||||
int read_p = -1;
|
||||
bool found = false;
|
||||
while (!found) {
|
||||
if (read_p < 0) {
|
||||
if (!bb.readChunk(read_p)) {
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (_eocd.read(bb, read_p)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
--read_p;
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
bool ZipHeader::confirmLocalHeaders(std::istream &_zipfile)
|
||||
{
|
||||
zipios::Entries::const_iterator it;
|
||||
zipios::ZipCDirEntry *ent;
|
||||
int inconsistencies = 0;
|
||||
zipios::ZipLocalEntry zlh;
|
||||
for (it = _entries.begin(); it != _entries.end(); ++it) {
|
||||
ent = static_cast<zipios::ZipCDirEntry *>((*it).get());
|
||||
_vs.vseekg(_zipfile, ent->getLocalHeaderOffset(), std::ios::beg);
|
||||
_zipfile >> zlh;
|
||||
if (!_zipfile || zlh != *ent) {
|
||||
inconsistencies++;
|
||||
_zipfile.clear();
|
||||
}
|
||||
}
|
||||
return !inconsistencies;
|
||||
}
|
||||
|
||||
void ZipHeader::setError(std::string /*error_str*/)
|
||||
{
|
||||
_valid = false;
|
||||
}
|
||||
82
src/Base/ZipHeader.h
Normal file
82
src/Base/ZipHeader.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2022 Werner Mayer <wmayer[at]users.sourceforge.net> *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#ifndef ZIPIOS_ZIP_HEADER_H
|
||||
#define ZIPIOS_ZIP_HEADER_H
|
||||
|
||||
#include <FCGlobal.h>
|
||||
#include <zipios++/backbuffer.h>
|
||||
#include <zipios++/fcoll.h>
|
||||
|
||||
namespace zipios
|
||||
{
|
||||
|
||||
/** ZipHeader is a FileCollection, where the files are stored in a .zip file.
|
||||
* The class is similar to zipios' ZipFile class with the difference that instead of
|
||||
* a file a std::istream can be passed.
|
||||
*/
|
||||
class BaseExport ZipHeader : public FileCollection {
|
||||
public:
|
||||
/** Opens the zip file name. If the zip "file" is
|
||||
embedded in a file that contains other data, e.g. a binary
|
||||
program, the offset of the zip file start and end must be
|
||||
specified.
|
||||
@param inp The input stream of the zip file to open.
|
||||
@param s_off Offset relative to the start of the file, that
|
||||
indicates the beginning of the zip file.
|
||||
@param e_off Offset relative to the end of the file, that
|
||||
indicates the end of the zip file. The offset is a positive number,
|
||||
even though the offset is towards the beginning of the file.
|
||||
@throw FColException Thrown if the specified file name is not a valid zip
|
||||
archive.
|
||||
@throw IOException Thrown if an I/O problem is encountered, while the directory
|
||||
of the specified zip archive is being read. */
|
||||
explicit ZipHeader(std::istream &inp, int s_off = 0, int e_off = 0);
|
||||
|
||||
/** Create a copy of this instance. */
|
||||
FileCollection *clone() const override;
|
||||
|
||||
~ZipHeader() override = default;
|
||||
|
||||
void close() override;
|
||||
|
||||
std::istream *getInputStream(const ConstEntryPointer &entry) override;
|
||||
std::istream *getInputStream(const string &entry_name, MatchPath matchpath = MATCH) override;
|
||||
|
||||
private:
|
||||
std::istream& _input;
|
||||
VirtualSeeker _vs;
|
||||
EndOfCentralDirectory _eocd;
|
||||
|
||||
bool init(std::istream &_zipfile);
|
||||
bool readCentralDirectory(std::istream &_zipfile);
|
||||
bool readEndOfCentralDirectory(std::istream &_zipfile);
|
||||
bool confirmLocalHeaders(std::istream &_zipfile);
|
||||
void setError(std::string error_str);
|
||||
};
|
||||
|
||||
} //namespace zipios
|
||||
|
||||
|
||||
#endif // ZIPIOS_ZIP_HEADER_H
|
||||
|
||||
Reference in New Issue
Block a user