|
1 // 7zHandlerOut.cpp |
|
2 |
|
3 #include "StdAfx.h" |
|
4 |
|
5 #include "7zHandler.h" |
|
6 #include "7zOut.h" |
|
7 #include "7zUpdate.h" |
|
8 |
|
9 #include "../../../Windows/PropVariant.h" |
|
10 |
|
11 #include "../../../Common/ComTry.h" |
|
12 #include "../../../Common/StringToInt.h" |
|
13 #include "../../IPassword.h" |
|
14 #include "../../ICoder.h" |
|
15 |
|
16 #include "../Common/ItemNameUtils.h" |
|
17 #include "../Common/ParseProperties.h" |
|
18 |
|
19 using namespace NWindows; |
|
20 |
|
21 namespace NArchive { |
|
22 namespace N7z { |
|
23 |
|
24 static const wchar_t *kLZMAMethodName = L"LZMA"; |
|
25 static const wchar_t *kCopyMethod = L"Copy"; |
|
26 static const wchar_t *kDefaultMethodName = kLZMAMethodName; |
|
27 |
|
28 static const UInt32 kLzmaAlgorithmX5 = 1; |
|
29 static const wchar_t *kLzmaMatchFinderForHeaders = L"BT2"; |
|
30 static const UInt32 kDictionaryForHeaders = 1 << 20; |
|
31 static const UInt32 kNumFastBytesForHeaders = 273; |
|
32 static const UInt32 kAlgorithmForHeaders = kLzmaAlgorithmX5; |
|
33 |
|
34 static inline bool IsCopyMethod(const UString &methodName) |
|
35 { return (methodName.CompareNoCase(kCopyMethod) == 0); } |
|
36 |
|
37 STDMETHODIMP CHandler::GetFileTimeType(UInt32 *type) |
|
38 { |
|
39 *type = NFileTimeType::kWindows; |
|
40 return S_OK; |
|
41 } |
|
42 |
|
43 HRESULT CHandler::SetPassword(CCompressionMethodMode &methodMode, |
|
44 IArchiveUpdateCallback *updateCallback) |
|
45 { |
|
46 CMyComPtr<ICryptoGetTextPassword2> getTextPassword; |
|
47 if (!getTextPassword) |
|
48 { |
|
49 CMyComPtr<IArchiveUpdateCallback> udateCallback2(updateCallback); |
|
50 udateCallback2.QueryInterface(IID_ICryptoGetTextPassword2, &getTextPassword); |
|
51 } |
|
52 |
|
53 if (getTextPassword) |
|
54 { |
|
55 CMyComBSTR password; |
|
56 Int32 passwordIsDefined; |
|
57 RINOK(getTextPassword->CryptoGetTextPassword2( |
|
58 &passwordIsDefined, &password)); |
|
59 methodMode.PasswordIsDefined = IntToBool(passwordIsDefined); |
|
60 if (methodMode.PasswordIsDefined) |
|
61 methodMode.Password = password; |
|
62 } |
|
63 else |
|
64 methodMode.PasswordIsDefined = false; |
|
65 return S_OK; |
|
66 } |
|
67 |
|
68 HRESULT CHandler::SetCompressionMethod( |
|
69 CCompressionMethodMode &methodMode, |
|
70 CCompressionMethodMode &headerMethod) |
|
71 { |
|
72 HRESULT res = SetCompressionMethod(methodMode, _methods |
|
73 #ifdef COMPRESS_MT |
|
74 , _numThreads |
|
75 #endif |
|
76 ); |
|
77 RINOK(res); |
|
78 methodMode.Binds = _binds; |
|
79 |
|
80 if (_compressHeaders) |
|
81 { |
|
82 // headerMethod.Methods.Add(methodMode.Methods.Back()); |
|
83 |
|
84 CObjectVector<COneMethodInfo> headerMethodInfoVector; |
|
85 COneMethodInfo oneMethodInfo; |
|
86 oneMethodInfo.MethodName = kLZMAMethodName; |
|
87 { |
|
88 CProp property; |
|
89 property.Id = NCoderPropID::kMatchFinder; |
|
90 property.Value = kLzmaMatchFinderForHeaders; |
|
91 oneMethodInfo.Properties.Add(property); |
|
92 } |
|
93 { |
|
94 CProp property; |
|
95 property.Id = NCoderPropID::kAlgorithm; |
|
96 property.Value = kAlgorithmForHeaders; |
|
97 oneMethodInfo.Properties.Add(property); |
|
98 } |
|
99 { |
|
100 CProp property; |
|
101 property.Id = NCoderPropID::kNumFastBytes; |
|
102 property.Value = UInt32(kNumFastBytesForHeaders); |
|
103 oneMethodInfo.Properties.Add(property); |
|
104 } |
|
105 { |
|
106 CProp property; |
|
107 property.Id = NCoderPropID::kDictionarySize; |
|
108 property.Value = UInt32(kDictionaryForHeaders); |
|
109 oneMethodInfo.Properties.Add(property); |
|
110 } |
|
111 headerMethodInfoVector.Add(oneMethodInfo); |
|
112 HRESULT res = SetCompressionMethod(headerMethod, headerMethodInfoVector |
|
113 #ifdef COMPRESS_MT |
|
114 ,1 |
|
115 #endif |
|
116 ); |
|
117 RINOK(res); |
|
118 } |
|
119 return S_OK; |
|
120 } |
|
121 |
|
122 HRESULT CHandler::SetCompressionMethod( |
|
123 CCompressionMethodMode &methodMode, |
|
124 CObjectVector<COneMethodInfo> &methodsInfo |
|
125 #ifdef COMPRESS_MT |
|
126 , UInt32 numThreads |
|
127 #endif |
|
128 ) |
|
129 { |
|
130 UInt32 level = _level; |
|
131 |
|
132 if (methodsInfo.IsEmpty()) |
|
133 { |
|
134 COneMethodInfo oneMethodInfo; |
|
135 oneMethodInfo.MethodName = ((level == 0) ? kCopyMethod : kDefaultMethodName); |
|
136 methodsInfo.Add(oneMethodInfo); |
|
137 } |
|
138 |
|
139 bool needSolid = false; |
|
140 for(int i = 0; i < methodsInfo.Size(); i++) |
|
141 { |
|
142 COneMethodInfo &oneMethodInfo = methodsInfo[i]; |
|
143 SetCompressionMethod2(oneMethodInfo |
|
144 #ifdef COMPRESS_MT |
|
145 , numThreads |
|
146 #endif |
|
147 ); |
|
148 |
|
149 if (!IsCopyMethod(oneMethodInfo.MethodName)) |
|
150 needSolid = true; |
|
151 |
|
152 CMethodFull methodFull; |
|
153 |
|
154 if (!FindMethod( |
|
155 EXTERNAL_CODECS_VARS |
|
156 oneMethodInfo.MethodName, methodFull.Id, methodFull.NumInStreams, methodFull.NumOutStreams)) |
|
157 return E_INVALIDARG; |
|
158 methodFull.Properties = oneMethodInfo.Properties; |
|
159 methodMode.Methods.Add(methodFull); |
|
160 |
|
161 if (!_numSolidBytesDefined) |
|
162 { |
|
163 for (int j = 0; j < methodFull.Properties.Size(); j++) |
|
164 { |
|
165 const CProp &prop = methodFull.Properties[j]; |
|
166 if ((prop.Id == NCoderPropID::kDictionarySize || |
|
167 prop.Id == NCoderPropID::kUsedMemorySize) && prop.Value.vt == VT_UI4) |
|
168 { |
|
169 _numSolidBytes = ((UInt64)prop.Value.ulVal) << 7; |
|
170 const UInt64 kMinSize = (1 << 24); |
|
171 if (_numSolidBytes < kMinSize) |
|
172 _numSolidBytes = kMinSize; |
|
173 _numSolidBytesDefined = true; |
|
174 break; |
|
175 } |
|
176 } |
|
177 } |
|
178 } |
|
179 |
|
180 if (!needSolid && !_numSolidBytesDefined) |
|
181 { |
|
182 _numSolidBytesDefined = true; |
|
183 _numSolidBytes = 0; |
|
184 } |
|
185 return S_OK; |
|
186 } |
|
187 |
|
188 static HRESULT GetTime(IArchiveUpdateCallback *updateCallback, int index, PROPID propID, CArchiveFileTime &filetime, bool &filetimeIsDefined) |
|
189 { |
|
190 filetimeIsDefined = false; |
|
191 NCOM::CPropVariant propVariant; |
|
192 RINOK(updateCallback->GetProperty(index, propID, &propVariant)); |
|
193 if (propVariant.vt == VT_FILETIME) |
|
194 { |
|
195 filetime = propVariant.filetime; |
|
196 filetimeIsDefined = true; |
|
197 } |
|
198 else if (propVariant.vt != VT_EMPTY) |
|
199 return E_INVALIDARG; |
|
200 return S_OK; |
|
201 } |
|
202 |
|
203 STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems, |
|
204 IArchiveUpdateCallback *updateCallback) |
|
205 { |
|
206 COM_TRY_BEGIN |
|
207 |
|
208 const CArchiveDatabaseEx *database = 0; |
|
209 #ifdef _7Z_VOL |
|
210 if(_volumes.Size() > 1) |
|
211 return E_FAIL; |
|
212 const CVolume *volume = 0; |
|
213 if (_volumes.Size() == 1) |
|
214 { |
|
215 volume = &_volumes.Front(); |
|
216 database = &volume->Database; |
|
217 } |
|
218 #else |
|
219 if (_inStream != 0) |
|
220 database = &_database; |
|
221 #endif |
|
222 |
|
223 // CRecordVector<bool> compressStatuses; |
|
224 CObjectVector<CUpdateItem> updateItems; |
|
225 // CRecordVector<UInt32> copyIndices; |
|
226 |
|
227 // CMyComPtr<IUpdateCallback2> updateCallback2; |
|
228 // updateCallback->QueryInterface(&updateCallback2); |
|
229 |
|
230 for(UInt32 i = 0; i < numItems; i++) |
|
231 { |
|
232 Int32 newData; |
|
233 Int32 newProperties; |
|
234 UInt32 indexInArchive; |
|
235 if (!updateCallback) |
|
236 return E_FAIL; |
|
237 RINOK(updateCallback->GetUpdateItemInfo(i, |
|
238 &newData, &newProperties, &indexInArchive)); |
|
239 CUpdateItem updateItem; |
|
240 updateItem.NewProperties = IntToBool(newProperties); |
|
241 updateItem.NewData = IntToBool(newData); |
|
242 updateItem.IndexInArchive = indexInArchive; |
|
243 updateItem.IndexInClient = i; |
|
244 updateItem.IsAnti = false; |
|
245 updateItem.Size = 0; |
|
246 |
|
247 if (updateItem.IndexInArchive != -1) |
|
248 { |
|
249 const CFileItem &fileItem = database->Files[updateItem.IndexInArchive]; |
|
250 updateItem.Name = fileItem.Name; |
|
251 updateItem.IsDirectory = fileItem.IsDirectory; |
|
252 updateItem.Size = fileItem.UnPackSize; |
|
253 updateItem.IsAnti = fileItem.IsAnti; |
|
254 |
|
255 updateItem.CreationTime = fileItem.CreationTime; |
|
256 updateItem.IsCreationTimeDefined = fileItem.IsCreationTimeDefined; |
|
257 updateItem.LastWriteTime = fileItem.LastWriteTime; |
|
258 updateItem.IsLastWriteTimeDefined = fileItem.IsLastWriteTimeDefined; |
|
259 updateItem.LastAccessTime = fileItem.LastAccessTime; |
|
260 updateItem.IsLastAccessTimeDefined = fileItem.IsLastAccessTimeDefined; |
|
261 } |
|
262 |
|
263 if (updateItem.NewProperties) |
|
264 { |
|
265 bool nameIsDefined; |
|
266 bool folderStatusIsDefined; |
|
267 { |
|
268 NCOM::CPropVariant propVariant; |
|
269 RINOK(updateCallback->GetProperty(i, kpidAttributes, &propVariant)); |
|
270 if (propVariant.vt == VT_EMPTY) |
|
271 updateItem.AttributesAreDefined = false; |
|
272 else if (propVariant.vt != VT_UI4) |
|
273 return E_INVALIDARG; |
|
274 else |
|
275 { |
|
276 updateItem.Attributes = propVariant.ulVal; |
|
277 updateItem.AttributesAreDefined = true; |
|
278 } |
|
279 } |
|
280 |
|
281 RINOK(GetTime(updateCallback, i, kpidCreationTime, updateItem.CreationTime, updateItem.IsCreationTimeDefined)); |
|
282 RINOK(GetTime(updateCallback, i, kpidLastWriteTime, updateItem.LastWriteTime , updateItem.IsLastWriteTimeDefined)); |
|
283 RINOK(GetTime(updateCallback, i, kpidLastAccessTime, updateItem.LastAccessTime, updateItem.IsLastAccessTimeDefined)); |
|
284 |
|
285 { |
|
286 NCOM::CPropVariant propVariant; |
|
287 RINOK(updateCallback->GetProperty(i, kpidPath, &propVariant)); |
|
288 if (propVariant.vt == VT_EMPTY) |
|
289 nameIsDefined = false; |
|
290 else if (propVariant.vt != VT_BSTR) |
|
291 return E_INVALIDARG; |
|
292 else |
|
293 { |
|
294 updateItem.Name = NItemName::MakeLegalName(propVariant.bstrVal); |
|
295 nameIsDefined = true; |
|
296 } |
|
297 } |
|
298 { |
|
299 NCOM::CPropVariant propVariant; |
|
300 RINOK(updateCallback->GetProperty(i, kpidIsFolder, &propVariant)); |
|
301 if (propVariant.vt == VT_EMPTY) |
|
302 folderStatusIsDefined = false; |
|
303 else if (propVariant.vt != VT_BOOL) |
|
304 return E_INVALIDARG; |
|
305 else |
|
306 { |
|
307 updateItem.IsDirectory = (propVariant.boolVal != VARIANT_FALSE); |
|
308 folderStatusIsDefined = true; |
|
309 } |
|
310 } |
|
311 |
|
312 { |
|
313 NCOM::CPropVariant propVariant; |
|
314 RINOK(updateCallback->GetProperty(i, kpidIsAnti, &propVariant)); |
|
315 if (propVariant.vt == VT_EMPTY) |
|
316 updateItem.IsAnti = false; |
|
317 else if (propVariant.vt != VT_BOOL) |
|
318 return E_INVALIDARG; |
|
319 else |
|
320 updateItem.IsAnti = (propVariant.boolVal != VARIANT_FALSE); |
|
321 } |
|
322 |
|
323 if (updateItem.IsAnti) |
|
324 { |
|
325 updateItem.AttributesAreDefined = false; |
|
326 |
|
327 updateItem.IsCreationTimeDefined = false; |
|
328 updateItem.IsLastWriteTimeDefined = false; |
|
329 updateItem.IsLastAccessTimeDefined = false; |
|
330 |
|
331 updateItem.Size = 0; |
|
332 } |
|
333 |
|
334 if (!folderStatusIsDefined && updateItem.AttributesAreDefined) |
|
335 updateItem.SetDirectoryStatusFromAttributes(); |
|
336 } |
|
337 |
|
338 if (updateItem.NewData) |
|
339 { |
|
340 NCOM::CPropVariant propVariant; |
|
341 RINOK(updateCallback->GetProperty(i, kpidSize, &propVariant)); |
|
342 if (propVariant.vt != VT_UI8) |
|
343 return E_INVALIDARG; |
|
344 updateItem.Size = (UInt64)propVariant.uhVal.QuadPart; |
|
345 if (updateItem.Size != 0 && updateItem.IsAnti) |
|
346 return E_INVALIDARG; |
|
347 } |
|
348 updateItems.Add(updateItem); |
|
349 } |
|
350 |
|
351 CCompressionMethodMode methodMode, headerMethod; |
|
352 RINOK(SetCompressionMethod(methodMode, headerMethod)); |
|
353 #ifdef COMPRESS_MT |
|
354 methodMode.NumThreads = _numThreads; |
|
355 headerMethod.NumThreads = 1; |
|
356 #endif |
|
357 |
|
358 RINOK(SetPassword(methodMode, updateCallback)); |
|
359 |
|
360 bool compressMainHeader = _compressHeaders; // check it |
|
361 |
|
362 if (methodMode.PasswordIsDefined) |
|
363 { |
|
364 compressMainHeader = true; |
|
365 if(_encryptHeaders) |
|
366 RINOK(SetPassword(headerMethod, updateCallback)); |
|
367 } |
|
368 |
|
369 if (numItems < 2) |
|
370 compressMainHeader = false; |
|
371 |
|
372 CUpdateOptions options; |
|
373 options.Method = &methodMode; |
|
374 options.HeaderMethod = (_compressHeaders || |
|
375 (methodMode.PasswordIsDefined && _encryptHeaders)) ? |
|
376 &headerMethod : 0; |
|
377 options.UseFilters = _level != 0 && _autoFilter; |
|
378 options.MaxFilter = _level >= 8; |
|
379 |
|
380 options.HeaderOptions.CompressMainHeader = compressMainHeader; |
|
381 options.HeaderOptions.WriteModified = WriteModified; |
|
382 options.HeaderOptions.WriteCreated = WriteCreated; |
|
383 options.HeaderOptions.WriteAccessed = WriteAccessed; |
|
384 |
|
385 options.NumSolidFiles = _numSolidFiles; |
|
386 options.NumSolidBytes = _numSolidBytes; |
|
387 options.SolidExtension = _solidExtension; |
|
388 options.RemoveSfxBlock = _removeSfxBlock; |
|
389 options.VolumeMode = _volumeMode; |
|
390 return Update( |
|
391 EXTERNAL_CODECS_VARS |
|
392 #ifdef _7Z_VOL |
|
393 volume ? volume->Stream: 0, |
|
394 volume ? database: 0, |
|
395 #else |
|
396 _inStream, |
|
397 database, |
|
398 #endif |
|
399 updateItems, outStream, updateCallback, options); |
|
400 COM_TRY_END |
|
401 } |
|
402 |
|
403 static HRESULT GetBindInfoPart(UString &srcString, UInt32 &coder, UInt32 &stream) |
|
404 { |
|
405 stream = 0; |
|
406 int index = ParseStringToUInt32(srcString, coder); |
|
407 if (index == 0) |
|
408 return E_INVALIDARG; |
|
409 srcString.Delete(0, index); |
|
410 if (srcString[0] == 'S') |
|
411 { |
|
412 srcString.Delete(0); |
|
413 int index = ParseStringToUInt32(srcString, stream); |
|
414 if (index == 0) |
|
415 return E_INVALIDARG; |
|
416 srcString.Delete(0, index); |
|
417 } |
|
418 return S_OK; |
|
419 } |
|
420 |
|
421 static HRESULT GetBindInfo(UString &srcString, CBind &bind) |
|
422 { |
|
423 RINOK(GetBindInfoPart(srcString, bind.OutCoder, bind.OutStream)); |
|
424 if (srcString[0] != ':') |
|
425 return E_INVALIDARG; |
|
426 srcString.Delete(0); |
|
427 RINOK(GetBindInfoPart(srcString, bind.InCoder, bind.InStream)); |
|
428 if (!srcString.IsEmpty()) |
|
429 return E_INVALIDARG; |
|
430 return S_OK; |
|
431 } |
|
432 |
|
433 STDMETHODIMP CHandler::SetProperties(const wchar_t **names, const PROPVARIANT *values, Int32 numProperties) |
|
434 { |
|
435 COM_TRY_BEGIN |
|
436 _binds.Clear(); |
|
437 BeforeSetProperty(); |
|
438 |
|
439 for (int i = 0; i < numProperties; i++) |
|
440 { |
|
441 UString name = names[i]; |
|
442 name.MakeUpper(); |
|
443 if (name.IsEmpty()) |
|
444 return E_INVALIDARG; |
|
445 |
|
446 const PROPVARIANT &value = values[i]; |
|
447 |
|
448 if (name[0] == 'B') |
|
449 { |
|
450 name.Delete(0); |
|
451 CBind bind; |
|
452 RINOK(GetBindInfo(name, bind)); |
|
453 _binds.Add(bind); |
|
454 continue; |
|
455 } |
|
456 |
|
457 RINOK(SetProperty(name, value)); |
|
458 } |
|
459 |
|
460 return S_OK; |
|
461 COM_TRY_END |
|
462 } |
|
463 |
|
464 }} |