1 // 7zHandlerOut.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "../../../Common/ComTry.h"
6 #include "../../../Common/StringToInt.h"
7 #include "../../../Common/Wildcard.h"
8 
9 #include "../Common/ItemNameUtils.h"
10 #include "../Common/ParseProperties.h"
11 
12 #include "7zHandler.h"
13 #include "7zOut.h"
14 #include "7zUpdate.h"
15 
16 using namespace NWindows;
17 
18 namespace NArchive {
19 namespace N7z {
20 
21 static const wchar_t *k_LZMA_Name = L"LZMA";
22 static const wchar_t *kDefaultMethodName = L"LZMA2";
23 static const wchar_t *k_Copy_Name = L"Copy";
24 
25 static const wchar_t *k_MatchFinder_ForHeaders = L"BT2";
26 static const UInt32 k_NumFastBytes_ForHeaders = 273;
27 static const UInt32 k_Level_ForHeaders = 5;
28 static const UInt32 k_Dictionary_ForHeaders =
29   #ifdef UNDER_CE
30   1 << 18;
31   #else
32   1 << 20;
33   #endif
34 
GetFileTimeType(UInt32 * type)35 STDMETHODIMP CHandler::GetFileTimeType(UInt32 *type)
36 {
37   *type = NFileTimeType::kWindows;
38   return S_OK;
39 }
40 
PropsMethod_To_FullMethod(CMethodFull & dest,const COneMethodInfo & m)41 HRESULT CHandler::PropsMethod_To_FullMethod(CMethodFull &dest, const COneMethodInfo &m)
42 {
43   if (!FindMethod(
44       EXTERNAL_CODECS_VARS
45       m.MethodName, dest.Id, dest.NumInStreams, dest.NumOutStreams))
46     return E_INVALIDARG;
47   (CProps &)dest = (CProps &)m;
48   return S_OK;
49 }
50 
SetHeaderMethod(CCompressionMethodMode & headerMethod)51 HRESULT CHandler::SetHeaderMethod(CCompressionMethodMode &headerMethod)
52 {
53   if (!_compressHeaders)
54     return S_OK;
55   COneMethodInfo m;
56   m.MethodName = k_LZMA_Name;
57   m.AddPropString(NCoderPropID::kMatchFinder, k_MatchFinder_ForHeaders);
58   m.AddProp32(NCoderPropID::kLevel, k_Level_ForHeaders);
59   m.AddProp32(NCoderPropID::kNumFastBytes, k_NumFastBytes_ForHeaders);
60   m.AddProp32(NCoderPropID::kDictionarySize, k_Dictionary_ForHeaders);
61   m.AddNumThreadsProp(1);
62 
63   CMethodFull methodFull;
64   RINOK(PropsMethod_To_FullMethod(methodFull, m));
65   headerMethod.Methods.Add(methodFull);
66   return S_OK;
67 }
68 
AddDefaultMethod()69 void CHandler::AddDefaultMethod()
70 {
71   FOR_VECTOR (i, _methods)
72   {
73     UString &methodName = _methods[i].MethodName;
74     if (methodName.IsEmpty())
75       methodName = kDefaultMethodName;
76   }
77   if (_methods.IsEmpty())
78   {
79     COneMethodInfo m;
80     m.MethodName = (GetLevel() == 0 ? k_Copy_Name : kDefaultMethodName);
81     _methods.Add(m);
82   }
83 }
84 
SetMainMethod(CCompressionMethodMode & methodMode,CObjectVector<COneMethodInfo> & methods,UInt32 numThreads)85 HRESULT CHandler::SetMainMethod(
86     CCompressionMethodMode &methodMode,
87     CObjectVector<COneMethodInfo> &methods
88     #ifndef _7ZIP_ST
89     , UInt32 numThreads
90     #endif
91     )
92 {
93   AddDefaultMethod();
94 
95   const UInt64 kSolidBytes_Min = (1 << 24);
96   const UInt64 kSolidBytes_Max = ((UInt64)1 << 32) - 1;
97 
98   bool needSolid = false;
99   FOR_VECTOR (i, methods)
100   {
101     COneMethodInfo &oneMethodInfo = methods[i];
102     SetGlobalLevelAndThreads(oneMethodInfo
103       #ifndef _7ZIP_ST
104       , numThreads
105       #endif
106       );
107 
108     CMethodFull methodFull;
109     RINOK(PropsMethod_To_FullMethod(methodFull, oneMethodInfo));
110     methodMode.Methods.Add(methodFull);
111 
112     if (methodFull.Id != k_Copy)
113       needSolid = true;
114 
115     if (_numSolidBytesDefined)
116       continue;
117 
118     UInt32 dicSize;
119     switch (methodFull.Id)
120     {
121       case k_LZMA:
122       case k_LZMA2: dicSize = oneMethodInfo.Get_Lzma_DicSize(); break;
123       case k_PPMD: dicSize = oneMethodInfo.Get_Ppmd_MemSize(); break;
124       case k_Deflate: dicSize = (UInt32)1 << 15; break;
125       case k_BZip2: dicSize = oneMethodInfo.Get_BZip2_BlockSize(); break;
126       default: continue;
127     }
128     _numSolidBytes = (UInt64)dicSize << 7;
129     if (_numSolidBytes < kSolidBytes_Min) _numSolidBytes = kSolidBytes_Min;
130     if (_numSolidBytes > kSolidBytes_Max) _numSolidBytes = kSolidBytes_Max;
131     _numSolidBytesDefined = true;
132   }
133 
134   if (!_numSolidBytesDefined)
135     if (needSolid)
136       _numSolidBytes = kSolidBytes_Max;
137     else
138       _numSolidBytes = 0;
139   _numSolidBytesDefined = true;
140   return S_OK;
141 }
142 
GetTime(IArchiveUpdateCallback * updateCallback,int index,PROPID propID,UInt64 & ft,bool & ftDefined)143 static HRESULT GetTime(IArchiveUpdateCallback *updateCallback, int index, PROPID propID, UInt64 &ft, bool &ftDefined)
144 {
145   // ft = 0;
146   // ftDefined = false;
147   NCOM::CPropVariant prop;
148   RINOK(updateCallback->GetProperty(index, propID, &prop));
149   if (prop.vt == VT_FILETIME)
150   {
151     ft = prop.filetime.dwLowDateTime | ((UInt64)prop.filetime.dwHighDateTime << 32);
152     ftDefined = true;
153   }
154   else if (prop.vt != VT_EMPTY)
155     return E_INVALIDARG;
156   else
157   {
158     ft = 0;
159     ftDefined = false;
160   }
161   return S_OK;
162 }
163 
164 /*
165 
166 #ifdef _WIN32
167 static const wchar_t kDirDelimiter1 = L'\\';
168 #endif
169 static const wchar_t kDirDelimiter2 = L'/';
170 
171 static inline bool IsCharDirLimiter(wchar_t c)
172 {
173   return (
174     #ifdef _WIN32
175     c == kDirDelimiter1 ||
176     #endif
177     c == kDirDelimiter2);
178 }
179 
180 static int FillSortIndex(CObjectVector<CTreeFolder> &treeFolders, int cur, int curSortIndex)
181 {
182   CTreeFolder &tf = treeFolders[cur];
183   tf.SortIndex = curSortIndex++;
184   for (int i = 0; i < tf.SubFolders.Size(); i++)
185     curSortIndex = FillSortIndex(treeFolders, tf.SubFolders[i], curSortIndex);
186   tf.SortIndexEnd = curSortIndex;
187   return curSortIndex;
188 }
189 
190 static int FindSubFolder(const CObjectVector<CTreeFolder> &treeFolders, int cur, const UString &name, int &insertPos)
191 {
192   const CIntVector &subFolders = treeFolders[cur].SubFolders;
193   int left = 0, right = subFolders.Size();
194   insertPos = -1;
195   for (;;)
196   {
197     if (left == right)
198     {
199       insertPos = left;
200       return -1;
201     }
202     int mid = (left + right) / 2;
203     int midFolder = subFolders[mid];
204     int compare = CompareFileNames(name, treeFolders[midFolder].Name);
205     if (compare == 0)
206       return midFolder;
207     if (compare < 0)
208       right = mid;
209     else
210       left = mid + 1;
211   }
212 }
213 
214 static int AddFolder(CObjectVector<CTreeFolder> &treeFolders, int cur, const UString &name)
215 {
216   int insertPos;
217   int folderIndex = FindSubFolder(treeFolders, cur, name, insertPos);
218   if (folderIndex < 0)
219   {
220     folderIndex = treeFolders.Size();
221     CTreeFolder &newFolder = treeFolders.AddNew();
222     newFolder.Parent = cur;
223     newFolder.Name = name;
224     treeFolders[cur].SubFolders.Insert(insertPos, folderIndex);
225   }
226   // else if (treeFolders[folderIndex].IsAltStreamFolder != isAltStreamFolder) throw 1123234234;
227   return folderIndex;
228 }
229 */
230 
UpdateItems(ISequentialOutStream * outStream,UInt32 numItems,IArchiveUpdateCallback * updateCallback)231 STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems,
232     IArchiveUpdateCallback *updateCallback)
233 {
234   COM_TRY_BEGIN
235 
236   const CDbEx *db = 0;
237   #ifdef _7Z_VOL
238   if (_volumes.Size() > 1)
239     return E_FAIL;
240   const CVolume *volume = 0;
241   if (_volumes.Size() == 1)
242   {
243     volume = &_volumes.Front();
244     db = &volume->Database;
245   }
246   #else
247   if (_inStream != 0)
248     db = &_db;
249   #endif
250 
251   /*
252   CMyComPtr<IArchiveGetRawProps> getRawProps;
253   updateCallback->QueryInterface(IID_IArchiveGetRawProps, (void **)&getRawProps);
254 
255   CUniqBlocks secureBlocks;
256   secureBlocks.AddUniq(NULL, 0);
257 
258   CObjectVector<CTreeFolder> treeFolders;
259   {
260     CTreeFolder folder;
261     folder.Parent = -1;
262     treeFolders.Add(folder);
263   }
264   */
265 
266   CObjectVector<CUpdateItem> updateItems;
267 
268   bool need_CTime = (Write_CTime.Def && Write_CTime.Val);
269   bool need_ATime = (Write_ATime.Def && Write_ATime.Val);
270   bool need_MTime = (Write_MTime.Def && Write_MTime.Val || !Write_MTime.Def);
271   if (db)
272   {
273     if (!Write_CTime.Def) need_CTime = !db->CTime.Defs.IsEmpty();
274     if (!Write_ATime.Def) need_ATime = !db->ATime.Defs.IsEmpty();
275     if (!Write_MTime.Def) need_MTime = !db->MTime.Defs.IsEmpty();
276   }
277 
278   UString s;
279 
280   for (UInt32 i = 0; i < numItems; i++)
281   {
282     Int32 newData, newProps;
283     UInt32 indexInArchive;
284     if (!updateCallback)
285       return E_FAIL;
286     RINOK(updateCallback->GetUpdateItemInfo(i, &newData, &newProps, &indexInArchive));
287     CUpdateItem ui;
288     ui.NewProps = IntToBool(newProps);
289     ui.NewData = IntToBool(newData);
290     ui.IndexInArchive = indexInArchive;
291     ui.IndexInClient = i;
292     ui.IsAnti = false;
293     ui.Size = 0;
294 
295     UString name;
296     // bool isAltStream = false;
297     if (ui.IndexInArchive != -1)
298     {
299       if (db == 0 || (unsigned)ui.IndexInArchive >= db->Files.Size())
300         return E_INVALIDARG;
301       const CFileItem &fi = db->Files[ui.IndexInArchive];
302       if (!ui.NewProps)
303       {
304         _db.GetPath(ui.IndexInArchive, name);
305       }
306       ui.IsDir = fi.IsDir;
307       ui.Size = fi.Size;
308       // isAltStream = fi.IsAltStream;
309       ui.IsAnti = db->IsItemAnti(ui.IndexInArchive);
310 
311       if (!ui.NewProps)
312       {
313         ui.CTimeDefined = db->CTime.GetItem(ui.IndexInArchive, ui.CTime);
314         ui.ATimeDefined = db->ATime.GetItem(ui.IndexInArchive, ui.ATime);
315         ui.MTimeDefined = db->MTime.GetItem(ui.IndexInArchive, ui.MTime);
316       }
317     }
318 
319     if (ui.NewProps)
320     {
321       bool folderStatusIsDefined;
322       {
323         NCOM::CPropVariant prop;
324         RINOK(updateCallback->GetProperty(i, kpidAttrib, &prop));
325         if (prop.vt == VT_EMPTY)
326           ui.AttribDefined = false;
327         else if (prop.vt != VT_UI4)
328           return E_INVALIDARG;
329         else
330         {
331           ui.Attrib = prop.ulVal;
332           ui.AttribDefined = true;
333         }
334       }
335 
336       // we need MTime to sort files.
337       if (need_CTime) RINOK(GetTime(updateCallback, i, kpidCTime, ui.CTime, ui.CTimeDefined));
338       if (need_ATime) RINOK(GetTime(updateCallback, i, kpidATime, ui.ATime, ui.ATimeDefined));
339       if (need_MTime) RINOK(GetTime(updateCallback, i, kpidMTime, ui.MTime, ui.MTimeDefined));
340 
341       /*
342       if (getRawProps)
343       {
344         const void *data;
345         UInt32 dataSize;
346         UInt32 propType;
347 
348         getRawProps->GetRawProp(i, kpidNtSecure, &data, &dataSize, &propType);
349         if (dataSize != 0 && propType != NPropDataType::kRaw)
350           return E_FAIL;
351         ui.SecureIndex = secureBlocks.AddUniq((const Byte *)data, dataSize);
352       }
353       */
354 
355       {
356         NCOM::CPropVariant prop;
357         RINOK(updateCallback->GetProperty(i, kpidPath, &prop));
358         if (prop.vt == VT_EMPTY)
359         {
360         }
361         else if (prop.vt != VT_BSTR)
362           return E_INVALIDARG;
363         else
364         {
365           name = NItemName::MakeLegalName(prop.bstrVal);
366         }
367       }
368       {
369         NCOM::CPropVariant prop;
370         RINOK(updateCallback->GetProperty(i, kpidIsDir, &prop));
371         if (prop.vt == VT_EMPTY)
372           folderStatusIsDefined = false;
373         else if (prop.vt != VT_BOOL)
374           return E_INVALIDARG;
375         else
376         {
377           ui.IsDir = (prop.boolVal != VARIANT_FALSE);
378           folderStatusIsDefined = true;
379         }
380       }
381 
382       {
383         NCOM::CPropVariant prop;
384         RINOK(updateCallback->GetProperty(i, kpidIsAnti, &prop));
385         if (prop.vt == VT_EMPTY)
386           ui.IsAnti = false;
387         else if (prop.vt != VT_BOOL)
388           return E_INVALIDARG;
389         else
390           ui.IsAnti = (prop.boolVal != VARIANT_FALSE);
391       }
392 
393       /*
394       {
395         NCOM::CPropVariant prop;
396         RINOK(updateCallback->GetProperty(i, kpidIsAltStream, &prop));
397         if (prop.vt == VT_EMPTY)
398           isAltStream = false;
399         else if (prop.vt != VT_BOOL)
400           return E_INVALIDARG;
401         else
402           isAltStream = (prop.boolVal != VARIANT_FALSE);
403       }
404       */
405 
406       if (ui.IsAnti)
407       {
408         ui.AttribDefined = false;
409 
410         ui.CTimeDefined = false;
411         ui.ATimeDefined = false;
412         ui.MTimeDefined = false;
413 
414         ui.Size = 0;
415       }
416 
417       if (!folderStatusIsDefined && ui.AttribDefined)
418         ui.SetDirStatusFromAttrib();
419     }
420     else
421     {
422       /*
423       if (_db.SecureIDs.IsEmpty())
424         ui.SecureIndex = secureBlocks.AddUniq(NULL, 0);
425       else
426       {
427         int id = _db.SecureIDs[ui.IndexInArchive];
428         size_t offs = _db.SecureOffsets[id];
429         size_t size = _db.SecureOffsets[id + 1] - offs;
430         ui.SecureIndex = secureBlocks.AddUniq(_db.SecureBuf + offs, size);
431       }
432       */
433     }
434 
435     /*
436     {
437       int folderIndex = 0;
438       if (_useParents)
439       {
440         int j;
441         s.Empty();
442         for (j = 0; j < name.Len(); j++)
443         {
444           wchar_t c = name[j];
445           if (IsCharDirLimiter(c))
446           {
447             folderIndex = AddFolder(treeFolders, folderIndex, s);
448             s.Empty();
449             continue;
450           }
451           s += c;
452         }
453         if (isAltStream)
454         {
455           int colonPos = s.Find(':');
456           if (colonPos < 0)
457           {
458             // isAltStream = false;
459             return E_INVALIDARG;
460           }
461           UString mainName = s.Left(colonPos);
462           int newFolderIndex = AddFolder(treeFolders, folderIndex, mainName);
463           if (treeFolders[newFolderIndex].UpdateItemIndex < 0)
464           {
465             for (int j = updateItems.Size() - 1; j >= 0; j--)
466             {
467               CUpdateItem &ui2 = updateItems[j];
468               if (ui2.ParentFolderIndex == folderIndex
469                   && ui2.Name == mainName)
470               {
471                 ui2.TreeFolderIndex = newFolderIndex;
472                 treeFolders[newFolderIndex].UpdateItemIndex = j;
473               }
474             }
475           }
476           folderIndex = newFolderIndex;
477           s.Delete(0, colonPos + 1);
478         }
479         ui.Name = s;
480       }
481       else
482         ui.Name = name;
483       ui.IsAltStream = isAltStream;
484       ui.ParentFolderIndex = folderIndex;
485       ui.TreeFolderIndex = -1;
486       if (ui.IsDir && !s.IsEmpty())
487       {
488         ui.TreeFolderIndex = AddFolder(treeFolders, folderIndex, s);
489         treeFolders[ui.TreeFolderIndex].UpdateItemIndex = updateItems.Size();
490       }
491     }
492     */
493     ui.Name = name;
494 
495     if (ui.NewData)
496     {
497       NCOM::CPropVariant prop;
498       RINOK(updateCallback->GetProperty(i, kpidSize, &prop));
499       if (prop.vt != VT_UI8)
500         return E_INVALIDARG;
501       ui.Size = (UInt64)prop.uhVal.QuadPart;
502       if (ui.Size != 0 && ui.IsAnti)
503         return E_INVALIDARG;
504     }
505     updateItems.Add(ui);
506   }
507 
508   /*
509   FillSortIndex(treeFolders, 0, 0);
510   for (i = 0; i < (UInt32)updateItems.Size(); i++)
511   {
512     CUpdateItem &ui = updateItems[i];
513     ui.ParentSortIndex = treeFolders[ui.ParentFolderIndex].SortIndex;
514     ui.ParentSortIndexEnd = treeFolders[ui.ParentFolderIndex].SortIndexEnd;
515   }
516   */
517 
518   CCompressionMethodMode methodMode, headerMethod;
519 
520   HRESULT res = SetMainMethod(methodMode, _methods
521     #ifndef _7ZIP_ST
522     , _numThreads
523     #endif
524     );
525   RINOK(res);
526   methodMode.Binds = _binds;
527 
528   RINOK(SetHeaderMethod(headerMethod));
529   #ifndef _7ZIP_ST
530   methodMode.NumThreads = _numThreads;
531   headerMethod.NumThreads = 1;
532   #endif
533 
534   CMyComPtr<ICryptoGetTextPassword2> getPassword2;
535   updateCallback->QueryInterface(IID_ICryptoGetTextPassword2, (void **)&getPassword2);
536 
537   methodMode.PasswordIsDefined = false;
538   methodMode.Password.Empty();
539   if (getPassword2)
540   {
541     CMyComBSTR password;
542     Int32 passwordIsDefined;
543     RINOK(getPassword2->CryptoGetTextPassword2(&passwordIsDefined, &password));
544     methodMode.PasswordIsDefined = IntToBool(passwordIsDefined);
545     if (methodMode.PasswordIsDefined && (BSTR)password)
546       methodMode.Password = password;
547   }
548 
549   bool compressMainHeader = _compressHeaders;  // check it
550 
551   bool encryptHeaders = false;
552 
553   if (methodMode.PasswordIsDefined)
554   {
555     if (_encryptHeadersSpecified)
556       encryptHeaders = _encryptHeaders;
557     #ifndef _NO_CRYPTO
558     else
559       encryptHeaders = _passwordIsDefined;
560     #endif
561     compressMainHeader = true;
562     if (encryptHeaders)
563     {
564       headerMethod.PasswordIsDefined = methodMode.PasswordIsDefined;
565       headerMethod.Password = methodMode.Password;
566     }
567   }
568 
569   if (numItems < 2)
570     compressMainHeader = false;
571 
572   CUpdateOptions options;
573   options.Method = &methodMode;
574   options.HeaderMethod = (_compressHeaders || encryptHeaders) ? &headerMethod : 0;
575   int level = GetLevel();
576   options.UseFilters = level != 0 && _autoFilter;
577   options.MaxFilter = level >= 8;
578 
579   options.HeaderOptions.CompressMainHeader = compressMainHeader;
580   /*
581   options.HeaderOptions.WriteCTime = Write_CTime;
582   options.HeaderOptions.WriteATime = Write_ATime;
583   options.HeaderOptions.WriteMTime = Write_MTime;
584   */
585 
586   options.NumSolidFiles = _numSolidFiles;
587   options.NumSolidBytes = _numSolidBytes;
588   options.SolidExtension = _solidExtension;
589   options.RemoveSfxBlock = _removeSfxBlock;
590   options.VolumeMode = _volumeMode;
591 
592   COutArchive archive;
593   CArchiveDatabaseOut newDatabase;
594 
595   CMyComPtr<ICryptoGetTextPassword> getPassword;
596   updateCallback->QueryInterface(IID_ICryptoGetTextPassword, (void **)&getPassword);
597 
598   /*
599   if (secureBlocks.Sorted.Size() > 1)
600   {
601     secureBlocks.GetReverseMap();
602     for (int i = 0; i < updateItems.Size(); i++)
603     {
604       int &secureIndex = updateItems[i].SecureIndex;
605       secureIndex = secureBlocks.BufIndexToSortedIndex[secureIndex];
606     }
607   }
608   */
609 
610   res = Update(
611       EXTERNAL_CODECS_VARS
612       #ifdef _7Z_VOL
613       volume ? volume->Stream: 0,
614       volume ? db : 0,
615       #else
616       _inStream,
617       db,
618       #endif
619       updateItems,
620       // treeFolders,
621       // secureBlocks,
622       archive, newDatabase, outStream, updateCallback, options
623       #ifndef _NO_CRYPTO
624       , getPassword
625       #endif
626       );
627 
628   RINOK(res);
629 
630   updateItems.ClearAndFree();
631 
632   return archive.WriteDatabase(EXTERNAL_CODECS_VARS
633       newDatabase, options.HeaderMethod, options.HeaderOptions);
634 
635   COM_TRY_END
636 }
637 
GetBindInfoPart(UString & srcString,UInt32 & coder,UInt32 & stream)638 static HRESULT GetBindInfoPart(UString &srcString, UInt32 &coder, UInt32 &stream)
639 {
640   stream = 0;
641   int index = ParseStringToUInt32(srcString, coder);
642   if (index == 0)
643     return E_INVALIDARG;
644   srcString.Delete(0, index);
645   if (srcString[0] == 's')
646   {
647     srcString.Delete(0);
648     int index = ParseStringToUInt32(srcString, stream);
649     if (index == 0)
650       return E_INVALIDARG;
651     srcString.Delete(0, index);
652   }
653   return S_OK;
654 }
655 
InitProps()656 void COutHandler::InitProps()
657 {
658   CMultiMethodProps::Init();
659 
660   _removeSfxBlock = false;
661   _compressHeaders = true;
662   _encryptHeadersSpecified = false;
663   _encryptHeaders = false;
664   // _useParents = false;
665 
666   Write_CTime.Init();
667   Write_ATime.Init();
668   Write_MTime.Init();
669 
670   _volumeMode = false;
671   InitSolid();
672 }
673 
SetSolidFromString(const UString & s)674 HRESULT COutHandler::SetSolidFromString(const UString &s)
675 {
676   UString s2 = s;
677   s2.MakeLower_Ascii();
678   for (unsigned i = 0; i < s2.Len();)
679   {
680     const wchar_t *start = ((const wchar_t *)s2) + i;
681     const wchar_t *end;
682     UInt64 v = ConvertStringToUInt64(start, &end);
683     if (start == end)
684     {
685       if (s2[i++] != 'e')
686         return E_INVALIDARG;
687       _solidExtension = true;
688       continue;
689     }
690     i += (int)(end - start);
691     if (i == s2.Len())
692       return E_INVALIDARG;
693     wchar_t c = s2[i++];
694     if (c == 'f')
695     {
696       if (v < 1)
697         v = 1;
698       _numSolidFiles = v;
699     }
700     else
701     {
702       unsigned numBits;
703       switch (c)
704       {
705         case 'b': numBits =  0; break;
706         case 'k': numBits = 10; break;
707         case 'm': numBits = 20; break;
708         case 'g': numBits = 30; break;
709         case 't': numBits = 40; break;
710         default: return E_INVALIDARG;
711       }
712       _numSolidBytes = (v << numBits);
713       _numSolidBytesDefined = true;
714     }
715   }
716   return S_OK;
717 }
718 
SetSolidFromPROPVARIANT(const PROPVARIANT & value)719 HRESULT COutHandler::SetSolidFromPROPVARIANT(const PROPVARIANT &value)
720 {
721   bool isSolid;
722   switch (value.vt)
723   {
724     case VT_EMPTY: isSolid = true; break;
725     case VT_BOOL: isSolid = (value.boolVal != VARIANT_FALSE); break;
726     case VT_BSTR:
727       if (StringToBool(value.bstrVal, isSolid))
728         break;
729       return SetSolidFromString(value.bstrVal);
730     default: return E_INVALIDARG;
731   }
732   if (isSolid)
733     InitSolid();
734   else
735     _numSolidFiles = 1;
736   return S_OK;
737 }
738 
PROPVARIANT_to_BoolPair(const PROPVARIANT & prop,CBoolPair & dest)739 static HRESULT PROPVARIANT_to_BoolPair(const PROPVARIANT &prop, CBoolPair &dest)
740 {
741   RINOK(PROPVARIANT_to_bool(prop, dest.Val));
742   dest.Def = true;
743   return S_OK;
744 }
745 
SetProperty(const wchar_t * nameSpec,const PROPVARIANT & value)746 HRESULT COutHandler::SetProperty(const wchar_t *nameSpec, const PROPVARIANT &value)
747 {
748   UString name = nameSpec;
749   name.MakeLower_Ascii();
750   if (name.IsEmpty())
751     return E_INVALIDARG;
752 
753   if (name[0] == L's')
754   {
755     name.Delete(0);
756     if (name.IsEmpty())
757       return SetSolidFromPROPVARIANT(value);
758     if (value.vt != VT_EMPTY)
759       return E_INVALIDARG;
760     return SetSolidFromString(name);
761   }
762 
763   UInt32 number;
764   int index = ParseStringToUInt32(name, number);
765   UString realName = name.Ptr(index);
766   if (index == 0)
767   {
768     if (name.IsEqualTo("rsfx")) return PROPVARIANT_to_bool(value, _removeSfxBlock);
769     if (name.IsEqualTo("hc")) return PROPVARIANT_to_bool(value, _compressHeaders);
770     // if (name.IsEqualToNoCase(L"HS")) return PROPVARIANT_to_bool(value, _useParents);
771 
772     if (name.IsEqualTo("hcf"))
773     {
774       bool compressHeadersFull = true;
775       RINOK(PROPVARIANT_to_bool(value, compressHeadersFull));
776       return compressHeadersFull ? S_OK: E_INVALIDARG;
777     }
778 
779     if (name.IsEqualTo("he"))
780     {
781       RINOK(PROPVARIANT_to_bool(value, _encryptHeaders));
782       _encryptHeadersSpecified = true;
783       return S_OK;
784     }
785 
786     if (name.IsEqualTo("tc")) return PROPVARIANT_to_BoolPair(value, Write_CTime);
787     if (name.IsEqualTo("ta")) return PROPVARIANT_to_BoolPair(value, Write_ATime);
788     if (name.IsEqualTo("tm")) return PROPVARIANT_to_BoolPair(value, Write_MTime);
789 
790     if (name.IsEqualTo("v"))  return PROPVARIANT_to_bool(value, _volumeMode);
791   }
792   return CMultiMethodProps::SetProperty(name, value);
793 }
794 
SetProperties(const wchar_t ** names,const PROPVARIANT * values,UInt32 numProps)795 STDMETHODIMP CHandler::SetProperties(const wchar_t **names, const PROPVARIANT *values, UInt32 numProps)
796 {
797   COM_TRY_BEGIN
798   _binds.Clear();
799   InitProps();
800 
801   for (UInt32 i = 0; i < numProps; i++)
802   {
803     UString name = names[i];
804     name.MakeLower_Ascii();
805     if (name.IsEmpty())
806       return E_INVALIDARG;
807 
808     const PROPVARIANT &value = values[i];
809 
810     if (name[0] == 'b')
811     {
812       if (value.vt != VT_EMPTY)
813         return E_INVALIDARG;
814       name.Delete(0);
815       CBind bind;
816       RINOK(GetBindInfoPart(name, bind.OutCoder, bind.OutStream));
817       if (name[0] != ':')
818         return E_INVALIDARG;
819       name.Delete(0);
820       RINOK(GetBindInfoPart(name, bind.InCoder, bind.InStream));
821       if (!name.IsEmpty())
822         return E_INVALIDARG;
823       _binds.Add(bind);
824       continue;
825     }
826 
827     RINOK(SetProperty(name, value));
828   }
829 
830   unsigned numEmptyMethods = GetNumEmptyMethods();
831   if (numEmptyMethods > 0)
832   {
833     unsigned k;
834     for (k = 0; k < _binds.Size(); k++)
835     {
836       const CBind &bind = _binds[k];
837       if (bind.InCoder < (UInt32)numEmptyMethods ||
838           bind.OutCoder < (UInt32)numEmptyMethods)
839         return E_INVALIDARG;
840     }
841     for (k = 0; k < _binds.Size(); k++)
842     {
843       CBind &bind = _binds[k];
844       bind.InCoder -= (UInt32)numEmptyMethods;
845       bind.OutCoder -= (UInt32)numEmptyMethods;
846     }
847     _methods.DeleteFrontal(numEmptyMethods);
848   }
849 
850   AddDefaultMethod();
851 
852   if (!_filterMethod.MethodName.IsEmpty())
853   {
854     FOR_VECTOR (k, _binds)
855     {
856       CBind &bind = _binds[k];
857       bind.InCoder++;
858       bind.OutCoder++;
859     }
860     _methods.Insert(0, _filterMethod);
861   }
862 
863   FOR_VECTOR (k, _binds)
864   {
865     const CBind &bind = _binds[k];
866     if (bind.InCoder >= (UInt32)_methods.Size() ||
867         bind.OutCoder >= (UInt32)_methods.Size())
868       return E_INVALIDARG;
869   }
870 
871   return S_OK;
872   COM_TRY_END
873 }
874 
875 }}
876