* Copyright (c) 2023 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "zip_reader.h"
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include <utility>
#include "checked_cast.h"
#include "contrib/minizip/unzip.h"
#include "log.h"
#include "string_ex.h"
#include "zip_internal.h"
#include "zip_utils.h"
using namespace OHOS::AppExecFwk;
namespace OHOS {
namespace AppExecFwk {
namespace LIBZIP {
ZipReader::EntryInfo::EntryInfo(const std::string& fileNameInZip, const unz_file_info& rawFileInfo)
: filePath_(FilePath::FromUTF8Unsafe(fileNameInZip)), isDirectory_(false), isUnsafe_(false), isEncrypted_(false)
{
originalSize_ = rawFileInfo.uncompressed_size;
isDirectory_ = EndsWith(fileNameInZip, "/");
isUnsafe_ = filePath_.ReferencesParent();
if (filePath_.IsAbsolute() || StartsWith(fileNameInZip, "/")) {
isUnsafe_ = false;
}
isEncrypted_ = rawFileInfo.flag & 1;
if (GetCurrentSystemTime() != nullptr) {
lastModified_ = *GetCurrentSystemTime();
}
}
ZipReader::ZipReader()
{
Reset();
}
ZipReader::~ZipReader()
{
Close();
}
bool ZipReader::Open(FilePath& zipFilePath)
{
if (zipFile_ != nullptr) {
return false;
}
std::string zipfile = zipFilePath.Value();
zipFile_ = OpenForUnzipping(zipfile);
if (zipFile_ == nullptr) {
return false;
}
return OpenInternal();
}
bool ZipReader::OpenFromPlatformFile(PlatformFile zipFd)
{
if (zipFile_ != nullptr) {
return false;
}
zipFile_ = OpenFdForUnzipping(zipFd);
if (!zipFile_) {
return false;
}
return OpenInternal();
}
bool ZipReader::OpenFromString(const std::string& data)
{
zipFile_ = PrepareMemoryForUnzipping(data);
if (!zipFile_) {
return false;
}
return OpenInternal();
}
void ZipReader::Close()
{
if (zipFile_) {
unzClose(zipFile_);
}
Reset();
}
bool ZipReader::HasMore()
{
return !reachedEnd_;
}
bool ZipReader::AdvanceToNextEntry()
{
if (zipFile_ == nullptr) {
return false;
}
if (reachedEnd_) {
return false;
}
unz_file_pos position = {};
if (unzGetFilePos(zipFile_, &position) != UNZ_OK) {
return false;
}
const int currentEntryIndex = position.num_of_file;
if (currentEntryIndex + 1 == numEntries_) {
reachedEnd_ = true;
} else {
if (unzGoToNextFile(zipFile_) != UNZ_OK) {
return false;
}
}
currentEntryInfo_.reset();
return true;
}
bool ZipReader::OpenCurrentEntryInZip()
{
if (zipFile_ == nullptr) {
return false;
}
unz_file_info raw_file_info = {};
char raw_file_name_in_zip[kZipMaxPath] = {};
const int result =
unzGetCurrentFileInfo(zipFile_, &raw_file_info, raw_file_name_in_zip, sizeof(raw_file_name_in_zip) - 1,
NULL,
0,
NULL,
0);
if (result != UNZ_OK) {
return false;
}
if (raw_file_name_in_zip[0] == '\0') {
return false;
}
currentEntryInfo_.reset(new EntryInfo(std::string(raw_file_name_in_zip), raw_file_info));
return true;
}
bool ZipReader::ExtractCurrentEntry(WriterDelegate* delegate, uint64_t numBytesToExtract) const
{
if ((zipFile_ == nullptr) || (delegate == nullptr)) {
return false;
}
const int openResult = unzOpenCurrentFile(zipFile_);
if (openResult != UNZ_OK) {
return false;
}
if (!delegate->PrepareOutput()) {
return false;
}
auto buf = std::make_unique<char[]>(kZipBufSize);
uint64_t remainingCapacity = numBytesToExtract;
bool entirefileextracted = false;
while (remainingCapacity > 0) {
const int numBytesRead = unzReadCurrentFile(zipFile_, buf.get(), kZipBufSize);
if (numBytesRead == 0) {
entirefileextracted = true;
break;
} else if (numBytesRead < 0) {
break;
} else {
uint64_t numBytesToWrite = std::min<uint64_t>(remainingCapacity, checked_cast<uint64_t>(numBytesRead));
if (!delegate->WriteBytes(buf.get(), numBytesToWrite)) {
break;
}
if (remainingCapacity == checked_cast<uint64_t>(numBytesRead)) {
entirefileextracted = (unzReadCurrentFile(zipFile_, buf.get(), 1) == 0);
}
if (remainingCapacity >= numBytesToWrite) {
remainingCapacity -= numBytesToWrite;
}
}
}
unzCloseCurrentFile(zipFile_);
delegate->SetTimeModified(GetCurrentSystemTime());
return entirefileextracted;
}
bool ZipReader::OpenInternal()
{
if (zipFile_ == nullptr) {
return false;
}
unz_global_info zipInfo = {};
if (unzGetGlobalInfo(zipFile_, &zipInfo) != UNZ_OK) {
return false;
}
numEntries_ = zipInfo.number_entry;
if (numEntries_ < 0) {
return false;
}
reachedEnd_ = (numEntries_ == 0);
return true;
}
void ZipReader::Reset()
{
zipFile_ = nullptr;
numEntries_ = 0;
reachedEnd_ = false;
currentEntryInfo_.reset();
}
FilePathWriterDelegate::FilePathWriterDelegate(const FilePath& outputFilePath) : outputFilePath_(outputFilePath) {}
FilePathWriterDelegate::~FilePathWriterDelegate() {}
bool FilePathWriterDelegate::PrepareOutput()
{
if (!FilePathCheckValid(outputFilePath_.Value())) {
LOGE("outputFilePath_ invalid !!!.");
return false;
}
if (!FilePath::CreateDirectory(outputFilePath_.DirName())) {
return false;
}
file_ = fopen(outputFilePath_.Value().c_str(), "wb");
return FilePath::PathIsValid(outputFilePath_);
}
bool FilePathWriterDelegate::WriteBytes(const char* data, int numBytes)
{
if ((file_ == nullptr) || (numBytes <= 0) || (data == nullptr)) {
return false;
}
int writebytes = fwrite(data, 1, numBytes, file_);
return numBytes == writebytes;
}
void FilePathWriterDelegate::SetTimeModified(const struct tm* time)
{
fclose(file_);
file_ = nullptr;
}
}
}
}