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
32namespace Poco {
33namespace SevenZip {
34
35
36class ArchiveImpl
37{
38public:
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 extract(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 extractedPath(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 extractedSize = 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
147protected:
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
282private:
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
295ISzAlloc ArchiveImpl::_szAlloc = { SzAlloc, SzFree };
296ISzAlloc ArchiveImpl::_szAllocTemp = { SzAlloc, SzFree };
297Poco::FastMutex ArchiveImpl::_initMutex;
298bool ArchiveImpl::_initialized(false);
299
300
301Archive::Archive(const std::string& path):
302 _pImpl(new ArchiveImpl(path))
303{
304}
305
306
307Archive::~Archive()
308{
309 delete _pImpl;
310}
311
312
313const std::string& Archive::path() const
314{
315 return _pImpl->path();
316}
317
318
319std::size_t Archive::size() const
320{
321 return _pImpl->size();
322}
323
324
325Archive::ConstIterator Archive::begin() const
326{
327 return _pImpl->begin();
328}
329
330
331Archive::ConstIterator Archive::end() const
332{
333 return _pImpl->end();
334}
335
336
337void Archive::extract(const std::string& destPath)
338{
339 for (ConstIterator it = begin(); it != end(); ++it)
340 {
341 ExtractedEventArgs extractedArgs;
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
364std::string Archive::extract(const ArchiveEntry& entry, const std::string& destPath)
365{
366 return _pImpl->extract(entry, destPath);
367}
368
369
370} } // namespace Poco::SevenZip
371