Files
easy7zip/CPP/7zip/Archive/Zip/ZipItem.cpp
Igor Pavlov d789d4137d 21.03
2021-11-28 19:01:13 -08:00

419 lines
9.7 KiB
C++

// Archive/ZipItem.cpp
#include "StdAfx.h"
#include "../../../../C/CpuArch.h"
#include "../../../../C/7zCrc.h"
#include "../../../Common/IntToString.h"
#include "../../../Common/MyLinux.h"
#include "../../../Common/StringConvert.h"
#include "../../../Windows/PropVariantUtils.h"
#include "../Common/ItemNameUtils.h"
#include "ZipItem.h"
namespace NArchive {
namespace NZip {
using namespace NFileHeader;
/*
const char *k_SpecName_NTFS_STREAM = "@@NTFS@STREAM@";
const char *k_SpecName_MAC_RESOURCE_FORK = "@@MAC@RESOURCE-FORK@";
*/
static const CUInt32PCharPair g_ExtraTypes[] =
{
{ NExtraID::kZip64, "Zip64" },
{ NExtraID::kNTFS, "NTFS" },
{ NExtraID::kStrongEncrypt, "StrongCrypto" },
{ NExtraID::kUnixTime, "UT" },
{ NExtraID::kUnixExtra, "UX" },
{ NExtraID::kUnix2Extra, "Ux" },
{ NExtraID::kUnix3Extra, "ux" },
{ NExtraID::kIzUnicodeComment, "uc" },
{ NExtraID::kIzUnicodeName, "up" },
{ NExtraID::kIzNtSecurityDescriptor, "SD" },
{ NExtraID::kWzAES, "WzAES" },
{ NExtraID::kApkAlign, "ApkAlign" }
};
void CExtraSubBlock::PrintInfo(AString &s) const
{
for (unsigned i = 0; i < ARRAY_SIZE(g_ExtraTypes); i++)
{
const CUInt32PCharPair &pair = g_ExtraTypes[i];
if (pair.Value == ID)
{
s += pair.Name;
/*
if (ID == NExtraID::kApkAlign && Data.Size() >= 2)
{
char sz[32];
sz[0] = ':';
ConvertUInt32ToHex(GetUi16(Data), sz + 1);
s += sz;
for (unsigned j = 2; j < Data.Size(); j++)
{
char sz[32];
sz[0] = '-';
ConvertUInt32ToHex(Data[j], sz + 1);
s += sz;
}
}
*/
return;
}
}
{
char sz[32];
sz[0] = '0';
sz[1] = 'x';
ConvertUInt32ToHex(ID, sz + 2);
s += sz;
}
}
void CExtraBlock::PrintInfo(AString &s) const
{
if (Error)
s.Add_OptSpaced("Extra_ERROR");
if (MinorError)
s.Add_OptSpaced("Minor_Extra_ERROR");
if (IsZip64 || IsZip64_Error)
{
s.Add_OptSpaced("Zip64");
if (IsZip64_Error)
s += "_ERROR";
}
FOR_VECTOR (i, SubBlocks)
{
s.Add_Space_if_NotEmpty();
SubBlocks[i].PrintInfo(s);
}
}
bool CExtraSubBlock::ExtractNtfsTime(unsigned index, FILETIME &ft) const
{
ft.dwHighDateTime = ft.dwLowDateTime = 0;
UInt32 size = (UInt32)Data.Size();
if (ID != NExtraID::kNTFS || size < 32)
return false;
const Byte *p = (const Byte *)Data;
p += 4; // for reserved
size -= 4;
while (size > 4)
{
UInt16 tag = GetUi16(p);
unsigned attrSize = GetUi16(p + 2);
p += 4;
size -= 4;
if (attrSize > size)
attrSize = size;
if (tag == NNtfsExtra::kTagTime && attrSize >= 24)
{
p += 8 * index;
ft.dwLowDateTime = GetUi32(p);
ft.dwHighDateTime = GetUi32(p + 4);
return true;
}
p += attrSize;
size -= attrSize;
}
return false;
}
bool CExtraSubBlock::ExtractUnixTime(bool isCentral, unsigned index, UInt32 &res) const
{
res = 0;
UInt32 size = (UInt32)Data.Size();
if (ID != NExtraID::kUnixTime || size < 5)
return false;
const Byte *p = (const Byte *)Data;
Byte flags = *p++;
size--;
if (isCentral)
{
if (index != NUnixTime::kMTime ||
(flags & (1 << NUnixTime::kMTime)) == 0 ||
size < 4)
return false;
res = GetUi32(p);
return true;
}
for (unsigned i = 0; i < 3; i++)
if ((flags & (1 << i)) != 0)
{
if (size < 4)
return false;
if (index == i)
{
res = GetUi32(p);
return true;
}
p += 4;
size -= 4;
}
return false;
}
bool CExtraSubBlock::ExtractUnixExtraTime(unsigned index, UInt32 &res) const
{
res = 0;
const size_t size = Data.Size();
unsigned offset = index * 4;
if (ID != NExtraID::kUnixExtra || size < offset + 4)
return false;
const Byte *p = (const Byte *)Data + offset;
res = GetUi32(p);
return true;
}
bool CExtraBlock::GetNtfsTime(unsigned index, FILETIME &ft) const
{
FOR_VECTOR (i, SubBlocks)
{
const CExtraSubBlock &sb = SubBlocks[i];
if (sb.ID == NFileHeader::NExtraID::kNTFS)
return sb.ExtractNtfsTime(index, ft);
}
return false;
}
bool CExtraBlock::GetUnixTime(bool isCentral, unsigned index, UInt32 &res) const
{
{
FOR_VECTOR (i, SubBlocks)
{
const CExtraSubBlock &sb = SubBlocks[i];
if (sb.ID == NFileHeader::NExtraID::kUnixTime)
return sb.ExtractUnixTime(isCentral, index, res);
}
}
switch (index)
{
case NUnixTime::kMTime: index = NUnixExtra::kMTime; break;
case NUnixTime::kATime: index = NUnixExtra::kATime; break;
default: return false;
}
{
FOR_VECTOR (i, SubBlocks)
{
const CExtraSubBlock &sb = SubBlocks[i];
if (sb.ID == NFileHeader::NExtraID::kUnixExtra)
return sb.ExtractUnixExtraTime(index, res);
}
}
return false;
}
bool CLocalItem::IsDir() const
{
return NItemName::HasTailSlash(Name, GetCodePage());
}
bool CItem::IsDir() const
{
// FIXME: we can check InfoZip UTF-8 name at first.
if (NItemName::HasTailSlash(Name, GetCodePage()))
return true;
Byte hostOS = GetHostOS();
if (Size == 0 && PackSize == 0 && !Name.IsEmpty() && Name.Back() == '\\')
{
// do we need to use CharPrevExA?
// .NET Framework 4.5 : System.IO.Compression::CreateFromDirectory() probably writes backslashes to headers?
// so we support that case
switch (hostOS)
{
case NHostOS::kFAT:
case NHostOS::kNTFS:
case NHostOS::kHPFS:
case NHostOS::kVFAT:
return true;
}
}
if (!FromCentral)
return false;
UInt16 highAttrib = (UInt16)((ExternalAttrib >> 16 ) & 0xFFFF);
switch (hostOS)
{
case NHostOS::kAMIGA:
switch (highAttrib & NAmigaAttrib::kIFMT)
{
case NAmigaAttrib::kIFDIR: return true;
case NAmigaAttrib::kIFREG: return false;
default: return false; // change it throw kUnknownAttributes;
}
case NHostOS::kFAT:
case NHostOS::kNTFS:
case NHostOS::kHPFS:
case NHostOS::kVFAT:
return ((ExternalAttrib & FILE_ATTRIBUTE_DIRECTORY) != 0);
case NHostOS::kAtari:
case NHostOS::kMac:
case NHostOS::kVMS:
case NHostOS::kVM_CMS:
case NHostOS::kAcorn:
case NHostOS::kMVS:
return false; // change it throw kUnknownAttributes;
case NHostOS::kUnix:
return MY_LIN_S_ISDIR(highAttrib);
default:
return false;
}
}
UInt32 CItem::GetWinAttrib() const
{
UInt32 winAttrib = 0;
switch (GetHostOS())
{
case NHostOS::kFAT:
case NHostOS::kNTFS:
if (FromCentral)
winAttrib = ExternalAttrib;
break;
case NHostOS::kUnix:
// do we need to clear 16 low bits in this case?
if (FromCentral)
{
/*
Some programs write posix attributes in high 16 bits of ExternalAttrib
Also some programs can write additional marker flag:
0x8000 - p7zip
0x4000 - Zip in MacOS
no marker - Info-Zip
Client code has two options to detect posix field:
1) check 0x8000 marker. In that case we must add 0x8000 marker here.
2) check that high 4 bits (file type bits in posix field) of attributes are not zero.
*/
winAttrib = ExternalAttrib & 0xFFFF0000;
// #ifndef _WIN32
winAttrib |= 0x8000; // add posix mode marker
// #endif
}
break;
}
if (IsDir()) // test it;
winAttrib |= FILE_ATTRIBUTE_DIRECTORY;
return winAttrib;
}
bool CItem::GetPosixAttrib(UInt32 &attrib) const
{
// some archivers can store PosixAttrib in high 16 bits even with HostOS=FAT.
if (FromCentral && GetHostOS() == NHostOS::kUnix)
{
attrib = ExternalAttrib >> 16;
return (attrib != 0);
}
attrib = 0;
if (IsDir())
attrib = MY_LIN_S_IFDIR;
return false;
}
bool CExtraSubBlock::CheckIzUnicode(const AString &s) const
{
size_t size = Data.Size();
if (size < 1 + 4)
return false;
const Byte *p = (const Byte *)Data;
if (p[0] > 1)
return false;
if (CrcCalc(s, s.Len()) != GetUi32(p + 1))
return false;
size -= 5;
p += 5;
for (size_t i = 0; i < size; i++)
if (p[i] == 0)
return false;
return Check_UTF8_Buf((const char *)(const void *)p, size, false);
}
void CItem::GetUnicodeString(UString &res, const AString &s, bool isComment, bool useSpecifiedCodePage, UINT codePage) const
{
bool isUtf8 = IsUtf8();
// bool ignore_Utf8_Errors = true;
if (!isUtf8)
{
{
const unsigned id = isComment ?
NFileHeader::NExtraID::kIzUnicodeComment:
NFileHeader::NExtraID::kIzUnicodeName;
const CObjectVector<CExtraSubBlock> &subBlocks = GetMainExtra().SubBlocks;
FOR_VECTOR (i, subBlocks)
{
const CExtraSubBlock &sb = subBlocks[i];
if (sb.ID == id)
{
if (sb.CheckIzUnicode(s))
{
// const unsigned kIzUnicodeHeaderSize = 5;
if (Convert_UTF8_Buf_To_Unicode(
(const char *)(const void *)(const Byte *)sb.Data + 5,
sb.Data.Size() - 5, res))
return;
}
break;
}
}
}
if (useSpecifiedCodePage)
isUtf8 = (codePage == CP_UTF8);
#ifdef _WIN32
else if (GetHostOS() == NFileHeader::NHostOS::kUnix)
{
/* Some ZIP archives in Unix use UTF-8 encoding without Utf8 flag in header.
We try to get name as UTF-8.
Do we need to do it in POSIX version also? */
isUtf8 = true;
/* 21.02: we want to ignore UTF-8 errors to support file paths that are mixed
of UTF-8 and non-UTF-8 characters. */
// ignore_Utf8_Errors = false;
// ignore_Utf8_Errors = true;
}
#endif
}
if (isUtf8)
{
ConvertUTF8ToUnicode(s, res);
return;
}
MultiByteToUnicodeString2(res, s, useSpecifiedCodePage ? codePage : GetCodePage());
}
}}