diff options
Diffstat (limited to 'CPP/7zip/Archive/Zip/ZipItem.cpp')
-rw-r--r-- | CPP/7zip/Archive/Zip/ZipItem.cpp | 418 |
1 files changed, 418 insertions, 0 deletions
diff --git a/CPP/7zip/Archive/Zip/ZipItem.cpp b/CPP/7zip/Archive/Zip/ZipItem.cpp new file mode 100644 index 0000000..be33648 --- /dev/null +++ b/CPP/7zip/Archive/Zip/ZipItem.cpp | |||
@@ -0,0 +1,418 @@ | |||
1 | // Archive/ZipItem.cpp | ||
2 | |||
3 | #include "StdAfx.h" | ||
4 | |||
5 | #include "../../../../C/CpuArch.h" | ||
6 | #include "../../../../C/7zCrc.h" | ||
7 | |||
8 | #include "../../../Common/IntToString.h" | ||
9 | #include "../../../Common/MyLinux.h" | ||
10 | #include "../../../Common/StringConvert.h" | ||
11 | |||
12 | #include "../../../Windows/PropVariantUtils.h" | ||
13 | |||
14 | #include "../Common/ItemNameUtils.h" | ||
15 | |||
16 | #include "ZipItem.h" | ||
17 | |||
18 | namespace NArchive { | ||
19 | namespace NZip { | ||
20 | |||
21 | using namespace NFileHeader; | ||
22 | |||
23 | |||
24 | /* | ||
25 | const char *k_SpecName_NTFS_STREAM = "@@NTFS@STREAM@"; | ||
26 | const char *k_SpecName_MAC_RESOURCE_FORK = "@@MAC@RESOURCE-FORK@"; | ||
27 | */ | ||
28 | |||
29 | static const CUInt32PCharPair g_ExtraTypes[] = | ||
30 | { | ||
31 | { NExtraID::kZip64, "Zip64" }, | ||
32 | { NExtraID::kNTFS, "NTFS" }, | ||
33 | { NExtraID::kStrongEncrypt, "StrongCrypto" }, | ||
34 | { NExtraID::kUnixTime, "UT" }, | ||
35 | { NExtraID::kUnixExtra, "UX" }, | ||
36 | { NExtraID::kUnix2Extra, "Ux" }, | ||
37 | { NExtraID::kUnix3Extra, "ux" }, | ||
38 | { NExtraID::kIzUnicodeComment, "uc" }, | ||
39 | { NExtraID::kIzUnicodeName, "up" }, | ||
40 | { NExtraID::kIzNtSecurityDescriptor, "SD" }, | ||
41 | { NExtraID::kWzAES, "WzAES" }, | ||
42 | { NExtraID::kApkAlign, "ApkAlign" } | ||
43 | }; | ||
44 | |||
45 | void CExtraSubBlock::PrintInfo(AString &s) const | ||
46 | { | ||
47 | for (unsigned i = 0; i < ARRAY_SIZE(g_ExtraTypes); i++) | ||
48 | { | ||
49 | const CUInt32PCharPair &pair = g_ExtraTypes[i]; | ||
50 | if (pair.Value == ID) | ||
51 | { | ||
52 | s += pair.Name; | ||
53 | /* | ||
54 | if (ID == NExtraID::kApkAlign && Data.Size() >= 2) | ||
55 | { | ||
56 | char sz[32]; | ||
57 | sz[0] = ':'; | ||
58 | ConvertUInt32ToHex(GetUi16(Data), sz + 1); | ||
59 | s += sz; | ||
60 | for (unsigned j = 2; j < Data.Size(); j++) | ||
61 | { | ||
62 | char sz[32]; | ||
63 | sz[0] = '-'; | ||
64 | ConvertUInt32ToHex(Data[j], sz + 1); | ||
65 | s += sz; | ||
66 | } | ||
67 | } | ||
68 | */ | ||
69 | return; | ||
70 | } | ||
71 | } | ||
72 | { | ||
73 | char sz[32]; | ||
74 | sz[0] = '0'; | ||
75 | sz[1] = 'x'; | ||
76 | ConvertUInt32ToHex(ID, sz + 2); | ||
77 | s += sz; | ||
78 | } | ||
79 | } | ||
80 | |||
81 | |||
82 | void CExtraBlock::PrintInfo(AString &s) const | ||
83 | { | ||
84 | if (Error) | ||
85 | s.Add_OptSpaced("Extra_ERROR"); | ||
86 | |||
87 | if (MinorError) | ||
88 | s.Add_OptSpaced("Minor_Extra_ERROR"); | ||
89 | |||
90 | if (IsZip64 || IsZip64_Error) | ||
91 | { | ||
92 | s.Add_OptSpaced("Zip64"); | ||
93 | if (IsZip64_Error) | ||
94 | s += "_ERROR"; | ||
95 | } | ||
96 | |||
97 | FOR_VECTOR (i, SubBlocks) | ||
98 | { | ||
99 | s.Add_Space_if_NotEmpty(); | ||
100 | SubBlocks[i].PrintInfo(s); | ||
101 | } | ||
102 | } | ||
103 | |||
104 | |||
105 | bool CExtraSubBlock::ExtractNtfsTime(unsigned index, FILETIME &ft) const | ||
106 | { | ||
107 | ft.dwHighDateTime = ft.dwLowDateTime = 0; | ||
108 | UInt32 size = (UInt32)Data.Size(); | ||
109 | if (ID != NExtraID::kNTFS || size < 32) | ||
110 | return false; | ||
111 | const Byte *p = (const Byte *)Data; | ||
112 | p += 4; // for reserved | ||
113 | size -= 4; | ||
114 | while (size > 4) | ||
115 | { | ||
116 | UInt16 tag = GetUi16(p); | ||
117 | unsigned attrSize = GetUi16(p + 2); | ||
118 | p += 4; | ||
119 | size -= 4; | ||
120 | if (attrSize > size) | ||
121 | attrSize = size; | ||
122 | |||
123 | if (tag == NNtfsExtra::kTagTime && attrSize >= 24) | ||
124 | { | ||
125 | p += 8 * index; | ||
126 | ft.dwLowDateTime = GetUi32(p); | ||
127 | ft.dwHighDateTime = GetUi32(p + 4); | ||
128 | return true; | ||
129 | } | ||
130 | p += attrSize; | ||
131 | size -= attrSize; | ||
132 | } | ||
133 | return false; | ||
134 | } | ||
135 | |||
136 | bool CExtraSubBlock::ExtractUnixTime(bool isCentral, unsigned index, UInt32 &res) const | ||
137 | { | ||
138 | res = 0; | ||
139 | UInt32 size = (UInt32)Data.Size(); | ||
140 | if (ID != NExtraID::kUnixTime || size < 5) | ||
141 | return false; | ||
142 | const Byte *p = (const Byte *)Data; | ||
143 | Byte flags = *p++; | ||
144 | size--; | ||
145 | if (isCentral) | ||
146 | { | ||
147 | if (index != NUnixTime::kMTime || | ||
148 | (flags & (1 << NUnixTime::kMTime)) == 0 || | ||
149 | size < 4) | ||
150 | return false; | ||
151 | res = GetUi32(p); | ||
152 | return true; | ||
153 | } | ||
154 | for (unsigned i = 0; i < 3; i++) | ||
155 | if ((flags & (1 << i)) != 0) | ||
156 | { | ||
157 | if (size < 4) | ||
158 | return false; | ||
159 | if (index == i) | ||
160 | { | ||
161 | res = GetUi32(p); | ||
162 | return true; | ||
163 | } | ||
164 | p += 4; | ||
165 | size -= 4; | ||
166 | } | ||
167 | return false; | ||
168 | } | ||
169 | |||
170 | |||
171 | bool CExtraSubBlock::ExtractUnixExtraTime(unsigned index, UInt32 &res) const | ||
172 | { | ||
173 | res = 0; | ||
174 | const size_t size = Data.Size(); | ||
175 | unsigned offset = index * 4; | ||
176 | if (ID != NExtraID::kUnixExtra || size < offset + 4) | ||
177 | return false; | ||
178 | const Byte *p = (const Byte *)Data + offset; | ||
179 | res = GetUi32(p); | ||
180 | return true; | ||
181 | } | ||
182 | |||
183 | |||
184 | bool CExtraBlock::GetNtfsTime(unsigned index, FILETIME &ft) const | ||
185 | { | ||
186 | FOR_VECTOR (i, SubBlocks) | ||
187 | { | ||
188 | const CExtraSubBlock &sb = SubBlocks[i]; | ||
189 | if (sb.ID == NFileHeader::NExtraID::kNTFS) | ||
190 | return sb.ExtractNtfsTime(index, ft); | ||
191 | } | ||
192 | return false; | ||
193 | } | ||
194 | |||
195 | bool CExtraBlock::GetUnixTime(bool isCentral, unsigned index, UInt32 &res) const | ||
196 | { | ||
197 | { | ||
198 | FOR_VECTOR (i, SubBlocks) | ||
199 | { | ||
200 | const CExtraSubBlock &sb = SubBlocks[i]; | ||
201 | if (sb.ID == NFileHeader::NExtraID::kUnixTime) | ||
202 | return sb.ExtractUnixTime(isCentral, index, res); | ||
203 | } | ||
204 | } | ||
205 | |||
206 | switch (index) | ||
207 | { | ||
208 | case NUnixTime::kMTime: index = NUnixExtra::kMTime; break; | ||
209 | case NUnixTime::kATime: index = NUnixExtra::kATime; break; | ||
210 | default: return false; | ||
211 | } | ||
212 | |||
213 | { | ||
214 | FOR_VECTOR (i, SubBlocks) | ||
215 | { | ||
216 | const CExtraSubBlock &sb = SubBlocks[i]; | ||
217 | if (sb.ID == NFileHeader::NExtraID::kUnixExtra) | ||
218 | return sb.ExtractUnixExtraTime(index, res); | ||
219 | } | ||
220 | } | ||
221 | return false; | ||
222 | } | ||
223 | |||
224 | |||
225 | bool CLocalItem::IsDir() const | ||
226 | { | ||
227 | return NItemName::HasTailSlash(Name, GetCodePage()); | ||
228 | } | ||
229 | |||
230 | bool CItem::IsDir() const | ||
231 | { | ||
232 | // FIXME: we can check InfoZip UTF-8 name at first. | ||
233 | if (NItemName::HasTailSlash(Name, GetCodePage())) | ||
234 | return true; | ||
235 | |||
236 | Byte hostOS = GetHostOS(); | ||
237 | |||
238 | if (Size == 0 && PackSize == 0 && !Name.IsEmpty() && Name.Back() == '\\') | ||
239 | { | ||
240 | // do we need to use CharPrevExA? | ||
241 | // .NET Framework 4.5 : System.IO.Compression::CreateFromDirectory() probably writes backslashes to headers? | ||
242 | // so we support that case | ||
243 | switch (hostOS) | ||
244 | { | ||
245 | case NHostOS::kFAT: | ||
246 | case NHostOS::kNTFS: | ||
247 | case NHostOS::kHPFS: | ||
248 | case NHostOS::kVFAT: | ||
249 | return true; | ||
250 | } | ||
251 | } | ||
252 | |||
253 | if (!FromCentral) | ||
254 | return false; | ||
255 | |||
256 | UInt16 highAttrib = (UInt16)((ExternalAttrib >> 16 ) & 0xFFFF); | ||
257 | |||
258 | switch (hostOS) | ||
259 | { | ||
260 | case NHostOS::kAMIGA: | ||
261 | switch (highAttrib & NAmigaAttrib::kIFMT) | ||
262 | { | ||
263 | case NAmigaAttrib::kIFDIR: return true; | ||
264 | case NAmigaAttrib::kIFREG: return false; | ||
265 | default: return false; // change it throw kUnknownAttributes; | ||
266 | } | ||
267 | case NHostOS::kFAT: | ||
268 | case NHostOS::kNTFS: | ||
269 | case NHostOS::kHPFS: | ||
270 | case NHostOS::kVFAT: | ||
271 | return ((ExternalAttrib & FILE_ATTRIBUTE_DIRECTORY) != 0); | ||
272 | case NHostOS::kAtari: | ||
273 | case NHostOS::kMac: | ||
274 | case NHostOS::kVMS: | ||
275 | case NHostOS::kVM_CMS: | ||
276 | case NHostOS::kAcorn: | ||
277 | case NHostOS::kMVS: | ||
278 | return false; // change it throw kUnknownAttributes; | ||
279 | case NHostOS::kUnix: | ||
280 | return MY_LIN_S_ISDIR(highAttrib); | ||
281 | default: | ||
282 | return false; | ||
283 | } | ||
284 | } | ||
285 | |||
286 | UInt32 CItem::GetWinAttrib() const | ||
287 | { | ||
288 | UInt32 winAttrib = 0; | ||
289 | switch (GetHostOS()) | ||
290 | { | ||
291 | case NHostOS::kFAT: | ||
292 | case NHostOS::kNTFS: | ||
293 | if (FromCentral) | ||
294 | winAttrib = ExternalAttrib; | ||
295 | break; | ||
296 | case NHostOS::kUnix: | ||
297 | // do we need to clear 16 low bits in this case? | ||
298 | if (FromCentral) | ||
299 | { | ||
300 | /* | ||
301 | Some programs write posix attributes in high 16 bits of ExternalAttrib | ||
302 | Also some programs can write additional marker flag: | ||
303 | 0x8000 - p7zip | ||
304 | 0x4000 - Zip in MacOS | ||
305 | no marker - Info-Zip | ||
306 | |||
307 | Client code has two options to detect posix field: | ||
308 | 1) check 0x8000 marker. In that case we must add 0x8000 marker here. | ||
309 | 2) check that high 4 bits (file type bits in posix field) of attributes are not zero. | ||
310 | */ | ||
311 | |||
312 | winAttrib = ExternalAttrib & 0xFFFF0000; | ||
313 | |||
314 | // #ifndef _WIN32 | ||
315 | winAttrib |= 0x8000; // add posix mode marker | ||
316 | // #endif | ||
317 | } | ||
318 | break; | ||
319 | } | ||
320 | if (IsDir()) // test it; | ||
321 | winAttrib |= FILE_ATTRIBUTE_DIRECTORY; | ||
322 | return winAttrib; | ||
323 | } | ||
324 | |||
325 | bool CItem::GetPosixAttrib(UInt32 &attrib) const | ||
326 | { | ||
327 | // some archivers can store PosixAttrib in high 16 bits even with HostOS=FAT. | ||
328 | if (FromCentral && GetHostOS() == NHostOS::kUnix) | ||
329 | { | ||
330 | attrib = ExternalAttrib >> 16; | ||
331 | return (attrib != 0); | ||
332 | } | ||
333 | attrib = 0; | ||
334 | if (IsDir()) | ||
335 | attrib = MY_LIN_S_IFDIR; | ||
336 | return false; | ||
337 | } | ||
338 | |||
339 | |||
340 | bool CExtraSubBlock::CheckIzUnicode(const AString &s) const | ||
341 | { | ||
342 | size_t size = Data.Size(); | ||
343 | if (size < 1 + 4) | ||
344 | return false; | ||
345 | const Byte *p = (const Byte *)Data; | ||
346 | if (p[0] > 1) | ||
347 | return false; | ||
348 | if (CrcCalc(s, s.Len()) != GetUi32(p + 1)) | ||
349 | return false; | ||
350 | size -= 5; | ||
351 | p += 5; | ||
352 | for (size_t i = 0; i < size; i++) | ||
353 | if (p[i] == 0) | ||
354 | return false; | ||
355 | return Check_UTF8_Buf((const char *)(const void *)p, size, false); | ||
356 | } | ||
357 | |||
358 | |||
359 | void CItem::GetUnicodeString(UString &res, const AString &s, bool isComment, bool useSpecifiedCodePage, UINT codePage) const | ||
360 | { | ||
361 | bool isUtf8 = IsUtf8(); | ||
362 | // bool ignore_Utf8_Errors = true; | ||
363 | |||
364 | if (!isUtf8) | ||
365 | { | ||
366 | { | ||
367 | const unsigned id = isComment ? | ||
368 | NFileHeader::NExtraID::kIzUnicodeComment: | ||
369 | NFileHeader::NExtraID::kIzUnicodeName; | ||
370 | const CObjectVector<CExtraSubBlock> &subBlocks = GetMainExtra().SubBlocks; | ||
371 | |||
372 | FOR_VECTOR (i, subBlocks) | ||
373 | { | ||
374 | const CExtraSubBlock &sb = subBlocks[i]; | ||
375 | if (sb.ID == id) | ||
376 | { | ||
377 | if (sb.CheckIzUnicode(s)) | ||
378 | { | ||
379 | // const unsigned kIzUnicodeHeaderSize = 5; | ||
380 | if (Convert_UTF8_Buf_To_Unicode( | ||
381 | (const char *)(const void *)(const Byte *)sb.Data + 5, | ||
382 | sb.Data.Size() - 5, res)) | ||
383 | return; | ||
384 | } | ||
385 | break; | ||
386 | } | ||
387 | } | ||
388 | } | ||
389 | |||
390 | if (useSpecifiedCodePage) | ||
391 | isUtf8 = (codePage == CP_UTF8); | ||
392 | #ifdef _WIN32 | ||
393 | else if (GetHostOS() == NFileHeader::NHostOS::kUnix) | ||
394 | { | ||
395 | /* Some ZIP archives in Unix use UTF-8 encoding without Utf8 flag in header. | ||
396 | We try to get name as UTF-8. | ||
397 | Do we need to do it in POSIX version also? */ | ||
398 | isUtf8 = true; | ||
399 | |||
400 | /* 21.02: we want to ignore UTF-8 errors to support file paths that are mixed | ||
401 | of UTF-8 and non-UTF-8 characters. */ | ||
402 | // ignore_Utf8_Errors = false; | ||
403 | // ignore_Utf8_Errors = true; | ||
404 | } | ||
405 | #endif | ||
406 | } | ||
407 | |||
408 | |||
409 | if (isUtf8) | ||
410 | { | ||
411 | ConvertUTF8ToUnicode(s, res); | ||
412 | return; | ||
413 | } | ||
414 | |||
415 | MultiByteToUnicodeString2(res, s, useSpecifiedCodePage ? codePage : GetCodePage()); | ||
416 | } | ||
417 | |||
418 | }} | ||