1 | // |
2 | // Archive.cpp |
3 | // |
4 | // Library: SevenZip |
5 | // Package: Archive |
6 | // Module: Archive |
7 | // |
8 | // Definition of the Archive class. |
9 | // |
10 | // Copyright (c) 2014, Applied Informatics Software Engineering GmbH. |
11 | // and Contributors. |
12 | // |
13 | // SPDX-License-Identifier: BSL-1.0 |
14 | // |
15 | |
16 | |
17 | #include "Poco/SevenZip/Archive.h" |
18 | #include "Poco/TextConverter.h" |
19 | #include "Poco/UTF8Encoding.h" |
20 | #include "Poco/UTF16Encoding.h" |
21 | #include "Poco/UnicodeConverter.h" |
22 | #include "Poco/FileStream.h" |
23 | #include "Poco/File.h" |
24 | #include "Poco/Path.h" |
25 | #include "Poco/Mutex.h" |
26 | #include "7z.h" |
27 | #include "7zAlloc.h" |
28 | #include "7zCrc.h" |
29 | #include "7zFile.h" |
30 | |
31 | |
32 | namespace Poco { |
33 | namespace SevenZip { |
34 | |
35 | |
36 | class ArchiveImpl |
37 | { |
38 | public: |
39 | ArchiveImpl(const std::string& path): |
40 | _path(path) |
41 | { |
42 | initialize(); |
43 | open(); |
44 | } |
45 | |
46 | ~ArchiveImpl() |
47 | { |
48 | try |
49 | { |
50 | close(); |
51 | } |
52 | catch (...) |
53 | { |
54 | poco_unexpected(); |
55 | } |
56 | } |
57 | |
58 | const std::string& path() const |
59 | { |
60 | return _path; |
61 | } |
62 | |
63 | std::size_t size() const |
64 | { |
65 | return _entries.size(); |
66 | } |
67 | |
68 | Archive::ConstIterator begin() const |
69 | { |
70 | return _entries.begin(); |
71 | } |
72 | |
73 | Archive::ConstIterator end() const |
74 | { |
75 | return _entries.end(); |
76 | } |
77 | |
78 | std::string (const ArchiveEntry& entry, const std::string& destPath) |
79 | { |
80 | Poco::Path basePath; |
81 | if (destPath.empty()) |
82 | { |
83 | basePath = Poco::Path::current(); |
84 | } |
85 | else |
86 | { |
87 | basePath = destPath; |
88 | } |
89 | basePath.makeDirectory(); |
90 | Poco::Path entryPath(entry.path(), Poco::Path::PATH_UNIX); |
91 | Poco::Path (basePath); |
92 | extractedPath.append(entryPath); |
93 | extractedPath.makeAbsolute(); |
94 | |
95 | if (entry.isFile()) |
96 | { |
97 | Poco::UInt32 blockIndex = 0; |
98 | Byte* pOutBuffer = 0; |
99 | std::size_t outBufferSize = 0; |
100 | std::size_t offset = 0; |
101 | std::size_t = 0; |
102 | int err = SzArEx_Extract( |
103 | &_db, |
104 | &_lookStream.s, |
105 | entry.index(), |
106 | &blockIndex, |
107 | &pOutBuffer, |
108 | &outBufferSize, |
109 | &offset, |
110 | &extractedSize, |
111 | &_szAlloc, |
112 | &_szAllocTemp); |
113 | if (err == SZ_OK) |
114 | { |
115 | try |
116 | { |
117 | poco_assert (extractedSize == entry.size()); |
118 | |
119 | Poco::Path parent = extractedPath.parent(); |
120 | Poco::File dir(parent.toString()); |
121 | dir.createDirectories(); |
122 | |
123 | Poco::FileOutputStream ostr(extractedPath.toString()); |
124 | ostr.write(reinterpret_cast<const char*>(pOutBuffer) + offset, extractedSize); |
125 | IAlloc_Free(&_szAlloc, pOutBuffer); |
126 | } |
127 | catch (...) |
128 | { |
129 | IAlloc_Free(&_szAlloc, pOutBuffer); |
130 | throw; |
131 | } |
132 | } |
133 | else |
134 | { |
135 | handleError(err, entry.path()); |
136 | } |
137 | } |
138 | else |
139 | { |
140 | Poco::File dir(extractedPath.toString()); |
141 | dir.createDirectories(); |
142 | } |
143 | |
144 | return extractedPath.toString(); |
145 | } |
146 | |
147 | protected: |
148 | void initialize() |
149 | { |
150 | FileInStream_CreateVTable(&_archiveStream); |
151 | LookToRead_CreateVTable(&_lookStream, False); |
152 | |
153 | Poco::FastMutex::ScopedLock lock(_initMutex); |
154 | if (!_initialized) |
155 | { |
156 | CrcGenerateTable(); |
157 | _initialized = true; |
158 | } |
159 | } |
160 | |
161 | void open() |
162 | { |
163 | checkFile(); |
164 | |
165 | #if defined(POCO_OS_FAMILY_WINDOWS) |
166 | std::wstring wpath; |
167 | Poco::UnicodeConverter::toUTF16(_path, wpath); |
168 | if (InFile_OpenW(&_archiveStream.file, wpath.c_str()) != SZ_OK) |
169 | { |
170 | throw Poco::OpenFileException(_path); |
171 | } |
172 | #endif |
173 | |
174 | _lookStream.realStream = &_archiveStream.s; |
175 | LookToRead_Init(&_lookStream); |
176 | |
177 | SzArEx_Init(&_db); |
178 | int err = SzArEx_Open(&_db, &_lookStream.s, &_szAlloc, &_szAllocTemp); |
179 | if (err == SZ_OK) |
180 | { |
181 | loadEntries(); |
182 | } |
183 | else |
184 | { |
185 | handleError(err); |
186 | } |
187 | } |
188 | |
189 | void close() |
190 | { |
191 | SzArEx_Free(&_db, &_szAlloc); |
192 | File_Close(&_archiveStream.file); |
193 | } |
194 | |
195 | void loadEntries() |
196 | { |
197 | _entries.reserve(_db.db.NumFiles); |
198 | for (Poco::UInt32 i = 0; i < _db.db.NumFiles; i++) |
199 | { |
200 | const CSzFileItem *f = _db.db.Files + i; |
201 | |
202 | ArchiveEntry::EntryType type = f->IsDir ? ArchiveEntry::ENTRY_DIRECTORY : ArchiveEntry::ENTRY_FILE; |
203 | Poco::UInt32 attributes = f->AttribDefined ? f->Attrib : 0; |
204 | Poco::UInt64 size = f->Size; |
205 | |
206 | std::vector<Poco::UInt16> utf16Path; |
207 | std::size_t utf16PathLen = SzArEx_GetFileNameUtf16(&_db, i, 0); |
208 | utf16Path.resize(utf16PathLen, 0); |
209 | utf16PathLen--; // we don't need terminating 0 later on |
210 | SzArEx_GetFileNameUtf16(&_db, i, &utf16Path[0]); |
211 | Poco::UTF8Encoding utf8Encoding; |
212 | Poco::UTF16Encoding utf16Encoding; |
213 | Poco::TextConverter converter(utf16Encoding, utf8Encoding); |
214 | std::string utf8Path; |
215 | converter.convert(&utf16Path[0], (int) utf16PathLen*sizeof(Poco::UInt16), utf8Path); |
216 | |
217 | Poco::Timestamp lastModified(0); |
218 | if (f->MTimeDefined) |
219 | { |
220 | Poco::Timestamp::TimeVal tv(0); |
221 | tv = (static_cast<Poco::UInt64>(f->MTime.High) << 32) + f->MTime.Low; |
222 | tv -= (static_cast<Poco::Int64>(0x019DB1DE) << 32) + 0xD53E8000; |
223 | tv /= 10; |
224 | lastModified = tv; |
225 | } |
226 | |
227 | ArchiveEntry entry(type, utf8Path, size, lastModified, attributes, i); |
228 | _entries.push_back(entry); |
229 | } |
230 | } |
231 | |
232 | void checkFile() |
233 | { |
234 | Poco::File f(_path); |
235 | if (!f.exists()) |
236 | { |
237 | throw Poco::FileNotFoundException(_path); |
238 | } |
239 | if (!f.isFile()) |
240 | { |
241 | throw Poco::OpenFileException("not a file" , _path); |
242 | } |
243 | if (!f.canRead()) |
244 | { |
245 | throw Poco::FileAccessDeniedException(_path); |
246 | } |
247 | } |
248 | |
249 | void handleError(int err) |
250 | { |
251 | std::string arg; |
252 | handleError(err, arg); |
253 | } |
254 | |
255 | void handleError(int err, const std::string& arg) |
256 | { |
257 | switch (err) |
258 | { |
259 | case SZ_ERROR_DATA: |
260 | throw Poco::DataFormatException("archive" , _path, err); |
261 | case SZ_ERROR_MEM: |
262 | throw Poco::RuntimeException("7z library out of memory" , err); |
263 | case SZ_ERROR_CRC: |
264 | throw Poco::DataException("CRC error" , arg, err); |
265 | case SZ_ERROR_UNSUPPORTED: |
266 | throw Poco::DataException("unsupported archive format" , arg, err); |
267 | case SZ_ERROR_PARAM: |
268 | throw Poco::InvalidArgumentException("7z library" , err); |
269 | case SZ_ERROR_INPUT_EOF: |
270 | throw Poco::ReadFileException("EOF while reading archive" , _path, err); |
271 | case SZ_ERROR_OUTPUT_EOF: |
272 | throw Poco::WriteFileException(arg, err); |
273 | case SZ_ERROR_READ: |
274 | throw Poco::ReadFileException(_path, err); |
275 | case SZ_ERROR_WRITE: |
276 | throw Poco::WriteFileException(arg, err); |
277 | default: |
278 | throw Poco::IOException("7z error" , arg, err); |
279 | } |
280 | } |
281 | |
282 | private: |
283 | std::string _path; |
284 | Archive::EntryVec _entries; |
285 | CFileInStream _archiveStream; |
286 | CLookToRead _lookStream; |
287 | CSzArEx _db; |
288 | static ISzAlloc _szAlloc; |
289 | static ISzAlloc _szAllocTemp; |
290 | static Poco::FastMutex _initMutex; |
291 | static bool _initialized; |
292 | }; |
293 | |
294 | |
295 | ISzAlloc ArchiveImpl::_szAlloc = { SzAlloc, SzFree }; |
296 | ISzAlloc ArchiveImpl::_szAllocTemp = { SzAlloc, SzFree }; |
297 | Poco::FastMutex ArchiveImpl::_initMutex; |
298 | bool ArchiveImpl::_initialized(false); |
299 | |
300 | |
301 | Archive::Archive(const std::string& path): |
302 | _pImpl(new ArchiveImpl(path)) |
303 | { |
304 | } |
305 | |
306 | |
307 | Archive::~Archive() |
308 | { |
309 | delete _pImpl; |
310 | } |
311 | |
312 | |
313 | const std::string& Archive::path() const |
314 | { |
315 | return _pImpl->path(); |
316 | } |
317 | |
318 | |
319 | std::size_t Archive::size() const |
320 | { |
321 | return _pImpl->size(); |
322 | } |
323 | |
324 | |
325 | Archive::ConstIterator Archive::begin() const |
326 | { |
327 | return _pImpl->begin(); |
328 | } |
329 | |
330 | |
331 | Archive::ConstIterator Archive::end() const |
332 | { |
333 | return _pImpl->end(); |
334 | } |
335 | |
336 | |
337 | void Archive::(const std::string& destPath) |
338 | { |
339 | for (ConstIterator it = begin(); it != end(); ++it) |
340 | { |
341 | ExtractedEventArgs ; |
342 | extractedArgs.entry = *it; |
343 | bool success = true; |
344 | try |
345 | { |
346 | extractedArgs.extractedPath = _pImpl->extract(*it, destPath); |
347 | } |
348 | catch (Poco::Exception& exc) |
349 | { |
350 | success = false; |
351 | FailedEventArgs failedArgs; |
352 | failedArgs.entry = *it; |
353 | failedArgs.pException = &exc; |
354 | failed(this, failedArgs); |
355 | } |
356 | if (success) |
357 | { |
358 | extracted(this, extractedArgs); |
359 | } |
360 | } |
361 | } |
362 | |
363 | |
364 | std::string Archive::(const ArchiveEntry& entry, const std::string& destPath) |
365 | { |
366 | return _pImpl->extract(entry, destPath); |
367 | } |
368 | |
369 | |
370 | } } // namespace Poco::SevenZip |
371 | |