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