rlm@1
|
1 // ZipHandler.cpp
|
rlm@1
|
2
|
rlm@1
|
3 #include "StdAfx.h"
|
rlm@1
|
4
|
rlm@1
|
5 #include "Common/ComTry.h"
|
rlm@1
|
6 #include "Common/Defs.h"
|
rlm@1
|
7 #include "Common/IntToString.h"
|
rlm@1
|
8 #include "Common/StringConvert.h"
|
rlm@1
|
9
|
rlm@1
|
10 #include "Windows/PropVariant.h"
|
rlm@1
|
11 #include "Windows/Time.h"
|
rlm@1
|
12
|
rlm@1
|
13 #include "../../IPassword.h"
|
rlm@1
|
14
|
rlm@1
|
15 #include "../../Common/CreateCoder.h"
|
rlm@1
|
16 #include "../../Common/FilterCoder.h"
|
rlm@1
|
17 #include "../../Common/ProgressUtils.h"
|
rlm@1
|
18 #include "../../Common/StreamObjects.h"
|
rlm@1
|
19 #include "../../Common/StreamUtils.h"
|
rlm@1
|
20
|
rlm@1
|
21 #include "../../Compress/CopyCoder.h"
|
rlm@1
|
22 #include "../../Compress/LzmaDecoder.h"
|
rlm@1
|
23 #include "../../Compress/ImplodeDecoder.h"
|
rlm@1
|
24 #include "../../Compress/ShrinkDecoder.h"
|
rlm@1
|
25
|
rlm@1
|
26 #include "../../Crypto/WzAes.h"
|
rlm@1
|
27 #include "../../Crypto/ZipCrypto.h"
|
rlm@1
|
28 #include "../../Crypto/ZipStrong.h"
|
rlm@1
|
29
|
rlm@1
|
30 #include "../Common/ItemNameUtils.h"
|
rlm@1
|
31 #include "../Common/OutStreamWithCRC.h"
|
rlm@1
|
32
|
rlm@1
|
33 #include "ZipHandler.h"
|
rlm@1
|
34
|
rlm@1
|
35 using namespace NWindows;
|
rlm@1
|
36
|
rlm@1
|
37 namespace NArchive {
|
rlm@1
|
38 namespace NZip {
|
rlm@1
|
39
|
rlm@1
|
40 // static const CMethodId kMethodId_Store = 0;
|
rlm@1
|
41 static const CMethodId kMethodId_ZipBase = 0x040100;
|
rlm@1
|
42 static const CMethodId kMethodId_BZip2 = 0x040202;
|
rlm@1
|
43
|
rlm@1
|
44 const wchar_t *kHostOS[] =
|
rlm@1
|
45 {
|
rlm@1
|
46 L"FAT",
|
rlm@1
|
47 L"AMIGA",
|
rlm@1
|
48 L"VMS",
|
rlm@1
|
49 L"Unix",
|
rlm@1
|
50 L"VM/CMS",
|
rlm@1
|
51 L"Atari",
|
rlm@1
|
52 L"HPFS",
|
rlm@1
|
53 L"Macintosh",
|
rlm@1
|
54 L"Z-System",
|
rlm@1
|
55 L"CP/M",
|
rlm@1
|
56 L"TOPS-20",
|
rlm@1
|
57 L"NTFS",
|
rlm@1
|
58 L"SMS/QDOS",
|
rlm@1
|
59 L"Acorn",
|
rlm@1
|
60 L"VFAT",
|
rlm@1
|
61 L"MVS",
|
rlm@1
|
62 L"BeOS",
|
rlm@1
|
63 L"Tandem",
|
rlm@1
|
64 L"OS/400",
|
rlm@1
|
65 L"OS/X"
|
rlm@1
|
66 };
|
rlm@1
|
67
|
rlm@1
|
68
|
rlm@1
|
69 static const int kNumHostOSes = sizeof(kHostOS) / sizeof(kHostOS[0]);
|
rlm@1
|
70
|
rlm@1
|
71 static const wchar_t *kUnknownOS = L"Unknown";
|
rlm@1
|
72
|
rlm@1
|
73 STATPROPSTG kProps[] =
|
rlm@1
|
74 {
|
rlm@1
|
75 { NULL, kpidPath, VT_BSTR},
|
rlm@1
|
76 { NULL, kpidIsDir, VT_BOOL},
|
rlm@1
|
77 { NULL, kpidSize, VT_UI8},
|
rlm@1
|
78 { NULL, kpidPackSize, VT_UI8},
|
rlm@1
|
79 { NULL, kpidMTime, VT_FILETIME},
|
rlm@1
|
80 { NULL, kpidCTime, VT_FILETIME},
|
rlm@1
|
81 { NULL, kpidATime, VT_FILETIME},
|
rlm@1
|
82
|
rlm@1
|
83 { NULL, kpidAttrib, VT_UI4},
|
rlm@1
|
84
|
rlm@1
|
85 { NULL, kpidEncrypted, VT_BOOL},
|
rlm@1
|
86 { NULL, kpidComment, VT_BSTR},
|
rlm@1
|
87
|
rlm@1
|
88 { NULL, kpidCRC, VT_UI4},
|
rlm@1
|
89
|
rlm@1
|
90 { NULL, kpidMethod, VT_BSTR},
|
rlm@1
|
91 { NULL, kpidHostOS, VT_BSTR}
|
rlm@1
|
92
|
rlm@1
|
93 // { NULL, kpidUnpackVer, VT_UI1},
|
rlm@1
|
94 };
|
rlm@1
|
95
|
rlm@1
|
96 const wchar_t *kMethods[] =
|
rlm@1
|
97 {
|
rlm@1
|
98 L"Store",
|
rlm@1
|
99 L"Shrink",
|
rlm@1
|
100 L"Reduced1",
|
rlm@1
|
101 L"Reduced2",
|
rlm@1
|
102 L"Reduced2",
|
rlm@1
|
103 L"Reduced3",
|
rlm@1
|
104 L"Implode",
|
rlm@1
|
105 L"Tokenizing",
|
rlm@1
|
106 L"Deflate",
|
rlm@1
|
107 L"Deflate64",
|
rlm@1
|
108 L"PKImploding"
|
rlm@1
|
109 };
|
rlm@1
|
110
|
rlm@1
|
111 const int kNumMethods = sizeof(kMethods) / sizeof(kMethods[0]);
|
rlm@1
|
112 const wchar_t *kBZip2Method = L"BZip2";
|
rlm@1
|
113 const wchar_t *kLZMAMethod = L"LZMA";
|
rlm@1
|
114 const wchar_t *kJpegMethod = L"Jpeg";
|
rlm@1
|
115 const wchar_t *kWavPackMethod = L"WavPack";
|
rlm@1
|
116 const wchar_t *kPPMdMethod = L"PPMd";
|
rlm@1
|
117 const wchar_t *kAESMethod = L"AES";
|
rlm@1
|
118 const wchar_t *kZipCryptoMethod = L"ZipCrypto";
|
rlm@1
|
119 const wchar_t *kStrongCryptoMethod = L"StrongCrypto";
|
rlm@1
|
120
|
rlm@1
|
121 struct CStrongCryptoPair
|
rlm@1
|
122 {
|
rlm@1
|
123 UInt16 Id;
|
rlm@1
|
124 const wchar_t *Name;
|
rlm@1
|
125 };
|
rlm@1
|
126
|
rlm@1
|
127 CStrongCryptoPair g_StrongCryptoPairs[] =
|
rlm@1
|
128 {
|
rlm@1
|
129 { NStrongCryptoFlags::kDES, L"DES" },
|
rlm@1
|
130 { NStrongCryptoFlags::kRC2old, L"RC2a" },
|
rlm@1
|
131 { NStrongCryptoFlags::k3DES168, L"3DES-168" },
|
rlm@1
|
132 { NStrongCryptoFlags::k3DES112, L"3DES-112" },
|
rlm@1
|
133 { NStrongCryptoFlags::kAES128, L"pkAES-128" },
|
rlm@1
|
134 { NStrongCryptoFlags::kAES192, L"pkAES-192" },
|
rlm@1
|
135 { NStrongCryptoFlags::kAES256, L"pkAES-256" },
|
rlm@1
|
136 { NStrongCryptoFlags::kRC2, L"RC2" },
|
rlm@1
|
137 { NStrongCryptoFlags::kBlowfish, L"Blowfish" },
|
rlm@1
|
138 { NStrongCryptoFlags::kTwofish, L"Twofish" },
|
rlm@1
|
139 { NStrongCryptoFlags::kRC4, L"RC4" }
|
rlm@1
|
140 };
|
rlm@1
|
141
|
rlm@1
|
142 STATPROPSTG kArcProps[] =
|
rlm@1
|
143 {
|
rlm@1
|
144 { NULL, kpidBit64, VT_BOOL},
|
rlm@1
|
145 { NULL, kpidComment, VT_BSTR}
|
rlm@1
|
146 };
|
rlm@1
|
147
|
rlm@1
|
148 CHandler::CHandler()
|
rlm@1
|
149 {
|
rlm@1
|
150 InitMethodProperties();
|
rlm@1
|
151 }
|
rlm@1
|
152
|
rlm@1
|
153 static AString BytesToString(const CByteBuffer &data)
|
rlm@1
|
154 {
|
rlm@1
|
155 AString s;
|
rlm@1
|
156 int size = (int)data.GetCapacity();
|
rlm@1
|
157 if (size > 0)
|
rlm@1
|
158 {
|
rlm@1
|
159 char *p = s.GetBuffer(size + 1);
|
rlm@1
|
160 memcpy(p, (const Byte *)data, size);
|
rlm@1
|
161 p[size] = '\0';
|
rlm@1
|
162 s.ReleaseBuffer();
|
rlm@1
|
163 }
|
rlm@1
|
164 return s;
|
rlm@1
|
165 }
|
rlm@1
|
166
|
rlm@1
|
167 IMP_IInArchive_Props
|
rlm@1
|
168 IMP_IInArchive_ArcProps
|
rlm@1
|
169
|
rlm@1
|
170 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
|
rlm@1
|
171 {
|
rlm@1
|
172 COM_TRY_BEGIN
|
rlm@1
|
173 NWindows::NCOM::CPropVariant prop;
|
rlm@1
|
174 switch(propID)
|
rlm@1
|
175 {
|
rlm@1
|
176 case kpidBit64: if (m_Archive.IsZip64) prop = m_Archive.IsZip64; break;
|
rlm@1
|
177 case kpidComment:
|
rlm@1
|
178 prop = MultiByteToUnicodeString(BytesToString(m_Archive.m_ArchiveInfo.Comment), CP_ACP);
|
rlm@1
|
179 break;
|
rlm@1
|
180 }
|
rlm@1
|
181 prop.Detach(value);
|
rlm@1
|
182 COM_TRY_END
|
rlm@1
|
183 return S_OK;
|
rlm@1
|
184 }
|
rlm@1
|
185
|
rlm@1
|
186 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
|
rlm@1
|
187 {
|
rlm@1
|
188 *numItems = m_Items.Size();
|
rlm@1
|
189 return S_OK;
|
rlm@1
|
190 }
|
rlm@1
|
191
|
rlm@1
|
192 STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
|
rlm@1
|
193 {
|
rlm@1
|
194 COM_TRY_BEGIN
|
rlm@1
|
195 NWindows::NCOM::CPropVariant prop;
|
rlm@1
|
196 const CItemEx &item = m_Items[index];
|
rlm@1
|
197 switch(propID)
|
rlm@1
|
198 {
|
rlm@1
|
199 case kpidPath: prop = NItemName::GetOSName2(item.GetUnicodeString(item.Name)); break;
|
rlm@1
|
200 case kpidIsDir: prop = item.IsDir(); break;
|
rlm@1
|
201 case kpidSize: prop = item.UnPackSize; break;
|
rlm@1
|
202 case kpidPackSize: prop = item.PackSize; break;
|
rlm@1
|
203 case kpidTimeType:
|
rlm@1
|
204 {
|
rlm@1
|
205 FILETIME utcFileTime;
|
rlm@1
|
206 if (item.CentralExtra.GetNtfsTime(NFileHeader::NNtfsExtra::kTagTime, utcFileTime))
|
rlm@1
|
207 prop = (UInt32)NFileTimeType::kWindows;
|
rlm@1
|
208 break;
|
rlm@1
|
209 }
|
rlm@1
|
210 case kpidCTime:
|
rlm@1
|
211 {
|
rlm@1
|
212 FILETIME ft;
|
rlm@1
|
213 if (item.CentralExtra.GetNtfsTime(NFileHeader::NNtfsExtra::kCTime, ft))
|
rlm@1
|
214 prop = ft;
|
rlm@1
|
215 break;
|
rlm@1
|
216 }
|
rlm@1
|
217 case kpidATime:
|
rlm@1
|
218 {
|
rlm@1
|
219 FILETIME ft;
|
rlm@1
|
220 if (item.CentralExtra.GetNtfsTime(NFileHeader::NNtfsExtra::kATime, ft))
|
rlm@1
|
221 prop = ft;
|
rlm@1
|
222 break;
|
rlm@1
|
223 }
|
rlm@1
|
224 case kpidMTime:
|
rlm@1
|
225 {
|
rlm@1
|
226 FILETIME utcFileTime;
|
rlm@1
|
227 if (!item.CentralExtra.GetNtfsTime(NFileHeader::NNtfsExtra::kMTime, utcFileTime))
|
rlm@1
|
228 {
|
rlm@1
|
229 FILETIME localFileTime;
|
rlm@1
|
230 if (NTime::DosTimeToFileTime(item.Time, localFileTime))
|
rlm@1
|
231 {
|
rlm@1
|
232 if (!LocalFileTimeToFileTime(&localFileTime, &utcFileTime))
|
rlm@1
|
233 utcFileTime.dwHighDateTime = utcFileTime.dwLowDateTime = 0;
|
rlm@1
|
234 }
|
rlm@1
|
235 else
|
rlm@1
|
236 utcFileTime.dwHighDateTime = utcFileTime.dwLowDateTime = 0;
|
rlm@1
|
237 }
|
rlm@1
|
238 prop = utcFileTime;
|
rlm@1
|
239 break;
|
rlm@1
|
240 }
|
rlm@1
|
241 case kpidAttrib: prop = item.GetWinAttributes(); break;
|
rlm@1
|
242 case kpidEncrypted: prop = item.IsEncrypted(); break;
|
rlm@1
|
243 case kpidComment: prop = item.GetUnicodeString(BytesToString(item.Comment)); break;
|
rlm@1
|
244 case kpidCRC: if (item.IsThereCrc()) prop = item.FileCRC; break;
|
rlm@1
|
245 case kpidMethod:
|
rlm@1
|
246 {
|
rlm@1
|
247 UInt16 methodId = item.CompressionMethod;
|
rlm@1
|
248 UString method;
|
rlm@1
|
249 if (item.IsEncrypted())
|
rlm@1
|
250 {
|
rlm@1
|
251 if (methodId == NFileHeader::NCompressionMethod::kWzAES)
|
rlm@1
|
252 {
|
rlm@1
|
253 method = kAESMethod;
|
rlm@1
|
254 CWzAesExtraField aesField;
|
rlm@1
|
255 if (item.CentralExtra.GetWzAesField(aesField))
|
rlm@1
|
256 {
|
rlm@1
|
257 method += L"-";
|
rlm@1
|
258 wchar_t s[32];
|
rlm@1
|
259 ConvertUInt64ToString((aesField.Strength + 1) * 64 , s);
|
rlm@1
|
260 method += s;
|
rlm@1
|
261 method += L" ";
|
rlm@1
|
262 methodId = aesField.Method;
|
rlm@1
|
263 }
|
rlm@1
|
264 }
|
rlm@1
|
265 else
|
rlm@1
|
266 {
|
rlm@1
|
267 if (item.IsStrongEncrypted())
|
rlm@1
|
268 {
|
rlm@1
|
269 CStrongCryptoField f;
|
rlm@1
|
270 bool finded = false;
|
rlm@1
|
271 if (item.CentralExtra.GetStrongCryptoField(f))
|
rlm@1
|
272 {
|
rlm@1
|
273 for (int i = 0; i < sizeof(g_StrongCryptoPairs) / sizeof(g_StrongCryptoPairs[0]); i++)
|
rlm@1
|
274 {
|
rlm@1
|
275 const CStrongCryptoPair &pair = g_StrongCryptoPairs[i];
|
rlm@1
|
276 if (f.AlgId == pair.Id)
|
rlm@1
|
277 {
|
rlm@1
|
278 method += pair.Name;
|
rlm@1
|
279 finded = true;
|
rlm@1
|
280 break;
|
rlm@1
|
281 }
|
rlm@1
|
282 }
|
rlm@1
|
283 }
|
rlm@1
|
284 if (!finded)
|
rlm@1
|
285 method += kStrongCryptoMethod;
|
rlm@1
|
286 }
|
rlm@1
|
287 else
|
rlm@1
|
288 method += kZipCryptoMethod;
|
rlm@1
|
289 method += L" ";
|
rlm@1
|
290 }
|
rlm@1
|
291 }
|
rlm@1
|
292 if (methodId < kNumMethods)
|
rlm@1
|
293 method += kMethods[methodId];
|
rlm@1
|
294 else switch (methodId)
|
rlm@1
|
295 {
|
rlm@1
|
296 case NFileHeader::NCompressionMethod::kLZMA:
|
rlm@1
|
297 method += kLZMAMethod;
|
rlm@1
|
298 if (item.IsLzmaEOS())
|
rlm@1
|
299 method += L":EOS";
|
rlm@1
|
300 break;
|
rlm@1
|
301 case NFileHeader::NCompressionMethod::kBZip2: method += kBZip2Method; break;
|
rlm@1
|
302 case NFileHeader::NCompressionMethod::kJpeg: method += kJpegMethod; break;
|
rlm@1
|
303 case NFileHeader::NCompressionMethod::kWavPack: method += kWavPackMethod; break;
|
rlm@1
|
304 case NFileHeader::NCompressionMethod::kPPMd: method += kPPMdMethod; break;
|
rlm@1
|
305 default:
|
rlm@1
|
306 {
|
rlm@1
|
307 wchar_t s[32];
|
rlm@1
|
308 ConvertUInt64ToString(methodId, s);
|
rlm@1
|
309 method += s;
|
rlm@1
|
310 }
|
rlm@1
|
311 }
|
rlm@1
|
312 prop = method;
|
rlm@1
|
313 break;
|
rlm@1
|
314 }
|
rlm@1
|
315 case kpidHostOS:
|
rlm@1
|
316 prop = (item.MadeByVersion.HostOS < kNumHostOSes) ?
|
rlm@1
|
317 (kHostOS[item.MadeByVersion.HostOS]) : kUnknownOS;
|
rlm@1
|
318 break;
|
rlm@1
|
319 }
|
rlm@1
|
320 prop.Detach(value);
|
rlm@1
|
321 return S_OK;
|
rlm@1
|
322 COM_TRY_END
|
rlm@1
|
323 }
|
rlm@1
|
324
|
rlm@1
|
325 class CProgressImp: public CProgressVirt
|
rlm@1
|
326 {
|
rlm@1
|
327 CMyComPtr<IArchiveOpenCallback> _callback;
|
rlm@1
|
328 public:
|
rlm@1
|
329 STDMETHOD(SetTotal)(UInt64 numFiles);
|
rlm@1
|
330 STDMETHOD(SetCompleted)(UInt64 numFiles);
|
rlm@1
|
331 CProgressImp(IArchiveOpenCallback *callback): _callback(callback) {}
|
rlm@1
|
332 };
|
rlm@1
|
333
|
rlm@1
|
334 STDMETHODIMP CProgressImp::SetTotal(UInt64 numFiles)
|
rlm@1
|
335 {
|
rlm@1
|
336 if (_callback)
|
rlm@1
|
337 return _callback->SetTotal(&numFiles, NULL);
|
rlm@1
|
338 return S_OK;
|
rlm@1
|
339 }
|
rlm@1
|
340
|
rlm@1
|
341 STDMETHODIMP CProgressImp::SetCompleted(UInt64 numFiles)
|
rlm@1
|
342 {
|
rlm@1
|
343 if (_callback)
|
rlm@1
|
344 return _callback->SetCompleted(&numFiles, NULL);
|
rlm@1
|
345 return S_OK;
|
rlm@1
|
346 }
|
rlm@1
|
347
|
rlm@1
|
348 STDMETHODIMP CHandler::Open(IInStream *inStream,
|
rlm@1
|
349 const UInt64 *maxCheckStartPosition, IArchiveOpenCallback *callback)
|
rlm@1
|
350 {
|
rlm@1
|
351 COM_TRY_BEGIN
|
rlm@1
|
352 try
|
rlm@1
|
353 {
|
rlm@1
|
354 Close();
|
rlm@1
|
355 RINOK(inStream->Seek(0, STREAM_SEEK_SET, NULL));
|
rlm@1
|
356 RINOK(m_Archive.Open(inStream, maxCheckStartPosition));
|
rlm@1
|
357 CProgressImp progressImp(callback);
|
rlm@1
|
358 return m_Archive.ReadHeaders(m_Items, &progressImp);
|
rlm@1
|
359 }
|
rlm@1
|
360 catch(const CInArchiveException &) { Close(); return S_FALSE; }
|
rlm@1
|
361 catch(...) { Close(); throw; }
|
rlm@1
|
362 COM_TRY_END
|
rlm@1
|
363 }
|
rlm@1
|
364
|
rlm@1
|
365 STDMETHODIMP CHandler::Close()
|
rlm@1
|
366 {
|
rlm@1
|
367 m_Items.Clear();
|
rlm@1
|
368 m_Archive.Close();
|
rlm@1
|
369 return S_OK;
|
rlm@1
|
370 }
|
rlm@1
|
371
|
rlm@1
|
372 //////////////////////////////////////
|
rlm@1
|
373 // CHandler::DecompressItems
|
rlm@1
|
374
|
rlm@1
|
375 class CLzmaDecoder:
|
rlm@1
|
376 public ICompressCoder,
|
rlm@1
|
377 public CMyUnknownImp
|
rlm@1
|
378 {
|
rlm@1
|
379 NCompress::NLzma::CDecoder *DecoderSpec;
|
rlm@1
|
380 CMyComPtr<ICompressCoder> Decoder;
|
rlm@1
|
381 public:
|
rlm@1
|
382 CLzmaDecoder();
|
rlm@1
|
383 STDMETHOD(Code)(ISequentialInStream *inStream, ISequentialOutStream *outStream,
|
rlm@1
|
384 const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress);
|
rlm@1
|
385
|
rlm@1
|
386 MY_UNKNOWN_IMP
|
rlm@1
|
387 };
|
rlm@1
|
388
|
rlm@1
|
389 CLzmaDecoder::CLzmaDecoder()
|
rlm@1
|
390 {
|
rlm@1
|
391 DecoderSpec = new NCompress::NLzma::CDecoder;
|
rlm@1
|
392 Decoder = DecoderSpec;
|
rlm@1
|
393 }
|
rlm@1
|
394
|
rlm@1
|
395 HRESULT CLzmaDecoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream,
|
rlm@1
|
396 const UInt64 * /* inSize */, const UInt64 *outSize, ICompressProgressInfo *progress)
|
rlm@1
|
397 {
|
rlm@1
|
398 Byte buf[9];
|
rlm@1
|
399 RINOK(ReadStream_FALSE(inStream, buf, 9));
|
rlm@1
|
400 if (buf[2] != 5 || buf[3] != 0)
|
rlm@1
|
401 return E_NOTIMPL;
|
rlm@1
|
402 RINOK(DecoderSpec->SetDecoderProperties2(buf + 4, 5));
|
rlm@1
|
403 return Decoder->Code(inStream, outStream, NULL, outSize, progress);
|
rlm@1
|
404 }
|
rlm@1
|
405
|
rlm@1
|
406 struct CMethodItem
|
rlm@1
|
407 {
|
rlm@1
|
408 UInt16 ZipMethod;
|
rlm@1
|
409 CMyComPtr<ICompressCoder> Coder;
|
rlm@1
|
410 };
|
rlm@1
|
411
|
rlm@1
|
412 class CZipDecoder
|
rlm@1
|
413 {
|
rlm@1
|
414 NCrypto::NZip::CDecoder *_zipCryptoDecoderSpec;
|
rlm@1
|
415 NCrypto::NZipStrong::CDecoder *_pkAesDecoderSpec;
|
rlm@1
|
416 NCrypto::NWzAes::CDecoder *_wzAesDecoderSpec;
|
rlm@1
|
417
|
rlm@1
|
418 CMyComPtr<ICompressFilter> _zipCryptoDecoder;
|
rlm@1
|
419 CMyComPtr<ICompressFilter> _pkAesDecoder;
|
rlm@1
|
420 CMyComPtr<ICompressFilter> _wzAesDecoder;
|
rlm@1
|
421
|
rlm@1
|
422 CFilterCoder *filterStreamSpec;
|
rlm@1
|
423 CMyComPtr<ISequentialInStream> filterStream;
|
rlm@1
|
424 CMyComPtr<ICryptoGetTextPassword> getTextPassword;
|
rlm@1
|
425 CObjectVector<CMethodItem> methodItems;
|
rlm@1
|
426
|
rlm@1
|
427 public:
|
rlm@1
|
428 CZipDecoder():
|
rlm@1
|
429 _zipCryptoDecoderSpec(0),
|
rlm@1
|
430 _pkAesDecoderSpec(0),
|
rlm@1
|
431 _wzAesDecoderSpec(0),
|
rlm@1
|
432 filterStreamSpec(0) {}
|
rlm@1
|
433
|
rlm@1
|
434 HRESULT Decode(
|
rlm@1
|
435 DECL_EXTERNAL_CODECS_LOC_VARS
|
rlm@1
|
436 CInArchive &archive, const CItemEx &item,
|
rlm@1
|
437 ISequentialOutStream *realOutStream,
|
rlm@1
|
438 IArchiveExtractCallback *extractCallback,
|
rlm@1
|
439 ICompressProgressInfo *compressProgress,
|
rlm@1
|
440 UInt32 numThreads, Int32 &res);
|
rlm@1
|
441 };
|
rlm@1
|
442
|
rlm@1
|
443 HRESULT CZipDecoder::Decode(
|
rlm@1
|
444 DECL_EXTERNAL_CODECS_LOC_VARS
|
rlm@1
|
445 CInArchive &archive, const CItemEx &item,
|
rlm@1
|
446 ISequentialOutStream *realOutStream,
|
rlm@1
|
447 IArchiveExtractCallback *extractCallback,
|
rlm@1
|
448 ICompressProgressInfo *compressProgress,
|
rlm@1
|
449 UInt32 numThreads, Int32 &res)
|
rlm@1
|
450 {
|
rlm@1
|
451 res = NArchive::NExtract::NOperationResult::kDataError;
|
rlm@1
|
452 CInStreamReleaser inStreamReleaser;
|
rlm@1
|
453
|
rlm@1
|
454 bool needCRC = true;
|
rlm@1
|
455 bool wzAesMode = false;
|
rlm@1
|
456 bool pkAesMode = false;
|
rlm@1
|
457 UInt16 methodId = item.CompressionMethod;
|
rlm@1
|
458 if (item.IsEncrypted())
|
rlm@1
|
459 {
|
rlm@1
|
460 if (item.IsStrongEncrypted())
|
rlm@1
|
461 {
|
rlm@1
|
462 CStrongCryptoField f;
|
rlm@1
|
463 if (item.CentralExtra.GetStrongCryptoField(f))
|
rlm@1
|
464 {
|
rlm@1
|
465 pkAesMode = true;
|
rlm@1
|
466 }
|
rlm@1
|
467 if (!pkAesMode)
|
rlm@1
|
468 {
|
rlm@1
|
469 res = NArchive::NExtract::NOperationResult::kUnSupportedMethod;
|
rlm@1
|
470 return S_OK;
|
rlm@1
|
471 }
|
rlm@1
|
472 }
|
rlm@1
|
473 if (methodId == NFileHeader::NCompressionMethod::kWzAES)
|
rlm@1
|
474 {
|
rlm@1
|
475 CWzAesExtraField aesField;
|
rlm@1
|
476 if (item.CentralExtra.GetWzAesField(aesField))
|
rlm@1
|
477 {
|
rlm@1
|
478 wzAesMode = true;
|
rlm@1
|
479 needCRC = aesField.NeedCrc();
|
rlm@1
|
480 }
|
rlm@1
|
481 }
|
rlm@1
|
482 }
|
rlm@1
|
483
|
rlm@1
|
484 COutStreamWithCRC *outStreamSpec = new COutStreamWithCRC;
|
rlm@1
|
485 CMyComPtr<ISequentialOutStream> outStream = outStreamSpec;
|
rlm@1
|
486 outStreamSpec->SetStream(realOutStream);
|
rlm@1
|
487 outStreamSpec->Init(needCRC);
|
rlm@1
|
488
|
rlm@1
|
489 UInt64 authenticationPos;
|
rlm@1
|
490
|
rlm@1
|
491 CMyComPtr<ISequentialInStream> inStream;
|
rlm@1
|
492 {
|
rlm@1
|
493 UInt64 packSize = item.PackSize;
|
rlm@1
|
494 if (wzAesMode)
|
rlm@1
|
495 {
|
rlm@1
|
496 if (packSize < NCrypto::NWzAes::kMacSize)
|
rlm@1
|
497 return S_OK;
|
rlm@1
|
498 packSize -= NCrypto::NWzAes::kMacSize;
|
rlm@1
|
499 }
|
rlm@1
|
500 UInt64 dataPos = item.GetDataPosition();
|
rlm@1
|
501 inStream.Attach(archive.CreateLimitedStream(dataPos, packSize));
|
rlm@1
|
502 authenticationPos = dataPos + packSize;
|
rlm@1
|
503 }
|
rlm@1
|
504
|
rlm@1
|
505 CMyComPtr<ICompressFilter> cryptoFilter;
|
rlm@1
|
506 if (item.IsEncrypted())
|
rlm@1
|
507 {
|
rlm@1
|
508 if (wzAesMode)
|
rlm@1
|
509 {
|
rlm@1
|
510 CWzAesExtraField aesField;
|
rlm@1
|
511 if (!item.CentralExtra.GetWzAesField(aesField))
|
rlm@1
|
512 return S_OK;
|
rlm@1
|
513 methodId = aesField.Method;
|
rlm@1
|
514 if (!_wzAesDecoder)
|
rlm@1
|
515 {
|
rlm@1
|
516 _wzAesDecoderSpec = new NCrypto::NWzAes::CDecoder;
|
rlm@1
|
517 _wzAesDecoder = _wzAesDecoderSpec;
|
rlm@1
|
518 }
|
rlm@1
|
519 cryptoFilter = _wzAesDecoder;
|
rlm@1
|
520 Byte properties = aesField.Strength;
|
rlm@1
|
521 RINOK(_wzAesDecoderSpec->SetDecoderProperties2(&properties, 1));
|
rlm@1
|
522 }
|
rlm@1
|
523 else if (pkAesMode)
|
rlm@1
|
524 {
|
rlm@1
|
525 if (!_pkAesDecoder)
|
rlm@1
|
526 {
|
rlm@1
|
527 _pkAesDecoderSpec = new NCrypto::NZipStrong::CDecoder;
|
rlm@1
|
528 _pkAesDecoder = _pkAesDecoderSpec;
|
rlm@1
|
529 }
|
rlm@1
|
530 cryptoFilter = _pkAesDecoder;
|
rlm@1
|
531 }
|
rlm@1
|
532 else
|
rlm@1
|
533 {
|
rlm@1
|
534 if (!_zipCryptoDecoder)
|
rlm@1
|
535 {
|
rlm@1
|
536 _zipCryptoDecoderSpec = new NCrypto::NZip::CDecoder;
|
rlm@1
|
537 _zipCryptoDecoder = _zipCryptoDecoderSpec;
|
rlm@1
|
538 }
|
rlm@1
|
539 cryptoFilter = _zipCryptoDecoder;
|
rlm@1
|
540 }
|
rlm@1
|
541 CMyComPtr<ICryptoSetPassword> cryptoSetPassword;
|
rlm@1
|
542 RINOK(cryptoFilter.QueryInterface(IID_ICryptoSetPassword, &cryptoSetPassword));
|
rlm@1
|
543
|
rlm@1
|
544 if (!getTextPassword)
|
rlm@1
|
545 extractCallback->QueryInterface(IID_ICryptoGetTextPassword, (void **)&getTextPassword);
|
rlm@1
|
546
|
rlm@1
|
547 if (getTextPassword)
|
rlm@1
|
548 {
|
rlm@1
|
549 CMyComBSTR password;
|
rlm@1
|
550 RINOK(getTextPassword->CryptoGetTextPassword(&password));
|
rlm@1
|
551 AString charPassword;
|
rlm@1
|
552 if (wzAesMode || pkAesMode)
|
rlm@1
|
553 {
|
rlm@1
|
554 charPassword = UnicodeStringToMultiByte((const wchar_t *)password, CP_ACP);
|
rlm@1
|
555 /*
|
rlm@1
|
556 for (int i = 0;; i++)
|
rlm@1
|
557 {
|
rlm@1
|
558 wchar_t c = password[i];
|
rlm@1
|
559 if (c == 0)
|
rlm@1
|
560 break;
|
rlm@1
|
561 if (c >= 0x80)
|
rlm@1
|
562 {
|
rlm@1
|
563 res = NArchive::NExtract::NOperationResult::kDataError;
|
rlm@1
|
564 return S_OK;
|
rlm@1
|
565 }
|
rlm@1
|
566 charPassword += (char)c;
|
rlm@1
|
567 }
|
rlm@1
|
568 */
|
rlm@1
|
569 }
|
rlm@1
|
570 else
|
rlm@1
|
571 {
|
rlm@1
|
572 // we use OEM. WinZip/Windows probably use ANSI for some files
|
rlm@1
|
573 charPassword = UnicodeStringToMultiByte((const wchar_t *)password, CP_OEMCP);
|
rlm@1
|
574 }
|
rlm@1
|
575 HRESULT result = cryptoSetPassword->CryptoSetPassword(
|
rlm@1
|
576 (const Byte *)(const char *)charPassword, charPassword.Length());
|
rlm@1
|
577 if (result != S_OK)
|
rlm@1
|
578 return S_OK;
|
rlm@1
|
579 }
|
rlm@1
|
580 else
|
rlm@1
|
581 {
|
rlm@1
|
582 RINOK(cryptoSetPassword->CryptoSetPassword(0, 0));
|
rlm@1
|
583 }
|
rlm@1
|
584 }
|
rlm@1
|
585
|
rlm@1
|
586 int m;
|
rlm@1
|
587 for (m = 0; m < methodItems.Size(); m++)
|
rlm@1
|
588 if (methodItems[m].ZipMethod == methodId)
|
rlm@1
|
589 break;
|
rlm@1
|
590
|
rlm@1
|
591 if (m == methodItems.Size())
|
rlm@1
|
592 {
|
rlm@1
|
593 CMethodItem mi;
|
rlm@1
|
594 mi.ZipMethod = methodId;
|
rlm@1
|
595 if (methodId == NFileHeader::NCompressionMethod::kStored)
|
rlm@1
|
596 mi.Coder = new NCompress::CCopyCoder;
|
rlm@1
|
597 else if (methodId == NFileHeader::NCompressionMethod::kShrunk)
|
rlm@1
|
598 mi.Coder = new NCompress::NShrink::CDecoder;
|
rlm@1
|
599 else if (methodId == NFileHeader::NCompressionMethod::kImploded)
|
rlm@1
|
600 mi.Coder = new NCompress::NImplode::NDecoder::CCoder;
|
rlm@1
|
601 else if (methodId == NFileHeader::NCompressionMethod::kLZMA)
|
rlm@1
|
602 mi.Coder = new CLzmaDecoder;
|
rlm@1
|
603 else
|
rlm@1
|
604 {
|
rlm@1
|
605 CMethodId szMethodID;
|
rlm@1
|
606 if (methodId == NFileHeader::NCompressionMethod::kBZip2)
|
rlm@1
|
607 szMethodID = kMethodId_BZip2;
|
rlm@1
|
608 else
|
rlm@1
|
609 {
|
rlm@1
|
610 if (methodId > 0xFF)
|
rlm@1
|
611 {
|
rlm@1
|
612 res = NArchive::NExtract::NOperationResult::kUnSupportedMethod;
|
rlm@1
|
613 return S_OK;
|
rlm@1
|
614 }
|
rlm@1
|
615 szMethodID = kMethodId_ZipBase + (Byte)methodId;
|
rlm@1
|
616 }
|
rlm@1
|
617
|
rlm@1
|
618 RINOK(CreateCoder(EXTERNAL_CODECS_LOC_VARS szMethodID, mi.Coder, false));
|
rlm@1
|
619
|
rlm@1
|
620 if (mi.Coder == 0)
|
rlm@1
|
621 {
|
rlm@1
|
622 res = NArchive::NExtract::NOperationResult::kUnSupportedMethod;
|
rlm@1
|
623 return S_OK;
|
rlm@1
|
624 }
|
rlm@1
|
625 }
|
rlm@1
|
626 m = methodItems.Add(mi);
|
rlm@1
|
627 }
|
rlm@1
|
628 ICompressCoder *coder = methodItems[m].Coder;
|
rlm@1
|
629
|
rlm@1
|
630 {
|
rlm@1
|
631 CMyComPtr<ICompressSetDecoderProperties2> setDecoderProperties;
|
rlm@1
|
632 coder->QueryInterface(IID_ICompressSetDecoderProperties2, (void **)&setDecoderProperties);
|
rlm@1
|
633 if (setDecoderProperties)
|
rlm@1
|
634 {
|
rlm@1
|
635 Byte properties = (Byte)item.Flags;
|
rlm@1
|
636 RINOK(setDecoderProperties->SetDecoderProperties2(&properties, 1));
|
rlm@1
|
637 }
|
rlm@1
|
638 }
|
rlm@1
|
639
|
rlm@1
|
640 #ifdef COMPRESS_MT
|
rlm@1
|
641 {
|
rlm@1
|
642 CMyComPtr<ICompressSetCoderMt> setCoderMt;
|
rlm@1
|
643 coder->QueryInterface(IID_ICompressSetCoderMt, (void **)&setCoderMt);
|
rlm@1
|
644 if (setCoderMt)
|
rlm@1
|
645 {
|
rlm@1
|
646 RINOK(setCoderMt->SetNumberOfThreads(numThreads));
|
rlm@1
|
647 }
|
rlm@1
|
648 }
|
rlm@1
|
649 #endif
|
rlm@1
|
650
|
rlm@1
|
651 {
|
rlm@1
|
652 HRESULT result = S_OK;
|
rlm@1
|
653 CMyComPtr<ISequentialInStream> inStreamNew;
|
rlm@1
|
654 if (item.IsEncrypted())
|
rlm@1
|
655 {
|
rlm@1
|
656 if (!filterStream)
|
rlm@1
|
657 {
|
rlm@1
|
658 filterStreamSpec = new CFilterCoder;
|
rlm@1
|
659 filterStream = filterStreamSpec;
|
rlm@1
|
660 }
|
rlm@1
|
661 filterStreamSpec->Filter = cryptoFilter;
|
rlm@1
|
662 if (wzAesMode)
|
rlm@1
|
663 {
|
rlm@1
|
664 result = _wzAesDecoderSpec->ReadHeader(inStream);
|
rlm@1
|
665 }
|
rlm@1
|
666 else if (pkAesMode)
|
rlm@1
|
667 {
|
rlm@1
|
668 result =_pkAesDecoderSpec->ReadHeader(inStream, item.FileCRC, item.UnPackSize);
|
rlm@1
|
669 if (result == S_OK)
|
rlm@1
|
670 {
|
rlm@1
|
671 bool passwOK;
|
rlm@1
|
672 result = _pkAesDecoderSpec->CheckPassword(passwOK);
|
rlm@1
|
673 if (result == S_OK && !passwOK)
|
rlm@1
|
674 result = S_FALSE;
|
rlm@1
|
675 }
|
rlm@1
|
676 }
|
rlm@1
|
677 else
|
rlm@1
|
678 {
|
rlm@1
|
679 result = _zipCryptoDecoderSpec->ReadHeader(inStream);
|
rlm@1
|
680 }
|
rlm@1
|
681
|
rlm@1
|
682 if (result == S_OK)
|
rlm@1
|
683 {
|
rlm@1
|
684 RINOK(filterStreamSpec->SetInStream(inStream));
|
rlm@1
|
685 inStreamReleaser.FilterCoder = filterStreamSpec;
|
rlm@1
|
686 inStreamNew = filterStream;
|
rlm@1
|
687 if (wzAesMode)
|
rlm@1
|
688 {
|
rlm@1
|
689 if (!_wzAesDecoderSpec->CheckPasswordVerifyCode())
|
rlm@1
|
690 result = S_FALSE;
|
rlm@1
|
691 }
|
rlm@1
|
692 }
|
rlm@1
|
693 }
|
rlm@1
|
694 else
|
rlm@1
|
695 inStreamNew = inStream;
|
rlm@1
|
696 if (result == S_OK)
|
rlm@1
|
697 result = coder->Code(inStreamNew, outStream, NULL, &item.UnPackSize, compressProgress);
|
rlm@1
|
698 if (result == S_FALSE)
|
rlm@1
|
699 return S_OK;
|
rlm@1
|
700 if (result == E_NOTIMPL)
|
rlm@1
|
701 {
|
rlm@1
|
702 res = NArchive::NExtract::NOperationResult::kUnSupportedMethod;
|
rlm@1
|
703 return S_OK;
|
rlm@1
|
704 }
|
rlm@1
|
705
|
rlm@1
|
706 RINOK(result);
|
rlm@1
|
707 }
|
rlm@1
|
708 bool crcOK = true;
|
rlm@1
|
709 bool authOk = true;
|
rlm@1
|
710 if (needCRC)
|
rlm@1
|
711 crcOK = (outStreamSpec->GetCRC() == item.FileCRC);
|
rlm@1
|
712 if (wzAesMode)
|
rlm@1
|
713 {
|
rlm@1
|
714 inStream.Attach(archive.CreateLimitedStream(authenticationPos, NCrypto::NWzAes::kMacSize));
|
rlm@1
|
715 if (_wzAesDecoderSpec->CheckMac(inStream, authOk) != S_OK)
|
rlm@1
|
716 authOk = false;
|
rlm@1
|
717 }
|
rlm@1
|
718
|
rlm@1
|
719 res = ((crcOK && authOk) ?
|
rlm@1
|
720 NArchive::NExtract::NOperationResult::kOK :
|
rlm@1
|
721 NArchive::NExtract::NOperationResult::kCRCError);
|
rlm@1
|
722 return S_OK;
|
rlm@1
|
723 }
|
rlm@1
|
724
|
rlm@1
|
725
|
rlm@1
|
726 STDMETHODIMP CHandler::Extract(const UInt32* indices, UInt32 numItems,
|
rlm@1
|
727 Int32 _aTestMode, IArchiveExtractCallback *extractCallback)
|
rlm@1
|
728 {
|
rlm@1
|
729 COM_TRY_BEGIN
|
rlm@1
|
730 CZipDecoder myDecoder;
|
rlm@1
|
731 bool testMode = (_aTestMode != 0);
|
rlm@1
|
732 UInt64 totalUnPacked = 0, totalPacked = 0;
|
rlm@1
|
733 bool allFilesMode = (numItems == UInt32(-1));
|
rlm@1
|
734 if (allFilesMode)
|
rlm@1
|
735 numItems = m_Items.Size();
|
rlm@1
|
736 if(numItems == 0)
|
rlm@1
|
737 return S_OK;
|
rlm@1
|
738 UInt32 i;
|
rlm@1
|
739 for(i = 0; i < numItems; i++)
|
rlm@1
|
740 {
|
rlm@1
|
741 const CItemEx &item = m_Items[allFilesMode ? i : indices[i]];
|
rlm@1
|
742 totalUnPacked += item.UnPackSize;
|
rlm@1
|
743 totalPacked += item.PackSize;
|
rlm@1
|
744 }
|
rlm@1
|
745 RINOK(extractCallback->SetTotal(totalUnPacked));
|
rlm@1
|
746
|
rlm@1
|
747 UInt64 currentTotalUnPacked = 0, currentTotalPacked = 0;
|
rlm@1
|
748 UInt64 currentItemUnPacked, currentItemPacked;
|
rlm@1
|
749
|
rlm@1
|
750 CLocalProgress *lps = new CLocalProgress;
|
rlm@1
|
751 CMyComPtr<ICompressProgressInfo> progress = lps;
|
rlm@1
|
752 lps->Init(extractCallback, false);
|
rlm@1
|
753
|
rlm@1
|
754 for (i = 0; i < numItems; i++, currentTotalUnPacked += currentItemUnPacked,
|
rlm@1
|
755 currentTotalPacked += currentItemPacked)
|
rlm@1
|
756 {
|
rlm@1
|
757 currentItemUnPacked = 0;
|
rlm@1
|
758 currentItemPacked = 0;
|
rlm@1
|
759
|
rlm@1
|
760 lps->InSize = currentTotalPacked;
|
rlm@1
|
761 lps->OutSize = currentTotalUnPacked;
|
rlm@1
|
762 RINOK(lps->SetCur());
|
rlm@1
|
763
|
rlm@1
|
764 CMyComPtr<ISequentialOutStream> realOutStream;
|
rlm@1
|
765 Int32 askMode = testMode ?
|
rlm@1
|
766 NArchive::NExtract::NAskMode::kTest :
|
rlm@1
|
767 NArchive::NExtract::NAskMode::kExtract;
|
rlm@1
|
768 Int32 index = allFilesMode ? i : indices[i];
|
rlm@1
|
769
|
rlm@1
|
770 RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
|
rlm@1
|
771
|
rlm@1
|
772 CItemEx item = m_Items[index];
|
rlm@1
|
773 if (!item.FromLocal)
|
rlm@1
|
774 {
|
rlm@1
|
775 HRESULT res = m_Archive.ReadLocalItemAfterCdItem(item);
|
rlm@1
|
776 if (res == S_FALSE)
|
rlm@1
|
777 {
|
rlm@1
|
778 if (item.IsDir() || realOutStream || testMode)
|
rlm@1
|
779 {
|
rlm@1
|
780 RINOK(extractCallback->PrepareOperation(askMode));
|
rlm@1
|
781 realOutStream.Release();
|
rlm@1
|
782 RINOK(extractCallback->SetOperationResult(NArchive::NExtract::NOperationResult::kUnSupportedMethod));
|
rlm@1
|
783 }
|
rlm@1
|
784 continue;
|
rlm@1
|
785 }
|
rlm@1
|
786 RINOK(res);
|
rlm@1
|
787 }
|
rlm@1
|
788
|
rlm@1
|
789 if (item.IsDir() || item.IgnoreItem())
|
rlm@1
|
790 {
|
rlm@1
|
791 // if (!testMode)
|
rlm@1
|
792 {
|
rlm@1
|
793 RINOK(extractCallback->PrepareOperation(askMode));
|
rlm@1
|
794 realOutStream.Release();
|
rlm@1
|
795 RINOK(extractCallback->SetOperationResult(NArchive::NExtract::NOperationResult::kOK));
|
rlm@1
|
796 }
|
rlm@1
|
797 continue;
|
rlm@1
|
798 }
|
rlm@1
|
799
|
rlm@1
|
800 currentItemUnPacked = item.UnPackSize;
|
rlm@1
|
801 currentItemPacked = item.PackSize;
|
rlm@1
|
802
|
rlm@1
|
803 if (!testMode && (!realOutStream))
|
rlm@1
|
804 continue;
|
rlm@1
|
805
|
rlm@1
|
806 RINOK(extractCallback->PrepareOperation(askMode));
|
rlm@1
|
807
|
rlm@1
|
808 #ifndef COMPRESS_MT
|
rlm@1
|
809 #define _numThreads 1
|
rlm@1
|
810 #endif
|
rlm@1
|
811
|
rlm@1
|
812 Int32 res;
|
rlm@1
|
813 RINOK(myDecoder.Decode(
|
rlm@1
|
814 EXTERNAL_CODECS_VARS
|
rlm@1
|
815 m_Archive, item, realOutStream, extractCallback,
|
rlm@1
|
816 progress,
|
rlm@1
|
817 _numThreads,
|
rlm@1
|
818 res));
|
rlm@1
|
819 realOutStream.Release();
|
rlm@1
|
820
|
rlm@1
|
821 RINOK(extractCallback->SetOperationResult(res))
|
rlm@1
|
822 }
|
rlm@1
|
823 return S_OK;
|
rlm@1
|
824 COM_TRY_END
|
rlm@1
|
825 }
|
rlm@1
|
826
|
rlm@1
|
827 #ifndef EXTRACT_ONLY
|
rlm@1
|
828 IMPL_ISetCompressCodecsInfo
|
rlm@1
|
829 #endif
|
rlm@1
|
830
|
rlm@1
|
831 }}
|