1//
2// ZipUtil.cpp
3//
4// Library: Zip
5// Package: Zip
6// Module: ZipUtil
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/ZipUtil.h"
16#include "Poco/Zip/ZipException.h"
17#include "Poco/Zip/ZipLocalFileHeader.h"
18#include "Poco/Zip/ZipFileInfo.h"
19#include "Poco/Zip/ZipDataInfo.h"
20#include "Poco/Zip/ZipArchiveInfo.h"
21#include <cstring>
22
23
24namespace Poco {
25namespace Zip {
26
27
28Poco::DateTime ZipUtil::parseDateTime(const char* pVal, const Poco::UInt32 timePos, const Poco::UInt32 datePos)
29{
30 Poco::UInt16 time = ZipUtil::get16BitValue(pVal, timePos);
31 Poco::UInt16 date = ZipUtil::get16BitValue(pVal, datePos);
32 // TIME: second 0-4, minute 5-10, hour 11-15, second resolution is 2!
33 int sec = 2*(time & 0x001fu); // 0000 0000 0001 1111
34 int min = ((time & 0x07e0u) >> 5); // 0000 0111 1110 0000
35 int hour = ((time & 0xf800u) >> 11); // 1111 1000 0000 0000
36
37 // DATE: day 0-4, month 5-8, year (starting with 1980): 9-16
38 int day = (date & 0x001fu); // 0000 0000 0001 1111
39 int mon = ((date & 0x01e0u) >> 5); // 0000 0001 1110 0000
40 int year = 1980+((date & 0xfe00u) >> 9); // 1111 1110 0000 0000
41
42 if (Poco::DateTime::isValid(year, mon, day, hour, min, sec))
43 return Poco::DateTime(year, mon, day, hour, min, sec);
44 else
45 return Poco::DateTime(1970, 01, 01);
46}
47
48
49void ZipUtil::setDateTime(const Poco::DateTime& dt, char* pVal, const Poco::UInt32 timePos, const Poco::UInt32 datePos)
50{
51 // TIME: second 0-4, minute 5-10, hour 11-15
52 Poco::UInt16 time = static_cast<Poco::UInt16>((dt.second()/2) + (dt.minute()<<5) + (dt.hour()<<11));
53 // DATE: day 0-4, month 5-8, year (starting with 1980): 9-16
54 int year = dt.year() - 1980;
55 if (year < 0) year = 0;
56 Poco::UInt16 date = static_cast<Poco::UInt16>(dt.day() + (dt.month()<<5) + (year<<9));
57 ZipUtil::set16BitValue(time, pVal, timePos);
58 ZipUtil::set16BitValue(date, pVal, datePos);
59}
60
61
62std::string ZipUtil::fakeZLibInitString(ZipCommon::CompressionLevel cl)
63{
64 std::string init(2, ' ');
65
66 // compression info:
67 // deflate is used, bit 0-3: 0x08
68 // dictionary size is always 32k: calc ld2(32k)-8 = ld2(2^15) - 8 = 15 - 8 = 7 --> bit 4-7: 0x70
69 init[0] = '\x78';
70
71 // now fake flags
72 // bits 0-4 check bits: set them so that init[0]*256+init[1] % 31 == 0
73 // bit 5: preset dictionary? always no for us, set to 0
74 // bits 6-7: compression level: 00 very fast, 01 fast, 10 normal, 11 best
75 if (cl == ZipCommon::CL_SUPERFAST)
76 init[1] = '\x00';
77 else if (cl == ZipCommon::CL_FAST)
78 init[1] = '\x40';
79 else if (cl == ZipCommon::CL_NORMAL)
80 init[1] = '\x80';
81 else
82 init[1] = '\xc0';
83 // now set the last 5 bits
84 Poco::UInt16 tmpVal = ((Poco::UInt16)init[0])*256+((unsigned char)init[1]);
85 char checkBits = (31 - (char)(tmpVal%31));
86 init[1] |= checkBits; // set the lower 5 bits
87 tmpVal = ((Poco::UInt16)init[0])*256+((unsigned char)init[1]);
88 poco_assert_dbg ((tmpVal % 31) == 0);
89 return init;
90}
91
92
93void ZipUtil::sync(std::istream& in)
94{
95 enum
96 {
97 PREFIX = 2,
98 BUFFER_SIZE = 1024
99 };
100 char temp[BUFFER_SIZE];
101 in.read(temp, PREFIX);
102 std::size_t tempPos = PREFIX;
103
104 while (in.good() && !in.eof())
105 {
106 // all zip headers start withe same 2byte prefix
107 if (std::memcmp(ZipLocalFileHeader::HEADER, &temp[tempPos - PREFIX], PREFIX) == 0)
108 {
109 // we have a possible header!
110 // read the next 2 bytes
111 in.read(temp+tempPos, PREFIX);
112 tempPos += PREFIX;
113 if (std::memcmp(ZipLocalFileHeader::HEADER+PREFIX, &temp[tempPos - PREFIX], PREFIX) == 0 ||
114 std::memcmp(ZipArchiveInfo::HEADER+PREFIX, &temp[tempPos - PREFIX], PREFIX) == 0 ||
115 std::memcmp(ZipFileInfo::HEADER+PREFIX, &temp[tempPos - PREFIX], PREFIX) == 0 ||
116 std::memcmp(ZipDataInfo::HEADER+PREFIX, &temp[tempPos - PREFIX], PREFIX) == 0)
117 {
118 if (std::memcmp(ZipLocalFileHeader::HEADER+PREFIX, &temp[tempPos - PREFIX], PREFIX) == 0)
119 {
120 in.seekg(-4, std::ios::cur);
121 if (!in.good()) throw Poco::IOException("Failed to seek on input stream");
122 }
123 else if (std::memcmp(ZipArchiveInfo::HEADER+PREFIX, &temp[tempPos - PREFIX], PREFIX) == 0)
124 {
125 in.seekg(-4, std::ios::cur);
126 if (!in.good()) throw Poco::IOException("Failed to seek on input stream");
127 }
128 else if (std::memcmp(ZipFileInfo::HEADER+PREFIX, &temp[tempPos - PREFIX], PREFIX) == 0)
129 {
130 in.seekg(-4, std::ios::cur);
131 if (!in.good()) throw Poco::IOException("Failed to seek on input stream");
132 }
133 else
134 {
135 in.seekg(-4, std::ios::cur);
136 if (!in.good()) throw Poco::IOException("Failed to seek on input stream");
137 }
138 return;
139 }
140 else
141 {
142 // we have read 2 bytes, should only be one: putback the last char
143 in.putback(temp[tempPos - 1]);
144 if (!in.good()) throw Poco::IOException("Failed to putback on input stream");
145 --tempPos;
146 }
147 }
148 else
149 {
150 // read one byte
151 in.read(temp + tempPos, 1);
152 ++tempPos;
153 }
154
155 if (tempPos > (BUFFER_SIZE - ZipCommon::HEADER_SIZE))
156 {
157 std::memcpy(temp, &temp[tempPos - ZipCommon::HEADER_SIZE], ZipCommon::HEADER_SIZE);
158 tempPos = ZipCommon::HEADER_SIZE;
159 }
160 }
161}
162
163
164void ZipUtil::syncDataDescriptor(std::istream & in, bool force64)
165{
166 std::streampos start = in.tellg();
167 const int eof = std::char_traits<char>::eof();
168
169 int c = in.get();
170 do
171 {
172 while (c != eof && c != ZipDataInfo::HEADER[0]) { c = in.get(); }
173
174 if (c == eof) return;
175
176 bool match = true;
177 for (int i = 1; i < 4 && match; i++)
178 {
179 c = in.get();
180 if (c != ZipDataInfo::HEADER[i]) match = false;
181 }
182
183 if (match)
184 {
185 std::streampos end = in.tellg();
186
187 if (force64)
188 {
189 ZipDataInfo64 nfo(in, true);
190 if (nfo.isValid())
191 {
192 if (end - start == nfo.getCompressedSize() + 4)
193 {
194 in.seekg(-static_cast<int>(ZipDataInfo64::getFullHeaderSize()), std::ios::cur);
195 if (!in.good()) throw Poco::IOException("Failed to seek on input stream");
196 break;
197 }
198 else
199 {
200 in.seekg(-static_cast<int>(ZipDataInfo64::getFullHeaderSize()) + 4, std::ios::cur);
201 if (!in.good()) throw Poco::IOException("Failed to seek on input stream");
202 }
203 }
204 }
205 else
206 {
207 ZipDataInfo nfo(in, true);
208 if (nfo.isValid())
209 {
210 if (end - start == nfo.getCompressedSize() + 4)
211 {
212 in.seekg(-static_cast<int>(ZipDataInfo::getFullHeaderSize()), std::ios::cur);
213 if (!in.good()) throw Poco::IOException("Failed to seek on input stream");
214 break;
215 }
216 else
217 {
218 in.seekg(-static_cast<int>(ZipDataInfo::getFullHeaderSize()) + 4, std::ios::cur);
219 if (!in.good()) throw Poco::IOException("Failed to seek on input stream");
220 }
221 }
222 }
223 }
224 } while (c != eof);
225}
226
227
228void ZipUtil::verifyZipEntryFileName(const std::string& fn)
229{
230 if (fn.find("\\") != std::string::npos)
231 throw ZipException("Illegal entry name " + fn + " containing \\");
232 if (fn == "/")
233 throw ZipException("Illegal entry name /");
234 if (fn.empty())
235 throw ZipException("Illegal empty entry name");
236 if (!ZipCommon::isValidPath(fn))
237 throw ZipException("Illegal entry name " + fn + " containing parent directory reference");
238}
239
240
241std::string ZipUtil::validZipEntryFileName(const Poco::Path& entry)
242{
243 std::string fn = entry.toString(Poco::Path::PATH_UNIX);
244 verifyZipEntryFileName(fn);
245 return fn;
246}
247
248
249} } // namespace Poco::Zip
250