5 #include "ZipHandler.h"
8 #include "Common/StringConvert.h"
9 #include "Common/ComTry.h"
10 #include "Common/StringToInt.h"
12 #include "Windows/PropVariant.h"
13 #include "Windows/Time.h"
15 #include "../../IPassword.h"
16 #include "../Common/ItemNameUtils.h"
17 #include "../Common/ParseProperties.h"
18 #include "../../Crypto/WzAES/WzAES.h"
19 #include "../../Common/OutBuffer.h"
21 using namespace NWindows;
23 using namespace NTime;
28 static const UInt32 kDeflateAlgoX1 = 0;
29 static const UInt32 kDeflateAlgoX5 = 1;
31 static const UInt32 kDeflateNumPassesX1 = 1;
32 static const UInt32 kDeflateNumPassesX7 = 3;
33 static const UInt32 kDeflateNumPassesX9 = 10;
35 static const UInt32 kNumFastBytesX1 = 32;
36 static const UInt32 kNumFastBytesX7 = 64;
37 static const UInt32 kNumFastBytesX9 = 128;
39 static const UInt32 kBZip2NumPassesX1 = 1;
40 static const UInt32 kBZip2NumPassesX7 = 2;
41 static const UInt32 kBZip2NumPassesX9 = 7;
43 static const UInt32 kBZip2DicSizeX1 = 100000;
44 static const UInt32 kBZip2DicSizeX3 = 500000;
45 static const UInt32 kBZip2DicSizeX5 = 900000;
47 STDMETHODIMP CHandler::GetFileTimeType(UInt32 *timeType)
49 *timeType = NFileTimeType::kDOS;
53 static bool IsAsciiString(const UString &s)
55 for (int i = 0; i < s.Length(); i++)
58 if (c < 0x20 || c > 0x7F)
64 #define COM_TRY_BEGIN2 try {
65 #define COM_TRY_END2 } \
66 catch(const CSystemException &e) { return e.ErrorCode; } \
67 catch(...) { return E_OUTOFMEMORY; }
69 STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems,
70 IArchiveUpdateCallback *updateCallback)
73 CObjectVector<CUpdateItem> updateItems;
74 for(UInt32 i = 0; i < numItems; i++)
76 CUpdateItem updateItem;
79 UInt32 indexInArchive;
82 RINOK(updateCallback->GetUpdateItemInfo(i,
83 &newData, // 1 - compress 0 - copy
86 updateItem.NewProperties = IntToBool(newProperties);
87 updateItem.NewData = IntToBool(newData);
88 updateItem.IndexInArchive = indexInArchive;
89 updateItem.IndexInClient = i;
90 // bool existInArchive = (indexInArchive != UInt32(-1));
91 if (IntToBool(newProperties))
95 bool isDirectoryStatusDefined;
97 NCOM::CPropVariant propVariant;
98 RINOK(updateCallback->GetProperty(i, kpidAttributes, &propVariant));
99 if (propVariant.vt == VT_EMPTY)
100 updateItem.Attributes = 0;
101 else if (propVariant.vt != VT_UI4)
104 updateItem.Attributes = propVariant.ulVal;
107 NCOM::CPropVariant propVariant;
108 RINOK(updateCallback->GetProperty(i, kpidLastWriteTime, &propVariant));
109 if (propVariant.vt != VT_FILETIME)
111 utcFileTime = propVariant.filetime;
114 NCOM::CPropVariant propVariant;
115 RINOK(updateCallback->GetProperty(i, kpidPath, &propVariant));
116 if (propVariant.vt == VT_EMPTY)
118 else if (propVariant.vt != VT_BSTR)
121 name = propVariant.bstrVal;
124 NCOM::CPropVariant propVariant;
125 RINOK(updateCallback->GetProperty(i, kpidIsFolder, &propVariant));
126 if (propVariant.vt == VT_EMPTY)
127 isDirectoryStatusDefined = false;
128 else if (propVariant.vt != VT_BOOL)
132 updateItem.IsDirectory = (propVariant.boolVal != VARIANT_FALSE);
133 isDirectoryStatusDefined = true;
136 FILETIME localFileTime;
137 if(!FileTimeToLocalFileTime(&utcFileTime, &localFileTime))
139 if(!FileTimeToDosTime(localFileTime, updateItem.Time))
141 // return E_INVALIDARG;
144 if (!isDirectoryStatusDefined)
145 updateItem.IsDirectory = ((updateItem.Attributes & FILE_ATTRIBUTE_DIRECTORY) != 0);
147 name = NItemName::MakeLegalName(name);
148 bool needSlash = updateItem.IsDirectory;
149 const wchar_t kSlash = L'/';
152 if (name[name.Length() - 1] == kSlash)
154 if (!updateItem.IsDirectory)
161 updateItem.Name = UnicodeStringToMultiByte(name, CP_OEMCP);
162 if (updateItem.Name.Length() > 0xFFFF)
165 updateItem.IndexInClient = i;
169 const CItemEx &itemInfo = m_Items[indexInArchive];
170 // updateItem.Commented = itemInfo.IsCommented();
171 updateItem.Commented = false;
172 if(updateItem.Commented)
174 updateItem.CommentRange.Position = itemInfo.GetCommentPosition();
175 updateItem.CommentRange.Size = itemInfo.CommentSize;
179 updateItem.Commented = false;
182 if (IntToBool(newData))
186 NCOM::CPropVariant propVariant;
187 RINOK(updateCallback->GetProperty(i, kpidSize, &propVariant));
188 if (propVariant.vt != VT_UI8)
190 size = propVariant.uhVal.QuadPart;
192 updateItem.Size = size;
194 updateItems.Add(updateItem);
197 CMyComPtr<ICryptoGetTextPassword2> getTextPassword;
198 if (!getTextPassword)
200 CMyComPtr<IArchiveUpdateCallback> udateCallBack2(updateCallback);
201 udateCallBack2.QueryInterface(IID_ICryptoGetTextPassword2, &getTextPassword);
203 CCompressionMethodMode options;
208 Int32 passwordIsDefined;
209 RINOK(getTextPassword->CryptoGetTextPassword2(&passwordIsDefined, &password));
210 options.PasswordIsDefined = IntToBool(passwordIsDefined);
211 if (options.PasswordIsDefined)
213 if (!IsAsciiString((const wchar_t *)password))
217 if (options.Password.Length() > NCrypto::NWzAES::kPasswordSizeMax)
220 options.Password = UnicodeStringToMultiByte((const wchar_t *)password, CP_OEMCP);
221 options.IsAesMode = m_IsAesMode;
222 options.AesKeyMode = m_AesKeyMode;
226 options.PasswordIsDefined = false;
233 if (m_MainMethod < 0)
234 mainMethod = (Byte)(((level == 0) ?
235 NFileHeader::NCompressionMethod::kStored :
236 NFileHeader::NCompressionMethod::kDeflated));
238 mainMethod = (Byte)m_MainMethod;
239 options.MethodSequence.Add(mainMethod);
240 if (mainMethod != NFileHeader::NCompressionMethod::kStored)
241 options.MethodSequence.Add(NFileHeader::NCompressionMethod::kStored);
242 bool isDeflate = (mainMethod == NFileHeader::NCompressionMethod::kDeflated) ||
243 (mainMethod == NFileHeader::NCompressionMethod::kDeflated64);
244 bool isBZip2 = (mainMethod == NFileHeader::NCompressionMethod::kBZip2);
245 options.NumPasses = m_NumPasses;
246 options.DicSize = m_DicSize;
247 options.NumFastBytes = m_NumFastBytes;
248 options.NumMatchFinderCycles = m_NumMatchFinderCycles;
249 options.NumMatchFinderCyclesDefined = m_NumMatchFinderCyclesDefined;
250 options.Algo = m_Algo;
252 options.NumThreads = _numThreads;
256 if (options.NumPasses == 0xFFFFFFFF)
257 options.NumPasses = (level >= 9 ? kDeflateNumPassesX9 :
258 (level >= 7 ? kDeflateNumPassesX7 :
259 kDeflateNumPassesX1));
260 if (options.NumFastBytes == 0xFFFFFFFF)
261 options.NumFastBytes = (level >= 9 ? kNumFastBytesX9 :
262 (level >= 7 ? kNumFastBytesX7 :
264 if (options.Algo == 0xFFFFFFFF)
266 (level >= 5 ? kDeflateAlgoX5 :
271 if (options.NumPasses == 0xFFFFFFFF)
272 options.NumPasses = (level >= 9 ? kBZip2NumPassesX9 :
273 (level >= 7 ? kBZip2NumPassesX7 :
275 if (options.DicSize == 0xFFFFFFFF)
276 options.DicSize = (level >= 5 ? kBZip2DicSizeX5 :
277 (level >= 3 ? kBZip2DicSizeX3 :
283 m_Items, updateItems, outStream,
284 m_ArchiveIsOpen ? &m_Archive : NULL, &options, updateCallback);
288 STDMETHODIMP CHandler::SetProperties(const wchar_t **names, const PROPVARIANT *values, Int32 numProperties)
291 const UInt32 numProcessors = NSystem::GetNumberOfProcessors();
292 _numThreads = numProcessors;
294 InitMethodProperties();
295 for (int i = 0; i < numProperties; i++)
297 UString name = UString(names[i]);
302 const PROPVARIANT &prop = values[i];
307 RINOK(ParsePropValue(name.Mid(1), prop, level));
311 else if (name == L"M")
313 if (prop.vt == VT_BSTR)
315 UString valueString = prop.bstrVal;
316 valueString.MakeUpper();
317 if (valueString == L"COPY")
318 m_MainMethod = NFileHeader::NCompressionMethod::kStored;
319 else if (valueString == L"DEFLATE")
320 m_MainMethod = NFileHeader::NCompressionMethod::kDeflated;
321 else if (valueString == L"DEFLATE64")
322 m_MainMethod = NFileHeader::NCompressionMethod::kDeflated64;
323 else if (valueString == L"BZIP2")
324 m_MainMethod = NFileHeader::NCompressionMethod::kBZip2;
328 else if (prop.vt == VT_UI4)
332 case NFileHeader::NCompressionMethod::kStored:
333 case NFileHeader::NCompressionMethod::kDeflated:
334 case NFileHeader::NCompressionMethod::kDeflated64:
335 case NFileHeader::NCompressionMethod::kBZip2:
336 m_MainMethod = (Byte)prop.ulVal;
345 else if (name.Left(2) == L"EM")
347 if (prop.vt == VT_BSTR)
349 UString valueString = prop.bstrVal;
350 valueString.MakeUpper();
351 if (valueString.Left(3) == L"AES")
353 valueString = valueString.Mid(3);
354 if (valueString == L"128")
356 else if (valueString == L"192")
358 else if (valueString == L"256" || valueString.IsEmpty())
364 else if (valueString == L"ZIPCRYPTO")
372 else if (name[0] == L'D')
374 UInt32 dicSize = kBZip2DicSizeX5;
375 RINOK(ParsePropDictionaryValue(name.Mid(1), prop, dicSize));
378 else if (name.Left(4) == L"PASS")
380 UInt32 num = kDeflateNumPassesX9;
381 RINOK(ParsePropValue(name.Mid(4), prop, num));
384 else if (name.Left(2) == L"FB")
386 UInt32 num = kNumFastBytesX9;
387 RINOK(ParsePropValue(name.Mid(2), prop, num));
388 m_NumFastBytes = num;
390 else if (name.Left(2) == L"MC")
392 UInt32 num = 0xFFFFFFFF;
393 RINOK(ParsePropValue(name.Mid(2), prop, num));
394 m_NumMatchFinderCycles = num;
395 m_NumMatchFinderCyclesDefined = true;
397 else if (name.Left(2) == L"MT")
400 RINOK(ParseMtProp(name.Mid(2), prop, numProcessors, _numThreads));
403 else if (name.Left(1) == L"A")
405 UInt32 num = kDeflateAlgoX5;
406 RINOK(ParsePropValue(name.Mid(1), prop, num));