2 * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
27 #include "common/logger.h"
28 #include "common/platform_exception.h"
29 #include "common/tools.h"
30 #include "filesystem_file.h"
32 #include "archive_file.h"
33 #include "archive_utils.h"
34 #include "un_zip_extract_request.h"
39 using namespace common;
40 using common::tools::GetErrorString;
42 UnZip::UnZip(const std::string& filename)
43 : m_zipfile_name(filename),
45 m_default_buffer_size(1024 * 1024),
48 m_unzip = unzOpen(filename.c_str());
53 for (auto& x : path_access_map) {
54 LoggerD("Setting permission for path: %s [%o] ", x.first.c_str(),
56 if (chmod(x.first.c_str(), x.second) == -1) {
57 LoggerE("Couldn't set permissions for: [%s] errno: %s", x.first.c_str(),
58 GetErrorString(errno).c_str());
64 PlatformResult UnZip::close() {
67 LoggerD("Unzip already closed - exiting");
68 return PlatformResult(ErrorCode::NO_ERROR);
71 int errclose = unzClose(m_unzip);
74 if (errclose != UNZ_OK) {
75 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, getArchiveLogMessage(errclose, "unzClose()"),
76 ("ret: %d", errclose));
79 return PlatformResult(ErrorCode::NO_ERROR);
82 PlatformResult UnZip::open(const std::string& filename, UnZipPtr* out_unzip) {
84 UnZipPtr unzip = UnZipPtr(new UnZip(filename));
86 if (!unzip->m_unzip) {
87 return LogAndCreateResult(ErrorCode::INVALID_VALUES_ERR, "Failed to open zip file",
88 ("unzOpen returned NULL : It means the file is invalid."));
90 unzip->m_is_open = true;
92 return PlatformResult(ErrorCode::NO_ERROR);
95 PlatformResult UnZip::listEntries(unsigned long* decompressedSize,
96 ArchiveFileEntryPtrMapPtr* out_map) {
99 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Failed to get list of files in zip archive");
102 int err = unzGetGlobalInfo(m_unzip, &gi);
104 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR,
105 getArchiveLogMessage(err, "unzGetGlobalInfo()"), ("ret: %d", err));
108 char filename_inzip[512];
109 unz_file_info file_info;
111 ArchiveFileEntryPtrMapPtr map = ArchiveFileEntryPtrMapPtr(new ArchiveFileEntryPtrMap());
113 unsigned long totalDecompressed = 0;
115 err = unzGoToFirstFile(m_unzip);
117 LoggerW("%s", getArchiveLogMessage(err, "unzGoToFirstFile()").c_str());
120 for (uLong i = 0; i < gi.number_entry; i++) {
121 err = unzGetCurrentFileInfo(m_unzip, &file_info, filename_inzip, sizeof(filename_inzip), NULL,
124 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR,
125 getArchiveLogMessage(err, "unzGetCurrentFileInfo()"),
129 LoggerD("file: %s | unc size: %lu | comp size: %lu", filename_inzip,
130 file_info.uncompressed_size, file_info.compressed_size);
132 ArchiveFileEntryPtr entry = ArchiveFileEntryPtr(new ArchiveFileEntry());
133 entry->setName(filename_inzip);
134 entry->setSize(file_info.uncompressed_size);
135 entry->setCompressedSize(file_info.compressed_size);
137 totalDecompressed += file_info.uncompressed_size;
139 tm date; // = file_info.tmu_date;
140 date.tm_sec = file_info.tmu_date.tm_sec;
141 date.tm_min = file_info.tmu_date.tm_min;
142 date.tm_hour = file_info.tmu_date.tm_hour;
143 date.tm_mday = file_info.tmu_date.tm_mday;
144 date.tm_mon = file_info.tmu_date.tm_mon;
145 date.tm_year = file_info.tmu_date.tm_year - 1900;
149 LoggerD("%d, %d, %d, %d, %d, %d", date.tm_hour, date.tm_min, date.tm_sec, date.tm_mday,
150 date.tm_mon, date.tm_year);
151 entry->setModified(mktime(&date));
153 map->insert(std::make_pair(filename_inzip, entry));
155 if ((i + 1) < gi.number_entry) {
156 err = unzGoToNextFile(m_unzip);
158 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR,
159 getArchiveLogMessage(err, "unzGoToNextFile()"), ("ret: %d", err));
164 (*decompressedSize) = totalDecompressed;
168 return PlatformResult(ErrorCode::NO_ERROR);
171 PlatformResult UnZip::extractAllFilesTo(const std::string& extract_path,
172 ExtractAllProgressCallback* callback) {
175 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Failed to extract zip archive");
179 // Calculate number of entries to extract and total number of bytes
182 PlatformResult result = updateCallbackWithArchiveStatistics(callback, gi);
183 if (result.error_code() != ErrorCode::NO_ERROR) {
184 LoggerE("Error: %s", result.message().c_str());
189 // Begin extracting entries
191 int err = unzGoToFirstFile(m_unzip);
193 LoggerE("%s", getArchiveLogMessage(err, "unzGoToFirstFile()").c_str());
196 for (uLong i = 0; i < gi.number_entry; i++) {
197 if (callback->isCanceled()) {
198 return LogAndCreateResult(ErrorCode::OPERATION_CANCELED_ERR, "Operation canceled");
201 result = extractCurrentFile(extract_path, std::string(), callback);
202 if (result.error_code() != ErrorCode::NO_ERROR) {
203 LoggerE("Fail: extractCurrentFile()");
207 if ((i + 1) < gi.number_entry) {
208 err = unzGoToNextFile(m_unzip);
210 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR,
211 getArchiveLogMessage(err, "unzGoToNextFile()"), ("ret: %d", err));
216 callback->callSuccessCallbackOnMainThread();
218 return PlatformResult(ErrorCode::NO_ERROR);
221 struct ExtractDataHolder {
223 ExtractEntryProgressCallback* callback;
224 std::string root_output_path;
227 PlatformResult UnZip::extractTo(ExtractEntryProgressCallback* callback) {
230 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Extract archive file entry failed");
234 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Extract archive file entry failed",
235 ("callback is NULL"));
238 if (!callback->getArchiveFileEntry()) {
239 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Extract archive file entry failed",
240 ("callback->getArchiveFileEntry() is NULL"));
243 filesystem::FilePtr out_dir = callback->getDirectory();
245 return LogAndCreateResult(ErrorCode::INVALID_VALUES_ERR, "Output directory is not correct");
248 NodePtr out_node = out_dir->getNode();
250 return LogAndCreateResult(ErrorCode::INVALID_VALUES_ERR, "Output directory is not correct");
253 PathPtr out_path = out_node->getPath();
255 return LogAndCreateResult(ErrorCode::INVALID_VALUES_ERR, "Output directory is not correct");
258 auto entry_name_in_zip = callback->getArchiveFileEntry()->getName();
259 auto root_output_path = out_dir->getNode()->getPath()->getFullPath();
260 LoggerD("Extract: [%s] to root output directory: [%s] (stripBasePath: [%s])",
261 entry_name_in_zip.c_str(), root_output_path.c_str(),
262 callback->getStripBasePath().c_str());
265 // Calculate number of entries to extract and total number of bytes
268 PlatformResult result = updateCallbackWithArchiveStatistics(callback, gi, entry_name_in_zip);
269 if (result.error_code() != ErrorCode::NO_ERROR) {
270 LoggerE("Fail: updateCallbackWithArchiveStatistics()");
275 // Begin extracting entries
280 h.callback = callback;
281 h.root_output_path = root_output_path;
283 // this loop call internally progress callbacks
284 unsigned int matched;
285 result = IterateFilesInZip(gi, entry_name_in_zip, callback, extractItFunction, matched, &h);
286 if (result.error_code() != ErrorCode::NO_ERROR) {
287 LoggerE("Fail: IterateFilesInZip()");
291 // after finish extracting success callback will be called
292 callback->callSuccessCallbackOnMainThread();
294 return PlatformResult(ErrorCode::NO_ERROR);
297 PlatformResult UnZip::extractItFunction(const std::string& file_name, unz_file_info& file_info,
300 ExtractDataHolder* h = static_cast<ExtractDataHolder*>(user_data);
302 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Could not list content of zip archive",
303 ("ExtractDataHolder is NULL!"));
306 PlatformResult result = h->unzip->extractCurrentFile(
307 h->root_output_path, h->callback->getStripBasePath(), h->callback);
308 if (result.error_code() != ErrorCode::NO_ERROR) {
309 LoggerE("Error: %s", result.message().c_str());
312 return PlatformResult(ErrorCode::NO_ERROR);
315 PlatformResult UnZip::IterateFilesInZip(unz_global_info& gi, const std::string& entry_name_in_zip,
316 OperationCallbackData* callback,
317 UnZip::IterateFunction itfunc,
318 unsigned int& num_file_or_folder_matched, void* user_data) {
320 int err = unzGoToFirstFile(m_unzip);
322 LoggerW("%s", getArchiveLogMessage(err, "unzGoToFirstFile()").c_str());
325 num_file_or_folder_matched = 0;
326 const bool is_directory = isDirectoryPath(entry_name_in_zip);
328 unz_file_info cur_file_info;
331 for (uLong i = 0; i < gi.number_entry; i++) {
332 if (callback->isCanceled()) {
333 return LogAndCreateResult(ErrorCode::OPERATION_CANCELED_ERR, "Operation canceled");
336 err = unzGetCurrentFileInfo(m_unzip, &cur_file_info, tmp_fname, sizeof(tmp_fname), NULL, 0,
339 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR,
340 getArchiveLogMessage(err, "unzGetCurrentFileInfo()"),
344 const std::string cur_filename_in_zip(tmp_fname);
347 if (!entry_name_in_zip.empty()) {
349 // If entry_name_in_zip is pointing at directory we need to check each entry in
350 // zip if its path starts with entry_name_in_zip
351 match = (0 == cur_filename_in_zip.find(entry_name_in_zip));
353 // If entry name points to file we only extract entry with matching name
354 match = (cur_filename_in_zip == entry_name_in_zip);
359 PlatformResult result = itfunc(cur_filename_in_zip, cur_file_info, user_data);
360 if (result.error_code() != ErrorCode::NO_ERROR) {
361 LoggerE("Error: %s", result.message().c_str());
364 ++num_file_or_folder_matched;
367 if ((i + 1) < gi.number_entry) {
368 err = unzGoToNextFile(m_unzip);
370 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR,
371 getArchiveLogMessage(err, "unzGoToNextFile()"), ("ret: %d", err));
376 return PlatformResult(ErrorCode::NO_ERROR);
379 PlatformResult UnZip::extractCurrentFile(const std::string& extract_path,
380 const std::string& base_strip_path,
381 BaseProgressCallback* callback) {
384 if (callback->isCanceled()) {
385 return LogAndCreateResult(ErrorCode::OPERATION_CANCELED_ERR, "Operation canceled");
388 LoggerD("extract_path: [%s] base_strip_path: [%s] ", extract_path.c_str(),
389 base_strip_path.c_str());
390 return UnZipExtractRequest::execute(*this, extract_path, base_strip_path, callback);
393 struct ArchiveStatistics {
394 ArchiveStatistics() : uncompressed_size(0), number_of_files(0), number_of_folders(0) {
397 unsigned long uncompressed_size;
398 unsigned long number_of_files;
399 unsigned long number_of_folders;
402 PlatformResult generateArchiveStatistics(const std::string& file_name, unz_file_info& file_info,
406 ArchiveStatistics* astats = static_cast<ArchiveStatistics*>(user_data);
407 astats->uncompressed_size += file_info.uncompressed_size;
409 if (isDirectoryPath(file_name)) {
410 astats->number_of_folders += 1;
412 astats->number_of_files += 1;
415 return PlatformResult(ErrorCode::NO_ERROR);
418 PlatformResult UnZip::updateCallbackWithArchiveStatistics(ExtractAllProgressCallback* callback,
419 unz_global_info& out_global_info,
420 const std::string& optional_filter) {
422 int err = unzGetGlobalInfo(m_unzip, &out_global_info);
424 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR,
425 getArchiveLogMessage(err, "unzGetGlobalInfo()"), ("ret: %d", err));
428 ArchiveStatistics astats;
429 unsigned int num_matched;
431 PlatformResult result = IterateFilesInZip(out_global_info, optional_filter, callback,
432 generateArchiveStatistics, num_matched, &astats);
433 if (result.error_code() != ErrorCode::NO_ERROR) {
434 LoggerE("Error: %s", result.message().c_str());
437 if (0 == num_matched) {
438 SLoggerE("No matching file/directory: [%s] has been found in zip archive",
439 optional_filter.c_str());
440 return LogAndCreateResult(ErrorCode::NOT_FOUND_ERR, "Could not extract file from archive");
443 callback->setExpectedDecompressedSize(astats.uncompressed_size);
444 LoggerD("Expected uncompressed size: %s",
445 bytesToReadableString(astats.uncompressed_size).c_str());
447 callback->setNumberOfFilesToExtract(astats.number_of_files);
448 LoggerD("Number entries to extract: files: %lu folders: %lu", astats.number_of_files,
449 astats.number_of_folders);
451 return PlatformResult(ErrorCode::NO_ERROR);
454 } // namespace archive
455 } // namespace extension