5 #include "Common/ComTry.h"
\r
6 #include "Common/StringConvert.h"
\r
7 #include "Common/StringToInt.h"
\r
9 #include "Windows/PropVariant.h"
\r
10 #include "Windows/Time.h"
\r
12 #include "../../IPassword.h"
\r
14 #include "../../Common/OutBuffer.h"
\r
16 #include "../../Crypto/WzAes.h"
\r
18 #include "../Common/ItemNameUtils.h"
\r
19 #include "../Common/ParseProperties.h"
\r
21 #include "ZipHandler.h"
\r
22 #include "ZipUpdate.h"
\r
24 using namespace NWindows;
\r
25 using namespace NCOM;
\r
26 using namespace NTime;
\r
28 namespace NArchive {
\r
31 static const UInt32 kLzAlgoX1 = 0;
\r
32 static const UInt32 kLzAlgoX5 = 1;
\r
34 static const UInt32 kDeflateNumPassesX1 = 1;
\r
35 static const UInt32 kDeflateNumPassesX7 = 3;
\r
36 static const UInt32 kDeflateNumPassesX9 = 10;
\r
38 static const UInt32 kDeflateNumFastBytesX1 = 32;
\r
39 static const UInt32 kDeflateNumFastBytesX7 = 64;
\r
40 static const UInt32 kDeflateNumFastBytesX9 = 128;
\r
42 static const wchar_t *kLzmaMatchFinderX1 = L"HC4";
\r
43 static const wchar_t *kLzmaMatchFinderX5 = L"BT4";
\r
45 static const UInt32 kLzmaNumFastBytesX1 = 32;
\r
46 static const UInt32 kLzmaNumFastBytesX7 = 64;
\r
48 static const UInt32 kLzmaDicSizeX1 = 1 << 16;
\r
49 static const UInt32 kLzmaDicSizeX3 = 1 << 20;
\r
50 static const UInt32 kLzmaDicSizeX5 = 1 << 24;
\r
51 static const UInt32 kLzmaDicSizeX7 = 1 << 25;
\r
52 static const UInt32 kLzmaDicSizeX9 = 1 << 26;
\r
54 static const UInt32 kBZip2NumPassesX1 = 1;
\r
55 static const UInt32 kBZip2NumPassesX7 = 2;
\r
56 static const UInt32 kBZip2NumPassesX9 = 7;
\r
58 static const UInt32 kBZip2DicSizeX1 = 100000;
\r
59 static const UInt32 kBZip2DicSizeX3 = 500000;
\r
60 static const UInt32 kBZip2DicSizeX5 = 900000;
\r
62 STDMETHODIMP CHandler::GetFileTimeType(UInt32 *timeType)
\r
64 *timeType = NFileTimeType::kDOS;
\r
68 static bool IsAsciiString(const UString &s)
\r
70 for (int i = 0; i < s.Length(); i++)
\r
73 if (c < 0x20 || c > 0x7F)
\r
79 #define COM_TRY_BEGIN2 try {
\r
80 #define COM_TRY_END2 } \
\r
81 catch(const CSystemException &e) { return e.ErrorCode; } \
\r
82 catch(...) { return E_OUTOFMEMORY; }
\r
84 static HRESULT GetTime(IArchiveUpdateCallback *callback, int index, PROPID propID, FILETIME &filetime)
\r
86 filetime.dwHighDateTime = filetime.dwLowDateTime = 0;
\r
87 NCOM::CPropVariant prop;
\r
88 RINOK(callback->GetProperty(index, propID, &prop));
\r
89 if (prop.vt == VT_FILETIME)
\r
90 filetime = prop.filetime;
\r
91 else if (prop.vt != VT_EMPTY)
\r
92 return E_INVALIDARG;
\r
96 STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems,
\r
97 IArchiveUpdateCallback *callback)
\r
100 CObjectVector<CUpdateItem> updateItems;
\r
101 bool thereAreAesUpdates = false;
\r
102 for (UInt32 i = 0; i < numItems; i++)
\r
106 Int32 newProperties;
\r
107 UInt32 indexInArchive;
\r
110 RINOK(callback->GetUpdateItemInfo(i, &newData, &newProperties, &indexInArchive));
\r
111 ui.NewProperties = IntToBool(newProperties);
\r
112 ui.NewData = IntToBool(newData);
\r
113 ui.IndexInArchive = indexInArchive;
\r
114 ui.IndexInClient = i;
\r
115 bool existInArchive = (indexInArchive != (UInt32)-1);
\r
116 if (existInArchive && newData)
\r
117 if (m_Items[indexInArchive].IsAesEncrypted())
\r
118 thereAreAesUpdates = true;
\r
120 if (IntToBool(newProperties))
\r
124 NCOM::CPropVariant prop;
\r
125 RINOK(callback->GetProperty(i, kpidAttrib, &prop));
\r
126 if (prop.vt == VT_EMPTY)
\r
128 else if (prop.vt != VT_UI4)
\r
129 return E_INVALIDARG;
\r
131 ui.Attributes = prop.ulVal;
\r
135 NCOM::CPropVariant prop;
\r
136 RINOK(callback->GetProperty(i, kpidPath, &prop));
\r
137 if (prop.vt == VT_EMPTY)
\r
139 else if (prop.vt != VT_BSTR)
\r
140 return E_INVALIDARG;
\r
142 name = prop.bstrVal;
\r
145 NCOM::CPropVariant prop;
\r
146 RINOK(callback->GetProperty(i, kpidIsDir, &prop));
\r
147 if (prop.vt == VT_EMPTY)
\r
149 else if (prop.vt != VT_BOOL)
\r
150 return E_INVALIDARG;
\r
152 ui.IsDir = (prop.boolVal != VARIANT_FALSE);
\r
157 RINOK(callback->GetProperty(i, kpidTimeType, &prop));
\r
158 if (prop.vt == VT_UI4)
\r
159 ui.NtfsTimeIsDefined = (prop.ulVal == NFileTimeType::kWindows);
\r
161 ui.NtfsTimeIsDefined = m_WriteNtfsTimeExtra;
\r
163 RINOK(GetTime(callback, i, kpidMTime, ui.NtfsMTime));
\r
164 RINOK(GetTime(callback, i, kpidATime, ui.NtfsATime));
\r
165 RINOK(GetTime(callback, i, kpidCTime, ui.NtfsCTime));
\r
168 FILETIME localFileTime = { 0, 0 };
\r
169 if (ui.NtfsMTime.dwHighDateTime != 0 ||
\r
170 ui.NtfsMTime.dwLowDateTime != 0)
\r
171 if (!FileTimeToLocalFileTime(&ui.NtfsMTime, &localFileTime))
\r
172 return E_INVALIDARG;
\r
173 FileTimeToDosTime(localFileTime, ui.Time);
\r
176 name = NItemName::MakeLegalName(name);
\r
177 bool needSlash = ui.IsDir;
\r
178 const wchar_t kSlash = L'/';
\r
179 if (!name.IsEmpty())
\r
181 if (name[name.Length() - 1] == kSlash)
\r
184 return E_INVALIDARG;
\r
191 bool tryUtf8 = true;
\r
192 if (m_ForceLocal || !m_ForceUtf8)
\r
194 bool defaultCharWasUsed;
\r
195 ui.Name = UnicodeStringToMultiByte(name, CP_OEMCP, '_', defaultCharWasUsed);
\r
196 tryUtf8 = (!m_ForceLocal && (defaultCharWasUsed ||
\r
197 MultiByteToUnicodeString(ui.Name, CP_OEMCP) != name));
\r
203 for (i = 0; i < name.Length() && (unsigned)name[i] < 0x80; i++);
\r
204 ui.IsUtf8 = (i != name.Length());
\r
205 if (!ConvertUnicodeToUTF8(name, ui.Name))
\r
206 return E_INVALIDARG;
\r
209 if (ui.Name.Length() >= (1 << 16))
\r
210 return E_INVALIDARG;
\r
212 ui.IndexInClient = i;
\r
214 if (existInArchive)
\r
216 const CItemEx &itemInfo = m_Items[indexInArchive];
\r
217 // ui.Commented = itemInfo.IsCommented();
\r
218 ui.Commented = false;
\r
221 ui.CommentRange.Position = itemInfo.GetCommentPosition();
\r
222 ui.CommentRange.Size = itemInfo.CommentSize;
\r
226 ui.Commented = false;
\r
229 if (IntToBool(newData))
\r
233 NCOM::CPropVariant prop;
\r
234 RINOK(callback->GetProperty(i, kpidSize, &prop));
\r
235 if (prop.vt != VT_UI8)
\r
236 return E_INVALIDARG;
\r
237 size = prop.uhVal.QuadPart;
\r
241 updateItems.Add(ui);
\r
244 CMyComPtr<ICryptoGetTextPassword2> getTextPassword;
\r
246 CMyComPtr<IArchiveUpdateCallback> udateCallBack2(callback);
\r
247 udateCallBack2.QueryInterface(IID_ICryptoGetTextPassword2, &getTextPassword);
\r
249 CCompressionMethodMode options;
\r
251 if (getTextPassword)
\r
253 CMyComBSTR password;
\r
254 Int32 passwordIsDefined;
\r
255 RINOK(getTextPassword->CryptoGetTextPassword2(&passwordIsDefined, &password));
\r
256 options.PasswordIsDefined = IntToBool(passwordIsDefined);
\r
257 if (options.PasswordIsDefined)
\r
259 options.IsAesMode = (m_ForceAesMode ? m_IsAesMode : thereAreAesUpdates);
\r
260 options.AesKeyMode = m_AesKeyMode;
\r
262 if (!IsAsciiString((const wchar_t *)password))
\r
263 return E_INVALIDARG;
\r
264 if (options.IsAesMode)
\r
266 if (options.Password.Length() > NCrypto::NWzAes::kPasswordSizeMax)
\r
267 return E_INVALIDARG;
\r
269 options.Password = UnicodeStringToMultiByte((const wchar_t *)password, CP_OEMCP);
\r
273 options.PasswordIsDefined = false;
\r
275 int level = m_Level;
\r
280 if (m_MainMethod < 0)
\r
281 mainMethod = (Byte)(((level == 0) ?
\r
282 NFileHeader::NCompressionMethod::kStored :
\r
283 NFileHeader::NCompressionMethod::kDeflated));
\r
285 mainMethod = (Byte)m_MainMethod;
\r
286 options.MethodSequence.Add(mainMethod);
\r
287 if (mainMethod != NFileHeader::NCompressionMethod::kStored)
\r
288 options.MethodSequence.Add(NFileHeader::NCompressionMethod::kStored);
\r
289 bool isDeflate = (mainMethod == NFileHeader::NCompressionMethod::kDeflated) ||
\r
290 (mainMethod == NFileHeader::NCompressionMethod::kDeflated64);
\r
291 bool isLZMA = (mainMethod == NFileHeader::NCompressionMethod::kLZMA);
\r
292 bool isLz = (isLZMA || isDeflate);
\r
293 options.NumPasses = m_NumPasses;
\r
294 options.DicSize = m_DicSize;
\r
295 options.NumFastBytes = m_NumFastBytes;
\r
296 options.NumMatchFinderCycles = m_NumMatchFinderCycles;
\r
297 options.NumMatchFinderCyclesDefined = m_NumMatchFinderCyclesDefined;
\r
298 options.Algo = m_Algo;
\r
299 options.MemSize = m_MemSize;
\r
300 options.Order = m_Order;
\r
302 options.NumThreads = _numThreads;
\r
308 if (options.NumPasses == 0xFFFFFFFF)
\r
309 options.NumPasses = (level >= 9 ? kDeflateNumPassesX9 :
\r
310 (level >= 7 ? kDeflateNumPassesX7 :
\r
311 kDeflateNumPassesX1));
\r
312 if (options.NumFastBytes == 0xFFFFFFFF)
\r
313 options.NumFastBytes = (level >= 9 ? kDeflateNumFastBytesX9 :
\r
314 (level >= 7 ? kDeflateNumFastBytesX7 :
\r
315 kDeflateNumFastBytesX1));
\r
319 if (options.DicSize == 0xFFFFFFFF)
\r
321 (level >= 9 ? kLzmaDicSizeX9 :
\r
322 (level >= 7 ? kLzmaDicSizeX7 :
\r
323 (level >= 5 ? kLzmaDicSizeX5 :
\r
324 (level >= 3 ? kLzmaDicSizeX3 :
\r
325 kLzmaDicSizeX1))));
\r
327 if (options.NumFastBytes == 0xFFFFFFFF)
\r
328 options.NumFastBytes = (level >= 7 ? kLzmaNumFastBytesX7 :
\r
329 kLzmaNumFastBytesX1);
\r
331 options.MatchFinder =
\r
332 (level >= 5 ? kLzmaMatchFinderX5 :
\r
333 kLzmaMatchFinderX1);
\r
336 if (options.Algo == 0xFFFFFFFF)
\r
337 options.Algo = (level >= 5 ? kLzAlgoX5 :
\r
340 if (mainMethod == NFileHeader::NCompressionMethod::kBZip2)
\r
342 if (options.NumPasses == 0xFFFFFFFF)
\r
343 options.NumPasses = (level >= 9 ? kBZip2NumPassesX9 :
\r
344 (level >= 7 ? kBZip2NumPassesX7 :
\r
345 kBZip2NumPassesX1));
\r
346 if (options.DicSize == 0xFFFFFFFF)
\r
347 options.DicSize = (level >= 5 ? kBZip2DicSizeX5 :
\r
348 (level >= 3 ? kBZip2DicSizeX3 :
\r
351 if (mainMethod == NFileHeader::NCompressionMethod::kPPMd)
\r
353 int level2 = level;
\r
354 if (level2 < 1) level2 = 1;
\r
355 if (level2 > 9) level2 = 9;
\r
357 if (options.MemSize == 0xFFFFFFFF)
\r
358 options.MemSize = (1 << (19 + (level2 > 8 ? 8 : level2)));
\r
360 if (options.Order == 0xFFFFFFFF)
\r
361 options.Order = 3 + level2;
\r
363 if (options.Algo == 0xFFFFFFFF)
\r
364 options.Algo = (level2 >= 7 ? 1 : 0);
\r
368 EXTERNAL_CODECS_VARS
\r
369 m_Items, updateItems, outStream,
\r
370 m_Archive.IsOpen() ? &m_Archive : NULL, &options, callback);
\r
374 STDMETHODIMP CHandler::SetProperties(const wchar_t **names, const PROPVARIANT *values, Int32 numProperties)
\r
377 const UInt32 numProcessors = NSystem::GetNumberOfProcessors();
\r
378 _numThreads = numProcessors;
\r
380 InitMethodProperties();
\r
381 for (int i = 0; i < numProperties; i++)
\r
383 UString name = UString(names[i]);
\r
385 if (name.IsEmpty())
\r
386 return E_INVALIDARG;
\r
388 const PROPVARIANT &prop = values[i];
\r
390 if (name[0] == L'X')
\r
393 RINOK(ParsePropValue(name.Mid(1), prop, level));
\r
397 else if (name == L"M")
\r
399 if (prop.vt == VT_BSTR)
\r
401 UString m = prop.bstrVal;
\r
403 if (m == L"COPY") m_MainMethod = NFileHeader::NCompressionMethod::kStored;
\r
404 else if (m == L"DEFLATE") m_MainMethod = NFileHeader::NCompressionMethod::kDeflated;
\r
405 else if (m == L"DEFLATE64") m_MainMethod = NFileHeader::NCompressionMethod::kDeflated64;
\r
406 else if (m == L"BZIP2") m_MainMethod = NFileHeader::NCompressionMethod::kBZip2;
\r
407 else if (m == L"LZMA") m_MainMethod = NFileHeader::NCompressionMethod::kLZMA;
\r
408 else if (m == L"PPMD") m_MainMethod = NFileHeader::NCompressionMethod::kPPMd;
\r
409 else return E_INVALIDARG;
\r
411 else if (prop.vt == VT_UI4)
\r
415 case NFileHeader::NCompressionMethod::kStored:
\r
416 case NFileHeader::NCompressionMethod::kDeflated:
\r
417 case NFileHeader::NCompressionMethod::kDeflated64:
\r
418 case NFileHeader::NCompressionMethod::kBZip2:
\r
419 case NFileHeader::NCompressionMethod::kLZMA:
\r
420 m_MainMethod = (Byte)prop.ulVal;
\r
423 return E_INVALIDARG;
\r
427 return E_INVALIDARG;
\r
429 else if (name.Left(2) == L"EM")
\r
431 if (prop.vt == VT_BSTR)
\r
433 UString valueString = prop.bstrVal;
\r
434 valueString.MakeUpper();
\r
435 if (valueString.Left(3) == L"AES")
\r
437 valueString = valueString.Mid(3);
\r
438 if (valueString == L"128")
\r
440 else if (valueString == L"192")
\r
442 else if (valueString == L"256" || valueString.IsEmpty())
\r
445 return E_INVALIDARG;
\r
446 m_IsAesMode = true;
\r
447 m_ForceAesMode = true;
\r
449 else if (valueString == L"ZIPCRYPTO")
\r
451 m_IsAesMode = false;
\r
452 m_ForceAesMode = true;
\r
455 return E_INVALIDARG;
\r
458 return E_INVALIDARG;
\r
460 else if (name[0] == L'D')
\r
462 UInt32 dicSize = kBZip2DicSizeX5;
\r
463 RINOK(ParsePropDictionaryValue(name.Mid(1), prop, dicSize));
\r
464 m_DicSize = dicSize;
\r
466 else if (name.Left(3) == L"MEM")
\r
468 UInt32 memSize = 1 << 24;
\r
469 RINOK(ParsePropDictionaryValue(name.Mid(3), prop, memSize));
\r
470 m_MemSize = memSize;
\r
472 else if (name[0] == L'O')
\r
475 RINOK(ParsePropValue(name.Mid(1), prop, order));
\r
478 else if (name.Left(4) == L"PASS")
\r
480 UInt32 num = kDeflateNumPassesX9;
\r
481 RINOK(ParsePropValue(name.Mid(4), prop, num));
\r
484 else if (name.Left(2) == L"FB")
\r
486 UInt32 num = kDeflateNumFastBytesX9;
\r
487 RINOK(ParsePropValue(name.Mid(2), prop, num));
\r
488 m_NumFastBytes = num;
\r
490 else if (name.Left(2) == L"MC")
\r
492 UInt32 num = 0xFFFFFFFF;
\r
493 RINOK(ParsePropValue(name.Mid(2), prop, num));
\r
494 m_NumMatchFinderCycles = num;
\r
495 m_NumMatchFinderCyclesDefined = true;
\r
497 else if (name.Left(2) == L"MT")
\r
500 RINOK(ParseMtProp(name.Mid(2), prop, numProcessors, _numThreads));
\r
503 else if (name.Left(1) == L"A")
\r
505 UInt32 num = kLzAlgoX5;
\r
506 RINOK(ParsePropValue(name.Mid(1), prop, num));
\r
509 else if (name.CompareNoCase(L"TC") == 0)
\r
511 RINOK(SetBoolProperty(m_WriteNtfsTimeExtra, prop));
\r
513 else if (name.CompareNoCase(L"CL") == 0)
\r
515 RINOK(SetBoolProperty(m_ForceLocal, prop));
\r
517 m_ForceUtf8 = false;
\r
519 else if (name.CompareNoCase(L"CU") == 0)
\r
521 RINOK(SetBoolProperty(m_ForceUtf8, prop));
\r
523 m_ForceLocal = false;
\r
526 return E_INVALIDARG;
\r