1 // 7zHandlerOut.cpp
2
3 #include "StdAfx.h"
4
5 #include "../../../Common/ComTry.h"
6 #include "../../../Common/StringToInt.h"
7
8 #include "../Common/ItemNameUtils.h"
9 #include "../Common/ParseProperties.h"
10
11 #include "7zHandler.h"
12 #include "7zOut.h"
13 #include "7zUpdate.h"
14
15 using namespace NWindows;
16
17 namespace NArchive {
18 namespace N7z {
19
20 static const wchar_t *k_LZMA_Name = L"LZMA";
21 static const wchar_t *kDefaultMethodName = k_LZMA_Name;
22 static const wchar_t *k_Copy_Name = L"Copy";
23
24 static const wchar_t *k_MatchFinder_ForHeaders = L"BT2";
25 static const UInt32 k_NumFastBytes_ForHeaders = 273;
26 static const UInt32 k_Level_ForHeaders = 5;
27 static const UInt32 k_Dictionary_ForHeaders =
28 #ifdef UNDER_CE
29 1 << 18;
30 #else
31 1 << 20;
32 #endif
33
GetFileTimeType(UInt32 * type)34 STDMETHODIMP CHandler::GetFileTimeType(UInt32 *type)
35 {
36 *type = NFileTimeType::kWindows;
37 return S_OK;
38 }
39
PropsMethod_To_FullMethod(CMethodFull & dest,const COneMethodInfo & m)40 HRESULT CHandler::PropsMethod_To_FullMethod(CMethodFull &dest, const COneMethodInfo &m)
41 {
42 if (!FindMethod(
43 EXTERNAL_CODECS_VARS
44 m.MethodName, dest.Id, dest.NumInStreams, dest.NumOutStreams))
45 return E_INVALIDARG;
46 (CProps &)dest = (CProps &)m;
47 return S_OK;
48 }
49
SetHeaderMethod(CCompressionMethodMode & headerMethod)50 HRESULT CHandler::SetHeaderMethod(CCompressionMethodMode &headerMethod)
51 {
52 if (!_compressHeaders)
53 return S_OK;
54 COneMethodInfo m;
55 m.MethodName = k_LZMA_Name;
56 m.AddPropString(NCoderPropID::kMatchFinder, k_MatchFinder_ForHeaders);
57 m.AddProp32(NCoderPropID::kLevel, k_Level_ForHeaders);
58 m.AddProp32(NCoderPropID::kNumFastBytes, k_NumFastBytes_ForHeaders);
59 m.AddProp32(NCoderPropID::kDictionarySize, k_Dictionary_ForHeaders);
60 m.AddNumThreadsProp(1);
61
62 CMethodFull methodFull;
63 RINOK(PropsMethod_To_FullMethod(methodFull, m));
64 headerMethod.Methods.Add(methodFull);
65 return S_OK;
66 }
67
AddDefaultMethod()68 void CHandler::AddDefaultMethod()
69 {
70 for (int i = 0; i < _methods.Size(); i++)
71 {
72 UString &methodName = _methods[0].MethodName;
73 if (methodName.IsEmpty())
74 methodName = kDefaultMethodName;
75 }
76 if (_methods.IsEmpty())
77 {
78 COneMethodInfo m;
79 m.MethodName = (GetLevel() == 0 ? k_Copy_Name : kDefaultMethodName);
80 _methods.Add(m);
81 }
82 }
83
SetMainMethod(CCompressionMethodMode & methodMode,CObjectVector<COneMethodInfo> & methods,UInt32 numThreads)84 HRESULT CHandler::SetMainMethod(
85 CCompressionMethodMode &methodMode,
86 CObjectVector<COneMethodInfo> &methods
87 #ifndef _7ZIP_ST
88 , UInt32 numThreads
89 #endif
90 )
91 {
92 AddDefaultMethod();
93
94 const UInt64 kSolidBytes_Min = (1 << 24);
95 const UInt64 kSolidBytes_Max = ((UInt64)1 << 32) - 1;
96
97 bool needSolid = false;
98 for (int i = 0; i < methods.Size(); i++)
99 {
100 COneMethodInfo &oneMethodInfo = methods[i];
101 SetGlobalLevelAndThreads(oneMethodInfo
102 #ifndef _7ZIP_ST
103 , numThreads
104 #endif
105 );
106
107 CMethodFull methodFull;
108 RINOK(PropsMethod_To_FullMethod(methodFull, oneMethodInfo));
109 methodMode.Methods.Add(methodFull);
110
111 if (methodFull.Id != k_Copy)
112 needSolid = true;
113
114 if (_numSolidBytesDefined)
115 continue;
116
117 UInt32 dicSize;
118 switch (methodFull.Id)
119 {
120 case k_LZMA:
121 case k_LZMA2: dicSize = oneMethodInfo.Get_Lzma_DicSize(); break;
122 case k_PPMD: dicSize = oneMethodInfo.Get_Ppmd_MemSize(); break;
123 case k_Deflate: dicSize = (UInt32)1 << 15; break;
124 case k_BZip2: dicSize = oneMethodInfo.Get_BZip2_BlockSize(); break;
125 default: continue;
126 }
127 _numSolidBytes = (UInt64)dicSize << 7;
128 if (_numSolidBytes < kSolidBytes_Min) _numSolidBytes = kSolidBytes_Min;
129 if (_numSolidBytes > kSolidBytes_Max) _numSolidBytes = kSolidBytes_Max;
130 _numSolidBytesDefined = true;
131 }
132
133 if (!_numSolidBytesDefined)
134 if (needSolid)
135 _numSolidBytes = kSolidBytes_Max;
136 else
137 _numSolidBytes = 0;
138 _numSolidBytesDefined = true;
139 return S_OK;
140 }
141
GetTime(IArchiveUpdateCallback * updateCallback,int index,bool writeTime,PROPID propID,UInt64 & ft,bool & ftDefined)142 static HRESULT GetTime(IArchiveUpdateCallback *updateCallback, int index, bool writeTime, PROPID propID, UInt64 &ft, bool &ftDefined)
143 {
144 ft = 0;
145 ftDefined = false;
146 if (!writeTime)
147 return S_OK;
148 NCOM::CPropVariant prop;
149 RINOK(updateCallback->GetProperty(index, propID, &prop));
150 if (prop.vt == VT_FILETIME)
151 {
152 ft = prop.filetime.dwLowDateTime | ((UInt64)prop.filetime.dwHighDateTime << 32);
153 ftDefined = true;
154 }
155 else if (prop.vt != VT_EMPTY)
156 return E_INVALIDARG;
157 return S_OK;
158 }
159
UpdateItems(ISequentialOutStream * outStream,UInt32 numItems,IArchiveUpdateCallback * updateCallback)160 STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems,
161 IArchiveUpdateCallback *updateCallback)
162 {
163 COM_TRY_BEGIN
164
165 const CArchiveDatabaseEx *db = 0;
166 #ifdef _7Z_VOL
167 if (_volumes.Size() > 1)
168 return E_FAIL;
169 const CVolume *volume = 0;
170 if (_volumes.Size() == 1)
171 {
172 volume = &_volumes.Front();
173 db = &volume->Database;
174 }
175 #else
176 if (_inStream != 0)
177 db = &_db;
178 #endif
179
180 CObjectVector<CUpdateItem> updateItems;
181
182 for (UInt32 i = 0; i < numItems; i++)
183 {
184 Int32 newData, newProps;
185 UInt32 indexInArchive;
186 if (!updateCallback)
187 return E_FAIL;
188 RINOK(updateCallback->GetUpdateItemInfo(i, &newData, &newProps, &indexInArchive));
189 CUpdateItem ui;
190 ui.NewProps = IntToBool(newProps);
191 ui.NewData = IntToBool(newData);
192 ui.IndexInArchive = indexInArchive;
193 ui.IndexInClient = i;
194 ui.IsAnti = false;
195 ui.Size = 0;
196
197 if (ui.IndexInArchive != -1)
198 {
199 if (db == 0 || ui.IndexInArchive >= db->Files.Size())
200 return E_INVALIDARG;
201 const CFileItem &fi = db->Files[ui.IndexInArchive];
202 ui.Name = fi.Name;
203 ui.IsDir = fi.IsDir;
204 ui.Size = fi.Size;
205 ui.IsAnti = db->IsItemAnti(ui.IndexInArchive);
206
207 ui.CTimeDefined = db->CTime.GetItem(ui.IndexInArchive, ui.CTime);
208 ui.ATimeDefined = db->ATime.GetItem(ui.IndexInArchive, ui.ATime);
209 ui.MTimeDefined = db->MTime.GetItem(ui.IndexInArchive, ui.MTime);
210 }
211
212 if (ui.NewProps)
213 {
214 bool nameIsDefined;
215 bool folderStatusIsDefined;
216 {
217 NCOM::CPropVariant prop;
218 RINOK(updateCallback->GetProperty(i, kpidAttrib, &prop));
219 if (prop.vt == VT_EMPTY)
220 ui.AttribDefined = false;
221 else if (prop.vt != VT_UI4)
222 return E_INVALIDARG;
223 else
224 {
225 ui.Attrib = prop.ulVal;
226 ui.AttribDefined = true;
227 }
228 }
229
230 // we need MTime to sort files.
231 RINOK(GetTime(updateCallback, i, WriteCTime, kpidCTime, ui.CTime, ui.CTimeDefined));
232 RINOK(GetTime(updateCallback, i, WriteATime, kpidATime, ui.ATime, ui.ATimeDefined));
233 RINOK(GetTime(updateCallback, i, true, kpidMTime, ui.MTime, ui.MTimeDefined));
234
235 {
236 NCOM::CPropVariant prop;
237 RINOK(updateCallback->GetProperty(i, kpidPath, &prop));
238 if (prop.vt == VT_EMPTY)
239 nameIsDefined = false;
240 else if (prop.vt != VT_BSTR)
241 return E_INVALIDARG;
242 else
243 {
244 ui.Name = NItemName::MakeLegalName(prop.bstrVal);
245 nameIsDefined = true;
246 }
247 }
248 {
249 NCOM::CPropVariant prop;
250 RINOK(updateCallback->GetProperty(i, kpidIsDir, &prop));
251 if (prop.vt == VT_EMPTY)
252 folderStatusIsDefined = false;
253 else if (prop.vt != VT_BOOL)
254 return E_INVALIDARG;
255 else
256 {
257 ui.IsDir = (prop.boolVal != VARIANT_FALSE);
258 folderStatusIsDefined = true;
259 }
260 }
261
262 {
263 NCOM::CPropVariant prop;
264 RINOK(updateCallback->GetProperty(i, kpidIsAnti, &prop));
265 if (prop.vt == VT_EMPTY)
266 ui.IsAnti = false;
267 else if (prop.vt != VT_BOOL)
268 return E_INVALIDARG;
269 else
270 ui.IsAnti = (prop.boolVal != VARIANT_FALSE);
271 }
272
273 if (ui.IsAnti)
274 {
275 ui.AttribDefined = false;
276
277 ui.CTimeDefined = false;
278 ui.ATimeDefined = false;
279 ui.MTimeDefined = false;
280
281 ui.Size = 0;
282 }
283
284 if (!folderStatusIsDefined && ui.AttribDefined)
285 ui.SetDirStatusFromAttrib();
286 }
287
288 if (ui.NewData)
289 {
290 NCOM::CPropVariant prop;
291 RINOK(updateCallback->GetProperty(i, kpidSize, &prop));
292 if (prop.vt != VT_UI8)
293 return E_INVALIDARG;
294 ui.Size = (UInt64)prop.uhVal.QuadPart;
295 if (ui.Size != 0 && ui.IsAnti)
296 return E_INVALIDARG;
297 }
298 updateItems.Add(ui);
299 }
300
301 CCompressionMethodMode methodMode, headerMethod;
302
303 HRESULT res = SetMainMethod(methodMode, _methods
304 #ifndef _7ZIP_ST
305 , _numThreads
306 #endif
307 );
308 RINOK(res);
309 methodMode.Binds = _binds;
310
311 RINOK(SetHeaderMethod(headerMethod));
312 #ifndef _7ZIP_ST
313 methodMode.NumThreads = _numThreads;
314 headerMethod.NumThreads = 1;
315 #endif
316
317 CMyComPtr<ICryptoGetTextPassword2> getPassword2;
318 updateCallback->QueryInterface(IID_ICryptoGetTextPassword2, (void **)&getPassword2);
319
320 if (getPassword2)
321 {
322 CMyComBSTR password;
323 Int32 passwordIsDefined;
324 RINOK(getPassword2->CryptoGetTextPassword2(&passwordIsDefined, &password));
325 methodMode.PasswordIsDefined = IntToBool(passwordIsDefined);
326 if (methodMode.PasswordIsDefined)
327 methodMode.Password = password;
328 }
329 else
330 methodMode.PasswordIsDefined = false;
331
332 bool compressMainHeader = _compressHeaders; // check it
333
334 bool encryptHeaders = false;
335
336 if (methodMode.PasswordIsDefined)
337 {
338 if (_encryptHeadersSpecified)
339 encryptHeaders = _encryptHeaders;
340 #ifndef _NO_CRYPTO
341 else
342 encryptHeaders = _passwordIsDefined;
343 #endif
344 compressMainHeader = true;
345 if (encryptHeaders)
346 {
347 headerMethod.PasswordIsDefined = methodMode.PasswordIsDefined;
348 headerMethod.Password = methodMode.Password;
349 }
350 }
351
352 if (numItems < 2)
353 compressMainHeader = false;
354
355 CUpdateOptions options;
356 options.Method = &methodMode;
357 options.HeaderMethod = (_compressHeaders || encryptHeaders) ? &headerMethod : 0;
358 int level = GetLevel();
359 options.UseFilters = level != 0 && _autoFilter;
360 options.MaxFilter = level >= 8;
361
362 options.HeaderOptions.CompressMainHeader = compressMainHeader;
363 options.HeaderOptions.WriteCTime = WriteCTime;
364 options.HeaderOptions.WriteATime = WriteATime;
365 options.HeaderOptions.WriteMTime = WriteMTime;
366
367 options.NumSolidFiles = _numSolidFiles;
368 options.NumSolidBytes = _numSolidBytes;
369 options.SolidExtension = _solidExtension;
370 options.RemoveSfxBlock = _removeSfxBlock;
371 options.VolumeMode = _volumeMode;
372
373 COutArchive archive;
374 CArchiveDatabase newDatabase;
375
376 CMyComPtr<ICryptoGetTextPassword> getPassword;
377 updateCallback->QueryInterface(IID_ICryptoGetTextPassword, (void **)&getPassword);
378
379 res = Update(
380 EXTERNAL_CODECS_VARS
381 #ifdef _7Z_VOL
382 volume ? volume->Stream: 0,
383 volume ? db : 0,
384 #else
385 _inStream,
386 db,
387 #endif
388 updateItems,
389 archive, newDatabase, outStream, updateCallback, options
390 #ifndef _NO_CRYPTO
391 , getPassword
392 #endif
393 );
394
395 RINOK(res);
396
397 updateItems.ClearAndFree();
398
399 return archive.WriteDatabase(EXTERNAL_CODECS_VARS
400 newDatabase, options.HeaderMethod, options.HeaderOptions);
401
402 COM_TRY_END
403 }
404
GetBindInfoPart(UString & srcString,UInt32 & coder,UInt32 & stream)405 static HRESULT GetBindInfoPart(UString &srcString, UInt32 &coder, UInt32 &stream)
406 {
407 stream = 0;
408 int index = ParseStringToUInt32(srcString, coder);
409 if (index == 0)
410 return E_INVALIDARG;
411 srcString.Delete(0, index);
412 if (srcString[0] == 'S')
413 {
414 srcString.Delete(0);
415 int index = ParseStringToUInt32(srcString, stream);
416 if (index == 0)
417 return E_INVALIDARG;
418 srcString.Delete(0, index);
419 }
420 return S_OK;
421 }
422
InitProps()423 void COutHandler::InitProps()
424 {
425 CMultiMethodProps::Init();
426
427 _removeSfxBlock = false;
428 _compressHeaders = true;
429 _encryptHeadersSpecified = false;
430 _encryptHeaders = false;
431
432 WriteCTime = false;
433 WriteATime = false;
434 WriteMTime = true;
435
436 _volumeMode = false;
437 InitSolid();
438 }
439
SetSolidFromString(const UString & s)440 HRESULT COutHandler::SetSolidFromString(const UString &s)
441 {
442 UString s2 = s;
443 s2.MakeUpper();
444 for (int i = 0; i < s2.Length();)
445 {
446 const wchar_t *start = ((const wchar_t *)s2) + i;
447 const wchar_t *end;
448 UInt64 v = ConvertStringToUInt64(start, &end);
449 if (start == end)
450 {
451 if (s2[i++] != 'E')
452 return E_INVALIDARG;
453 _solidExtension = true;
454 continue;
455 }
456 i += (int)(end - start);
457 if (i == s2.Length())
458 return E_INVALIDARG;
459 wchar_t c = s2[i++];
460 if (c == 'F')
461 {
462 if (v < 1)
463 v = 1;
464 _numSolidFiles = v;
465 }
466 else
467 {
468 unsigned numBits;
469 switch (c)
470 {
471 case 'B': numBits = 0; break;
472 case 'K': numBits = 10; break;
473 case 'M': numBits = 20; break;
474 case 'G': numBits = 30; break;
475 default: return E_INVALIDARG;
476 }
477 _numSolidBytes = (v << numBits);
478 _numSolidBytesDefined = true;
479 }
480 }
481 return S_OK;
482 }
483
SetSolidFromPROPVARIANT(const PROPVARIANT & value)484 HRESULT COutHandler::SetSolidFromPROPVARIANT(const PROPVARIANT &value)
485 {
486 bool isSolid;
487 switch (value.vt)
488 {
489 case VT_EMPTY: isSolid = true; break;
490 case VT_BOOL: isSolid = (value.boolVal != VARIANT_FALSE); break;
491 case VT_BSTR:
492 if (StringToBool(value.bstrVal, isSolid))
493 break;
494 return SetSolidFromString(value.bstrVal);
495 default: return E_INVALIDARG;
496 }
497 if (isSolid)
498 InitSolid();
499 else
500 _numSolidFiles = 1;
501 return S_OK;
502 }
503
SetProperty(const wchar_t * nameSpec,const PROPVARIANT & value)504 HRESULT COutHandler::SetProperty(const wchar_t *nameSpec, const PROPVARIANT &value)
505 {
506 UString name = nameSpec;
507 name.MakeUpper();
508 if (name.IsEmpty())
509 return E_INVALIDARG;
510
511 if (name[0] == L'S')
512 {
513 name.Delete(0);
514 if (name.IsEmpty())
515 return SetSolidFromPROPVARIANT(value);
516 if (value.vt != VT_EMPTY)
517 return E_INVALIDARG;
518 return SetSolidFromString(name);
519 }
520
521 UInt32 number;
522 int index = ParseStringToUInt32(name, number);
523 UString realName = name.Mid(index);
524 if (index == 0)
525 {
526 if (name.CompareNoCase(L"RSFX") == 0) return PROPVARIANT_to_bool(value, _removeSfxBlock);
527 if (name.CompareNoCase(L"HC") == 0) return PROPVARIANT_to_bool(value, _compressHeaders);
528 if (name.CompareNoCase(L"HCF") == 0)
529 {
530 bool compressHeadersFull = true;
531 RINOK(PROPVARIANT_to_bool(value, compressHeadersFull));
532 return compressHeadersFull ? S_OK: E_INVALIDARG;
533 }
534 if (name.CompareNoCase(L"HE") == 0)
535 {
536 RINOK(PROPVARIANT_to_bool(value, _encryptHeaders));
537 _encryptHeadersSpecified = true;
538 return S_OK;
539 }
540 if (name.CompareNoCase(L"TC") == 0) return PROPVARIANT_to_bool(value, WriteCTime);
541 if (name.CompareNoCase(L"TA") == 0) return PROPVARIANT_to_bool(value, WriteATime);
542 if (name.CompareNoCase(L"TM") == 0) return PROPVARIANT_to_bool(value, WriteMTime);
543 if (name.CompareNoCase(L"V") == 0) return PROPVARIANT_to_bool(value, _volumeMode);
544 }
545 return CMultiMethodProps::SetProperty(name, value);
546 }
547
SetProperties(const wchar_t ** names,const PROPVARIANT * values,Int32 numProps)548 STDMETHODIMP CHandler::SetProperties(const wchar_t **names, const PROPVARIANT *values, Int32 numProps)
549 {
550 COM_TRY_BEGIN
551 _binds.Clear();
552 InitProps();
553
554 for (int i = 0; i < numProps; i++)
555 {
556 UString name = names[i];
557 name.MakeUpper();
558 if (name.IsEmpty())
559 return E_INVALIDARG;
560
561 const PROPVARIANT &value = values[i];
562
563 if (name[0] == 'B')
564 {
565 if (value.vt != VT_EMPTY)
566 return E_INVALIDARG;
567 name.Delete(0);
568 CBind bind;
569 RINOK(GetBindInfoPart(name, bind.OutCoder, bind.OutStream));
570 if (name[0] != ':')
571 return E_INVALIDARG;
572 name.Delete(0);
573 RINOK(GetBindInfoPart(name, bind.InCoder, bind.InStream));
574 if (!name.IsEmpty())
575 return E_INVALIDARG;
576 _binds.Add(bind);
577 continue;
578 }
579
580 RINOK(SetProperty(name, value));
581 }
582
583 int numEmptyMethods = GetNumEmptyMethods();
584 if (numEmptyMethods > 0)
585 {
586 int k;
587 for (k = 0; k < _binds.Size(); k++)
588 {
589 const CBind &bind = _binds[k];
590 if (bind.InCoder < (UInt32)numEmptyMethods ||
591 bind.OutCoder < (UInt32)numEmptyMethods)
592 return E_INVALIDARG;
593 }
594 for (k = 0; k < _binds.Size(); k++)
595 {
596 CBind &bind = _binds[k];
597 bind.InCoder -= (UInt32)numEmptyMethods;
598 bind.OutCoder -= (UInt32)numEmptyMethods;
599 }
600 _methods.Delete(0, numEmptyMethods);
601 }
602
603 AddDefaultMethod();
604
605 if (!_filterMethod.MethodName.IsEmpty())
606 {
607 for (int k = 0; k < _binds.Size(); k++)
608 {
609 CBind &bind = _binds[k];
610 bind.InCoder++;
611 bind.OutCoder++;
612 }
613 _methods.Insert(0, _filterMethod);
614 }
615
616 for (int k = 0; k < _binds.Size(); k++)
617 {
618 const CBind &bind = _binds[k];
619 if (bind.InCoder >= (UInt32)_methods.Size() ||
620 bind.OutCoder >= (UInt32)_methods.Size())
621 return E_INVALIDARG;
622 }
623
624 return S_OK;
625 COM_TRY_END
626 }
627
628 }}
629