1//
2// ZipLocalFileHeader.cpp
3//
4// Library: Zip
5// Package: Zip
6// Module: ZipLocalFileHeader
7//
8// Copyright (c) 2007, Applied Informatics Software Engineering GmbH.
9// and Contributors.
10//
11// SPDX-License-Identifier: BSL-1.0
12//
13
14
15#include "Poco/Zip/ZipLocalFileHeader.h"
16#include "Poco/Zip/ZipDataInfo.h"
17#include "Poco/Zip/ParseCallback.h"
18#include "Poco/Buffer.h"
19#include "Poco/Exception.h"
20#include "Poco/File.h"
21#include <cstring>
22
23
24namespace Poco {
25namespace Zip {
26
27
28const char ZipLocalFileHeader::HEADER[ZipCommon::HEADER_SIZE] = {'\x50', '\x4b', '\x03', '\x04'};
29
30
31ZipLocalFileHeader::ZipLocalFileHeader(const Poco::Path& fileName,
32 const Poco::DateTime& lastModifiedAt,
33 ZipCommon::CompressionMethod cm,
34 ZipCommon::CompressionLevel cl,
35 bool forceZip64):
36 _forceZip64(forceZip64),
37 _rawHeader(),
38 _startPos(-1),
39 _endPos(-1),
40 _fileName(),
41 _lastModifiedAt(),
42 _extraField(),
43 _crc32(0),
44 _compressedSize(0),
45 _uncompressedSize(0)
46{
47 std::memcpy(_rawHeader, HEADER, ZipCommon::HEADER_SIZE);
48 std::memset(_rawHeader+ZipCommon::HEADER_SIZE, 0, FULLHEADER_SIZE - ZipCommon::HEADER_SIZE);
49 setHostSystem(ZipCommon::HS_FAT);
50 setEncryption(false);
51 setExtraFieldSize(0);
52 setLastModifiedAt(lastModifiedAt);
53 init(fileName, cm, cl);
54}
55
56
57ZipLocalFileHeader::ZipLocalFileHeader(std::istream& inp, bool assumeHeaderRead, ParseCallback& callback):
58 _forceZip64(false),
59 _rawHeader(),
60 _startPos(inp.tellg()),
61 _endPos(-1),
62 _fileName(),
63 _lastModifiedAt(),
64 _extraField(),
65 _crc32(0),
66 _compressedSize(0),
67 _uncompressedSize(0)
68{
69 poco_assert_dbg( (EXTRA_FIELD_POS+EXTRA_FIELD_LENGTH) == FULLHEADER_SIZE);
70
71 if (assumeHeaderRead)
72 _startPos -= ZipCommon::HEADER_SIZE;
73
74 parse(inp, assumeHeaderRead);
75
76 bool ok = callback.handleZipEntry(inp, *this);
77
78 if (ok)
79 {
80 if (searchCRCAndSizesAfterData())
81 {
82 char header[ZipCommon::HEADER_SIZE]={'\x00', '\x00', '\x00', '\x00'};
83 inp.read(header, ZipCommon::HEADER_SIZE);
84 if (_forceZip64)
85 {
86 ZipDataInfo64 nfo(inp, true);
87 setCRC(nfo.getCRC32());
88 setCompressedSize(nfo.getCompressedSize());
89 setUncompressedSize(nfo.getUncompressedSize());
90 }
91 else
92 {
93 ZipDataInfo nfo(inp, true);
94 setCRC(nfo.getCRC32());
95 setCompressedSize(nfo.getCompressedSize());
96 setUncompressedSize(nfo.getUncompressedSize());
97 }
98 }
99 }
100 else
101 {
102 poco_assert_dbg(!searchCRCAndSizesAfterData());
103 ZipUtil::sync(inp);
104 }
105 _endPos = _startPos + getHeaderSize() + _compressedSize; // exclude the data block!
106}
107
108
109ZipLocalFileHeader::~ZipLocalFileHeader()
110{
111}
112
113
114void ZipLocalFileHeader::parse(std::istream& inp, bool assumeHeaderRead)
115{
116 if (!assumeHeaderRead)
117 {
118 inp.read(_rawHeader, ZipCommon::HEADER_SIZE);
119 if (inp.gcount() != ZipCommon::HEADER_SIZE)
120 throw Poco::IOException("Failed to read local file header");
121 if (std::memcmp(_rawHeader, HEADER, ZipCommon::HEADER_SIZE) != 0)
122 throw Poco::DataFormatException("Bad local file header");
123 }
124 else
125 {
126 std::memcpy(_rawHeader, HEADER, ZipCommon::HEADER_SIZE);
127 }
128
129 // read the rest of the header
130 inp.read(_rawHeader + ZipCommon::HEADER_SIZE, FULLHEADER_SIZE - ZipCommon::HEADER_SIZE);
131 poco_assert (_rawHeader[VERSION_POS + 1]>= ZipCommon::HS_FAT && _rawHeader[VERSION_POS + 1] < ZipCommon::HS_UNUSED);
132 poco_assert (getMajorVersionNumber() <= 4); // Allow for Zip64 version 4.5
133 poco_assert (ZipUtil::get16BitValue(_rawHeader, COMPR_METHOD_POS) < ZipCommon::CM_UNUSED);
134 parseDateTime();
135 Poco::UInt16 len = getFileNameLength();
136 if (len > 0)
137 {
138 Poco::Buffer<char> buf(len);
139 inp.read(buf.begin(), len);
140 _fileName = std::string(buf.begin(), len);
141 }
142
143 if (!searchCRCAndSizesAfterData())
144 {
145 _crc32 = getCRCFromHeader();
146 _compressedSize = getCompressedSizeFromHeader();
147 _uncompressedSize = getUncompressedSizeFromHeader();
148 }
149
150 if (hasExtraField())
151 {
152 len = getExtraFieldLength();
153 if (len > 0)
154 {
155 Poco::Buffer<char> xtra(len);
156 inp.read(xtra.begin(), len);
157 _extraField = std::string(xtra.begin(), len);
158 char* ptr = xtra.begin();
159 while (ptr <= xtra.begin() + len - 4)
160 {
161 Poco::UInt16 id = ZipUtil::get16BitValue(ptr, 0);
162 ptr += 2;
163 Poco::UInt16 size = ZipUtil::get16BitValue(ptr, 0);
164 ptr += 2;
165 if (id == ZipCommon::ZIP64_EXTRA_ID)
166 {
167 _forceZip64 = true;
168 poco_assert(size >= 8);
169 if (getUncompressedSizeFromHeader() == ZipCommon::ZIP64_MAGIC)
170 {
171 setUncompressedSize(ZipUtil::get64BitValue(ptr, 0));
172 size -= 8;
173 ptr += 8;
174 }
175 if (size >= 8 && getCompressedSizeFromHeader() == ZipCommon::ZIP64_MAGIC)
176 {
177 setCompressedSize(ZipUtil::get64BitValue(ptr, 0));
178 size -= 8;
179 ptr += 8;
180 }
181 }
182 else
183 {
184 ptr += size;
185 }
186 }
187 }
188 }
189}
190
191
192bool ZipLocalFileHeader::searchCRCAndSizesAfterData() const
193{
194 if (getCompressionMethod() == ZipCommon::CM_STORE || getCompressionMethod() == ZipCommon::CM_DEFLATE)
195 {
196 // check bit 3
197 return ((ZipUtil::get16BitValue(_rawHeader, GENERAL_PURPOSE_POS) & 0x0008) != 0);
198 }
199 return false;
200}
201
202
203void ZipLocalFileHeader::setFileName(const std::string& fileName, bool isDirectory)
204{
205 poco_assert (!fileName.empty());
206 Poco::Path aPath(fileName);
207
208 if (isDirectory)
209 {
210 aPath.makeDirectory();
211 setCRC(0);
212 setCompressedSize(0);
213 setUncompressedSize(0);
214 setCompressionMethod(ZipCommon::CM_STORE);
215 setCompressionLevel(ZipCommon::CL_NORMAL);
216 }
217 else
218 {
219 aPath.makeFile();
220 }
221 _fileName = aPath.toString(Poco::Path::PATH_UNIX);
222 if (_fileName[0] == '/')
223 _fileName = _fileName.substr(1);
224 if (isDirectory)
225 {
226 poco_assert_dbg (_fileName[_fileName.size()-1] == '/');
227 }
228 setFileNameLength(static_cast<Poco::UInt16>(_fileName.size()));
229}
230
231
232void ZipLocalFileHeader::init(const Poco::Path& fName, ZipCommon::CompressionMethod cm, ZipCommon::CompressionLevel cl)
233{
234 poco_assert (_fileName.empty());
235 setSearchCRCAndSizesAfterData(false);
236 Poco::Path fileName(fName);
237 fileName.setDevice(""); // clear device!
238 setFileName(fileName.toString(Poco::Path::PATH_UNIX), fileName.isDirectory());
239 setRequiredVersion(2, 0);
240 if (fileName.isFile())
241 {
242 setCompressionMethod(cm);
243 setCompressionLevel(cl);
244 }
245 else
246 setCompressionMethod(ZipCommon::CM_STORE);
247 if (_forceZip64)
248 setZip64Data();
249
250 _rawHeader[GENERAL_PURPOSE_POS+1] |= 0x08; // Set "language encoding flag" to indicate that filenames and paths are in UTF-8.
251}
252
253
254std::string ZipLocalFileHeader::createHeader() const
255{
256 std::string result(_rawHeader, FULLHEADER_SIZE);
257 result.append(_fileName);
258 result.append(_extraField);
259 return result;
260}
261
262
263} } // namespace Poco::Zip
264