1 // Update.cpp 2 3 #include "StdAfx.h" 4 5 #include "Update.h" 6 7 #include "../../../Common/IntToString.h" 8 #include "../../../Common/StringConvert.h" 9 10 #include "../../../Windows/DLL.h" 11 #include "../../../Windows/FileDir.h" 12 #include "../../../Windows/FileFind.h" 13 #include "../../../Windows/FileName.h" 14 #include "../../../Windows/PropVariant.h" 15 #include "../../../Windows/PropVariantConv.h" 16 #include "../../../Windows/TimeUtils.h" 17 18 #include "../../Common/FileStreams.h" 19 #include "../../Common/LimitedStreams.h" 20 21 #include "../../Compress/CopyCoder.h" 22 23 #include "../Common/DirItem.h" 24 #include "../Common/EnumDirItems.h" 25 #include "../Common/OpenArchive.h" 26 #include "../Common/UpdateProduce.h" 27 28 #include "EnumDirItems.h" 29 #include "SetProperties.h" 30 #include "TempFiles.h" 31 #include "UpdateCallback.h" 32 33 static const char *kUpdateIsNotSupoorted = 34 "update operations are not supported for this archive"; 35 36 using namespace NWindows; 37 using namespace NCOM; 38 using namespace NFile; 39 using namespace NDir; 40 using namespace NName; 41 42 static CFSTR kTempFolderPrefix = FTEXT("7zE"); 43 44 45 static bool DeleteEmptyFolderAndEmptySubFolders(const FString &path) 46 { 47 NFind::CFileInfo fileInfo; 48 FString pathPrefix = path + FCHAR_PATH_SEPARATOR; 49 { 50 NFind::CEnumerator enumerator(pathPrefix + FCHAR_ANY_MASK); 51 while (enumerator.Next(fileInfo)) 52 { 53 if (fileInfo.IsDir()) 54 if (!DeleteEmptyFolderAndEmptySubFolders(pathPrefix + fileInfo.Name)) 55 return false; 56 } 57 } 58 /* 59 // we don't need clear read-only for folders 60 if (!MySetFileAttributes(path, 0)) 61 return false; 62 */ 63 return RemoveDir(path); 64 } 65 66 67 using namespace NUpdateArchive; 68 69 class COutMultiVolStream: 70 public IOutStream, 71 public CMyUnknownImp 72 { 73 unsigned _streamIndex; // required stream 74 UInt64 _offsetPos; // offset from start of _streamIndex index 75 UInt64 _absPos; 76 UInt64 _length; 77 78 struct CAltStreamInfo 79 { 80 COutFileStream *StreamSpec; 81 CMyComPtr<IOutStream> Stream; 82 FString Name; 83 UInt64 Pos; 84 UInt64 RealSize; 85 }; 86 CObjectVector<CAltStreamInfo> Streams; 87 public: 88 // CMyComPtr<IArchiveUpdateCallback2> VolumeCallback; 89 CRecordVector<UInt64> Sizes; 90 FString Prefix; 91 CTempFiles *TempFiles; 92 93 void Init() 94 { 95 _streamIndex = 0; 96 _offsetPos = 0; 97 _absPos = 0; 98 _length = 0; 99 } 100 101 bool SetMTime(const FILETIME *mTime); 102 HRESULT Close(); 103 104 MY_UNKNOWN_IMP1(IOutStream) 105 106 STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); 107 STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); 108 STDMETHOD(SetSize)(UInt64 newSize); 109 }; 110 111 // static NSynchronization::CCriticalSection g_TempPathsCS; 112 113 HRESULT COutMultiVolStream::Close() 114 { 115 HRESULT res = S_OK; 116 FOR_VECTOR (i, Streams) 117 { 118 COutFileStream *s = Streams[i].StreamSpec; 119 if (s) 120 { 121 HRESULT res2 = s->Close(); 122 if (res2 != S_OK) 123 res = res2; 124 } 125 } 126 return res; 127 } 128 129 bool COutMultiVolStream::SetMTime(const FILETIME *mTime) 130 { 131 bool res = true; 132 FOR_VECTOR (i, Streams) 133 { 134 COutFileStream *s = Streams[i].StreamSpec; 135 if (s) 136 if (!s->SetMTime(mTime)) 137 res = false; 138 } 139 return res; 140 } 141 142 STDMETHODIMP COutMultiVolStream::Write(const void *data, UInt32 size, UInt32 *processedSize) 143 { 144 if (processedSize != NULL) 145 *processedSize = 0; 146 while (size > 0) 147 { 148 if (_streamIndex >= Streams.Size()) 149 { 150 CAltStreamInfo altStream; 151 152 FChar temp[16]; 153 ConvertUInt32ToString(_streamIndex + 1, temp); 154 FString res = temp; 155 while (res.Len() < 3) 156 res = FString(FTEXT('0')) + res; 157 FString name = Prefix + res; 158 altStream.StreamSpec = new COutFileStream; 159 altStream.Stream = altStream.StreamSpec; 160 if (!altStream.StreamSpec->Create(name, false)) 161 return ::GetLastError(); 162 { 163 // NSynchronization::CCriticalSectionLock lock(g_TempPathsCS); 164 TempFiles->Paths.Add(name); 165 } 166 167 altStream.Pos = 0; 168 altStream.RealSize = 0; 169 altStream.Name = name; 170 Streams.Add(altStream); 171 continue; 172 } 173 CAltStreamInfo &altStream = Streams[_streamIndex]; 174 175 unsigned index = _streamIndex; 176 if (index >= Sizes.Size()) 177 index = Sizes.Size() - 1; 178 UInt64 volSize = Sizes[index]; 179 180 if (_offsetPos >= volSize) 181 { 182 _offsetPos -= volSize; 183 _streamIndex++; 184 continue; 185 } 186 if (_offsetPos != altStream.Pos) 187 { 188 // CMyComPtr<IOutStream> outStream; 189 // RINOK(altStream.Stream.QueryInterface(IID_IOutStream, &outStream)); 190 RINOK(altStream.Stream->Seek(_offsetPos, STREAM_SEEK_SET, NULL)); 191 altStream.Pos = _offsetPos; 192 } 193 194 UInt32 curSize = (UInt32)MyMin((UInt64)size, volSize - altStream.Pos); 195 UInt32 realProcessed; 196 RINOK(altStream.Stream->Write(data, curSize, &realProcessed)); 197 data = (void *)((Byte *)data + realProcessed); 198 size -= realProcessed; 199 altStream.Pos += realProcessed; 200 _offsetPos += realProcessed; 201 _absPos += realProcessed; 202 if (_absPos > _length) 203 _length = _absPos; 204 if (_offsetPos > altStream.RealSize) 205 altStream.RealSize = _offsetPos; 206 if (processedSize != NULL) 207 *processedSize += realProcessed; 208 if (altStream.Pos == volSize) 209 { 210 _streamIndex++; 211 _offsetPos = 0; 212 } 213 if (realProcessed == 0 && curSize != 0) 214 return E_FAIL; 215 break; 216 } 217 return S_OK; 218 } 219 220 STDMETHODIMP COutMultiVolStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) 221 { 222 if (seekOrigin >= 3) 223 return STG_E_INVALIDFUNCTION; 224 switch (seekOrigin) 225 { 226 case STREAM_SEEK_SET: _absPos = offset; break; 227 case STREAM_SEEK_CUR: _absPos += offset; break; 228 case STREAM_SEEK_END: _absPos = _length + offset; break; 229 } 230 _offsetPos = _absPos; 231 if (newPosition != NULL) 232 *newPosition = _absPos; 233 _streamIndex = 0; 234 return S_OK; 235 } 236 237 STDMETHODIMP COutMultiVolStream::SetSize(UInt64 newSize) 238 { 239 if (newSize < 0) 240 return E_INVALIDARG; 241 unsigned i = 0; 242 while (i < Streams.Size()) 243 { 244 CAltStreamInfo &altStream = Streams[i++]; 245 if ((UInt64)newSize < altStream.RealSize) 246 { 247 RINOK(altStream.Stream->SetSize(newSize)); 248 altStream.RealSize = newSize; 249 break; 250 } 251 newSize -= altStream.RealSize; 252 } 253 while (i < Streams.Size()) 254 { 255 { 256 CAltStreamInfo &altStream = Streams.Back(); 257 altStream.Stream.Release(); 258 DeleteFileAlways(altStream.Name); 259 } 260 Streams.DeleteBack(); 261 } 262 _offsetPos = _absPos; 263 _streamIndex = 0; 264 _length = newSize; 265 return S_OK; 266 } 267 268 void CArchivePath::ParseFromPath(const UString &path, EArcNameMode mode) 269 { 270 OriginalPath = path; 271 272 SplitPathToParts_2(path, Prefix, Name); 273 274 if (mode == k_ArcNameMode_Add) 275 return; 276 if (mode == k_ArcNameMode_Exact) 277 { 278 BaseExtension.Empty(); 279 return; 280 } 281 282 int dotPos = Name.ReverseFind(L'.'); 283 if (dotPos < 0) 284 return; 285 if ((unsigned)dotPos == Name.Len() - 1) 286 { 287 Name.DeleteBack(); 288 BaseExtension.Empty(); 289 return; 290 } 291 const UString ext = Name.Ptr(dotPos + 1); 292 if (BaseExtension.IsEqualToNoCase(ext)) 293 { 294 BaseExtension = ext; 295 Name.DeleteFrom(dotPos); 296 } 297 else 298 BaseExtension.Empty(); 299 } 300 301 UString CArchivePath::GetFinalPath() const 302 { 303 UString path = GetPathWithoutExt(); 304 if (!BaseExtension.IsEmpty()) 305 path += UString(L'.') + BaseExtension; 306 return path; 307 } 308 309 UString CArchivePath::GetFinalVolPath() const 310 { 311 UString path = GetPathWithoutExt(); 312 if (!BaseExtension.IsEmpty()) 313 path += UString(L'.') + VolExtension; 314 return path; 315 } 316 317 FString CArchivePath::GetTempPath() const 318 { 319 FString path = TempPrefix + us2fs(Name); 320 if (!BaseExtension.IsEmpty()) 321 path += FString(FTEXT('.')) + us2fs(BaseExtension); 322 path += FTEXT(".tmp"); 323 path += TempPostfix; 324 return path; 325 } 326 327 static const wchar_t *kDefaultArcType = L"7z"; 328 static const wchar_t *kDefaultArcExt = L"7z"; 329 static const wchar_t *kSFXExtension = 330 #ifdef _WIN32 331 L"exe"; 332 #else 333 L""; 334 #endif 335 336 bool CUpdateOptions::InitFormatIndex(const CCodecs *codecs, 337 const CObjectVector<COpenType> &types, const UString &arcPath) 338 { 339 if (types.Size() > 1) 340 return false; 341 // int arcTypeIndex = -1; 342 if (types.Size() != 0) 343 { 344 MethodMode.Type = types[0]; 345 MethodMode.Type_Defined = true; 346 } 347 if (MethodMode.Type.FormatIndex < 0) 348 { 349 // MethodMode.Type = -1; 350 MethodMode.Type = COpenType(); 351 if (ArcNameMode != k_ArcNameMode_Add) 352 { 353 MethodMode.Type.FormatIndex = codecs->FindFormatForArchiveName(arcPath); 354 if (MethodMode.Type.FormatIndex >= 0) 355 MethodMode.Type_Defined = true; 356 } 357 } 358 return true; 359 } 360 361 bool CUpdateOptions::SetArcPath(const CCodecs *codecs, const UString &arcPath) 362 { 363 UString typeExt; 364 int formatIndex = MethodMode.Type.FormatIndex; 365 if (formatIndex < 0) 366 { 367 typeExt = kDefaultArcExt; 368 } 369 else 370 { 371 const CArcInfoEx &arcInfo = codecs->Formats[formatIndex]; 372 if (!arcInfo.UpdateEnabled) 373 return false; 374 typeExt = arcInfo.GetMainExt(); 375 } 376 UString ext = typeExt; 377 if (SfxMode) 378 ext = kSFXExtension; 379 ArchivePath.BaseExtension = ext; 380 ArchivePath.VolExtension = typeExt; 381 ArchivePath.ParseFromPath(arcPath, ArcNameMode); 382 FOR_VECTOR (i, Commands) 383 { 384 CUpdateArchiveCommand &uc = Commands[i]; 385 uc.ArchivePath.BaseExtension = ext; 386 uc.ArchivePath.VolExtension = typeExt; 387 uc.ArchivePath.ParseFromPath(uc.UserArchivePath, ArcNameMode); 388 } 389 return true; 390 } 391 392 /* 393 struct CUpdateProduceCallbackImp: public IUpdateProduceCallback 394 { 395 const CObjectVector<CArcItem> *_arcItems; 396 IUpdateCallbackUI *_callback; 397 398 CUpdateProduceCallbackImp(const CObjectVector<CArcItem> *a, 399 IUpdateCallbackUI *callback): _arcItems(a), _callback(callback) {} 400 virtual HRESULT ShowDeleteFile(int arcIndex); 401 }; 402 403 HRESULT CUpdateProduceCallbackImp::ShowDeleteFile(int arcIndex) 404 { 405 return _callback->ShowDeleteFile((*_arcItems)[arcIndex].Name); 406 } 407 */ 408 409 bool CRenamePair::Prepare() 410 { 411 if (RecursedType != NRecursedType::kNonRecursed) 412 return false; 413 if (!WildcardParsing) 414 return true; 415 return !DoesNameContainWildcard(OldName); 416 } 417 418 extern bool g_CaseSensitive; 419 420 static int CompareTwoNames(const wchar_t *s1, const wchar_t *s2) 421 { 422 for (int i = 0;; i++) 423 { 424 wchar_t c1 = s1[i]; 425 wchar_t c2 = s2[i]; 426 if (c1 == 0 || c2 == 0) 427 return i; 428 if (c1 == c2) 429 continue; 430 if (!g_CaseSensitive && (MyCharUpper(c1) == MyCharUpper(c2))) 431 continue; 432 if (IsCharDirLimiter(c1) && IsCharDirLimiter(c2)) 433 continue; 434 return i; 435 } 436 } 437 438 bool CRenamePair::GetNewPath(bool isFolder, const UString &src, UString &dest) const 439 { 440 int num = CompareTwoNames(OldName, src); 441 if (OldName[num] == 0) 442 { 443 if (src[num] != 0 && !IsCharDirLimiter(src[num]) && num != 0 && !IsCharDirLimiter(src[num - 1])) 444 return false; 445 } 446 else 447 { 448 // OldName[num] != 0 449 // OldName = "1\1a.txt" 450 // src = "1" 451 452 if (!isFolder || 453 src[num] != 0 || 454 !IsCharDirLimiter(OldName[num]) || 455 OldName[num + 1] != 0) 456 return false; 457 } 458 dest = NewName + src.Ptr(num); 459 return true; 460 } 461 462 static int GetReverseSlashPos(const UString &name) 463 { 464 int slashPos = name.ReverseFind(L'/'); 465 #ifdef _WIN32 466 int slash1Pos = name.ReverseFind(L'\\'); 467 slashPos = MyMax(slashPos, slash1Pos); 468 #endif 469 return slashPos; 470 } 471 472 static HRESULT Compress( 473 const CUpdateOptions &options, 474 CCodecs *codecs, 475 const CActionSet &actionSet, 476 const CArc *arc, 477 CArchivePath &archivePath, 478 const CObjectVector<CArcItem> &arcItems, 479 Byte *processedItemsStatuses, 480 const CDirItems &dirItems, 481 const CDirItem *parentDirItem, 482 CTempFiles &tempFiles, 483 CUpdateErrorInfo &errorInfo, 484 IUpdateCallbackUI *callback) 485 { 486 CMyComPtr<IOutArchive> outArchive; 487 int formatIndex = options.MethodMode.Type.FormatIndex; 488 if (arc) 489 { 490 formatIndex = arc->FormatIndex; 491 if (formatIndex < 0) 492 return E_NOTIMPL; 493 CMyComPtr<IInArchive> archive2 = arc->Archive; 494 HRESULT result = archive2.QueryInterface(IID_IOutArchive, &outArchive); 495 if (result != S_OK) 496 throw kUpdateIsNotSupoorted; 497 } 498 else 499 { 500 RINOK(codecs->CreateOutArchive(formatIndex, outArchive)); 501 502 #ifdef EXTERNAL_CODECS 503 { 504 CMyComPtr<ISetCompressCodecsInfo> setCompressCodecsInfo; 505 outArchive.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo); 506 if (setCompressCodecsInfo) 507 { 508 RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(codecs)); 509 } 510 } 511 #endif 512 } 513 if (outArchive == 0) 514 throw kUpdateIsNotSupoorted; 515 516 NFileTimeType::EEnum fileTimeType; 517 UInt32 value; 518 RINOK(outArchive->GetFileTimeType(&value)); 519 520 switch (value) 521 { 522 case NFileTimeType::kWindows: 523 case NFileTimeType::kUnix: 524 case NFileTimeType::kDOS: 525 fileTimeType = (NFileTimeType::EEnum)value; 526 break; 527 default: 528 return E_FAIL; 529 } 530 531 { 532 const CArcInfoEx &arcInfo = codecs->Formats[formatIndex]; 533 if (options.AltStreams.Val && !arcInfo.Flags_AltStreams()) 534 return E_NOTIMPL; 535 if (options.NtSecurity.Val && !arcInfo.Flags_NtSecure()) 536 return E_NOTIMPL; 537 } 538 539 CRecordVector<CUpdatePair2> updatePairs2; 540 541 UStringVector newNames; 542 543 if (options.RenamePairs.Size() != 0) 544 { 545 FOR_VECTOR (i, arcItems) 546 { 547 const CArcItem &ai = arcItems[i]; 548 bool needRename = false; 549 UString dest; 550 if (ai.Censored) 551 { 552 FOR_VECTOR (j, options.RenamePairs) 553 { 554 const CRenamePair &rp = options.RenamePairs[j]; 555 if (rp.GetNewPath(ai.IsDir, ai.Name, dest)) 556 { 557 needRename = true; 558 break; 559 } 560 if (ai.IsAltStream) 561 { 562 int colonPos = ai.Name.ReverseFind(':'); 563 int slashPosPos = GetReverseSlashPos(ai.Name); 564 if (colonPos > slashPosPos) 565 { 566 UString mainName = ai.Name.Left(colonPos); 567 /* 568 actually we must improve that code to support cases 569 with folder renaming like: rn arc dir1\ dir2\ 570 */ 571 if (rp.GetNewPath(false, mainName, dest)) 572 { 573 needRename = true; 574 dest += ':'; 575 dest += ai.Name.Ptr(colonPos + 1); 576 break; 577 } 578 } 579 } 580 } 581 } 582 CUpdatePair2 up2; 583 up2.SetAs_NoChangeArcItem(ai.IndexInServer); 584 if (needRename) 585 { 586 up2.NewProps = true; 587 RINOK(arc->IsItemAnti(i, up2.IsAnti)); 588 up2.NewNameIndex = newNames.Add(dest); 589 } 590 updatePairs2.Add(up2); 591 } 592 } 593 else 594 { 595 CRecordVector<CUpdatePair> updatePairs; 596 GetUpdatePairInfoList(dirItems, arcItems, fileTimeType, updatePairs); // must be done only once!!! 597 // CUpdateProduceCallbackImp upCallback(&arcItems, callback); 598 UpdateProduce(updatePairs, actionSet, updatePairs2, NULL /* &upCallback */); 599 } 600 601 UInt32 numFiles = 0; 602 FOR_VECTOR (i, updatePairs2) 603 if (updatePairs2[i].NewData) 604 numFiles++; 605 606 RINOK(callback->SetNumFiles(numFiles)); 607 608 CArchiveUpdateCallback *updateCallbackSpec = new CArchiveUpdateCallback; 609 CMyComPtr<IArchiveUpdateCallback> updateCallback(updateCallbackSpec); 610 611 updateCallbackSpec->ShareForWrite = options.OpenShareForWrite; 612 updateCallbackSpec->StdInMode = options.StdInMode; 613 updateCallbackSpec->Callback = callback; 614 615 if (arc) 616 { 617 // we set Archive to allow to transfer GetProperty requests back to DLL. 618 updateCallbackSpec->Archive = arc->Archive; 619 updateCallbackSpec->GetRawProps = arc->GetRawProps; 620 updateCallbackSpec->GetRootProps = arc->GetRootProps; 621 } 622 623 updateCallbackSpec->DirItems = &dirItems; 624 updateCallbackSpec->ParentDirItem = parentDirItem; 625 626 updateCallbackSpec->StoreNtSecurity = options.NtSecurity.Val; 627 updateCallbackSpec->StoreHardLinks = options.HardLinks.Val; 628 updateCallbackSpec->StoreSymLinks = options.SymLinks.Val; 629 630 updateCallbackSpec->ArcItems = &arcItems; 631 updateCallbackSpec->UpdatePairs = &updatePairs2; 632 633 updateCallbackSpec->ProcessedItemsStatuses = processedItemsStatuses; 634 635 if (options.RenamePairs.Size() != 0) 636 updateCallbackSpec->NewNames = &newNames; 637 638 CMyComPtr<IOutStream> outSeekStream; 639 CMyComPtr<ISequentialOutStream> outStream; 640 641 if (!options.StdOutMode) 642 { 643 FString dirPrefix; 644 if (!GetOnlyDirPrefix(us2fs(archivePath.GetFinalPath()), dirPrefix)) 645 throw 1417161; 646 CreateComplexDir(dirPrefix); 647 } 648 649 COutFileStream *outStreamSpec = NULL; 650 COutMultiVolStream *volStreamSpec = NULL; 651 652 if (options.VolumesSizes.Size() == 0) 653 { 654 if (options.StdOutMode) 655 outStream = new CStdOutFileStream; 656 else 657 { 658 outStreamSpec = new COutFileStream; 659 outSeekStream = outStreamSpec; 660 outStream = outSeekStream; 661 bool isOK = false; 662 FString realPath; 663 for (int i = 0; i < (1 << 16); i++) 664 { 665 if (archivePath.Temp) 666 { 667 if (i > 0) 668 { 669 FChar s[16]; 670 ConvertUInt32ToString(i, s); 671 archivePath.TempPostfix = s; 672 } 673 realPath = archivePath.GetTempPath(); 674 } 675 else 676 realPath = us2fs(archivePath.GetFinalPath()); 677 if (outStreamSpec->Create(realPath, false)) 678 { 679 tempFiles.Paths.Add(realPath); 680 isOK = true; 681 break; 682 } 683 if (::GetLastError() != ERROR_FILE_EXISTS) 684 break; 685 if (!archivePath.Temp) 686 break; 687 } 688 if (!isOK) 689 { 690 errorInfo.SystemError = ::GetLastError(); 691 errorInfo.FileName = realPath; 692 errorInfo.Message = L"7-Zip cannot open file"; 693 return E_FAIL; 694 } 695 } 696 } 697 else 698 { 699 if (options.StdOutMode) 700 return E_FAIL; 701 if (arc && arc->GetGlobalOffset() > 0) 702 return E_NOTIMPL; 703 704 volStreamSpec = new COutMultiVolStream; 705 outSeekStream = volStreamSpec; 706 outStream = outSeekStream; 707 volStreamSpec->Sizes = options.VolumesSizes; 708 volStreamSpec->Prefix = us2fs(archivePath.GetFinalVolPath() + L"."); 709 volStreamSpec->TempFiles = &tempFiles; 710 volStreamSpec->Init(); 711 712 /* 713 updateCallbackSpec->VolumesSizes = volumesSizes; 714 updateCallbackSpec->VolName = archivePath.Prefix + archivePath.Name; 715 if (!archivePath.VolExtension.IsEmpty()) 716 updateCallbackSpec->VolExt = UString(L'.') + archivePath.VolExtension; 717 */ 718 } 719 720 RINOK(SetProperties(outArchive, options.MethodMode.Properties)); 721 722 if (options.SfxMode) 723 { 724 CInFileStream *sfxStreamSpec = new CInFileStream; 725 CMyComPtr<IInStream> sfxStream(sfxStreamSpec); 726 if (!sfxStreamSpec->Open(options.SfxModule)) 727 { 728 errorInfo.SystemError = ::GetLastError(); 729 errorInfo.Message = L"7-Zip cannot open SFX module"; 730 errorInfo.FileName = options.SfxModule; 731 return E_FAIL; 732 } 733 734 CMyComPtr<ISequentialOutStream> sfxOutStream; 735 COutFileStream *outStreamSpec = NULL; 736 if (options.VolumesSizes.Size() == 0) 737 sfxOutStream = outStream; 738 else 739 { 740 outStreamSpec = new COutFileStream; 741 sfxOutStream = outStreamSpec; 742 FString realPath = us2fs(archivePath.GetFinalPath()); 743 if (!outStreamSpec->Create(realPath, false)) 744 { 745 errorInfo.SystemError = ::GetLastError(); 746 errorInfo.FileName = realPath; 747 errorInfo.Message = L"7-Zip cannot open file"; 748 return E_FAIL; 749 } 750 } 751 RINOK(NCompress::CopyStream(sfxStream, sfxOutStream, NULL)); 752 if (outStreamSpec) 753 { 754 RINOK(outStreamSpec->Close()); 755 } 756 } 757 758 CMyComPtr<ISequentialOutStream> tailStream; 759 760 if (options.SfxMode || !arc || arc->ArcStreamOffset == 0) 761 tailStream = outStream; 762 else 763 { 764 // Int64 globalOffset = arc->GetGlobalOffset(); 765 RINOK(arc->InStream->Seek(0, STREAM_SEEK_SET, NULL)); 766 RINOK(NCompress::CopyStream_ExactSize(arc->InStream, outStream, arc->ArcStreamOffset, NULL)); 767 if (options.StdOutMode) 768 tailStream = outStream; 769 else 770 { 771 CTailOutStream *tailStreamSpec = new CTailOutStream; 772 tailStream = tailStreamSpec; 773 tailStreamSpec->Stream = outSeekStream; 774 tailStreamSpec->Offset = arc->ArcStreamOffset; 775 tailStreamSpec->Init(); 776 } 777 } 778 779 780 HRESULT result = outArchive->UpdateItems(tailStream, updatePairs2.Size(), updateCallback); 781 callback->Finilize(); 782 RINOK(result); 783 784 785 if (options.SetArcMTime) 786 { 787 FILETIME ft; 788 ft.dwLowDateTime = 0; 789 ft.dwHighDateTime = 0; 790 FOR_VECTOR (i, updatePairs2) 791 { 792 CUpdatePair2 &pair2 = updatePairs2[i]; 793 const FILETIME *ft2 = NULL; 794 if (pair2.NewProps && pair2.DirIndex >= 0) 795 ft2 = &dirItems.Items[pair2.DirIndex].MTime; 796 else if (pair2.UseArcProps && pair2.ArcIndex >= 0) 797 ft2 = &arcItems[pair2.ArcIndex].MTime; 798 if (ft2) 799 { 800 if (::CompareFileTime(&ft, ft2) < 0) 801 ft = *ft2; 802 } 803 } 804 if (ft.dwLowDateTime != 0 || ft.dwHighDateTime != 0) 805 { 806 if (outStreamSpec) 807 outStreamSpec->SetMTime(&ft); 808 else if (volStreamSpec) 809 volStreamSpec->SetMTime(&ft);; 810 } 811 } 812 813 if (outStreamSpec) 814 result = outStreamSpec->Close(); 815 else if (volStreamSpec) 816 result = volStreamSpec->Close(); 817 return result; 818 } 819 820 static HRESULT EnumerateInArchiveItems( 821 // bool storeStreamsMode, 822 const NWildcard::CCensor &censor, 823 const CArc &arc, 824 CObjectVector<CArcItem> &arcItems) 825 { 826 arcItems.Clear(); 827 UInt32 numItems; 828 IInArchive *archive = arc.Archive; 829 RINOK(archive->GetNumberOfItems(&numItems)); 830 arcItems.ClearAndReserve(numItems); 831 for (UInt32 i = 0; i < numItems; i++) 832 { 833 CArcItem ai; 834 835 RINOK(arc.GetItemPath(i, ai.Name)); 836 RINOK(Archive_IsItem_Folder(archive, i, ai.IsDir)); 837 RINOK(Archive_IsItem_AltStream(archive, i, ai.IsAltStream)); 838 /* 839 if (!storeStreamsMode && ai.IsAltStream) 840 continue; 841 */ 842 ai.Censored = censor.CheckPath(ai.IsAltStream, ai.Name, !ai.IsDir); 843 RINOK(arc.GetItemMTime(i, ai.MTime, ai.MTimeDefined)); 844 RINOK(arc.GetItemSize(i, ai.Size, ai.SizeDefined)); 845 846 { 847 CPropVariant prop; 848 RINOK(archive->GetProperty(i, kpidTimeType, &prop)); 849 if (prop.vt == VT_UI4) 850 { 851 ai.TimeType = (int)(NFileTimeType::EEnum)prop.ulVal; 852 switch (ai.TimeType) 853 { 854 case NFileTimeType::kWindows: 855 case NFileTimeType::kUnix: 856 case NFileTimeType::kDOS: 857 break; 858 default: 859 return E_FAIL; 860 } 861 } 862 } 863 864 ai.IndexInServer = i; 865 arcItems.AddInReserved(ai); 866 } 867 return S_OK; 868 } 869 870 struct CEnumDirItemUpdateCallback: public IEnumDirItemCallback 871 { 872 IUpdateCallbackUI2 *Callback; 873 HRESULT ScanProgress(UInt64 numFolders, UInt64 numFiles, UInt64 totalSize, const wchar_t *path, bool isDir) 874 { 875 return Callback->ScanProgress(numFolders, numFiles, totalSize, path, isDir); 876 } 877 }; 878 879 #if defined(_WIN32) && !defined(UNDER_CE) 880 881 #include <mapi.h> 882 883 #endif 884 885 struct CRefSortPair 886 { 887 int Len; 888 int Index; 889 }; 890 891 #define RINOZ(x) { int __tt = (x); if (__tt != 0) return __tt; } 892 893 static int CompareRefSortPair(const CRefSortPair *a1, const CRefSortPair *a2, void *) 894 { 895 RINOZ(-MyCompare(a1->Len, a2->Len)); 896 return MyCompare(a1->Index, a2->Index); 897 } 898 899 static int GetNumSlashes(const FChar *s) 900 { 901 for (int numSlashes = 0;;) 902 { 903 FChar c = *s++; 904 if (c == 0) 905 return numSlashes; 906 if ( 907 #ifdef _WIN32 908 c == FTEXT('\\') || 909 #endif 910 c == FTEXT('/')) 911 numSlashes++; 912 } 913 } 914 915 #ifdef _WIN32 916 void ConvertToLongNames(NWildcard::CCensor &censor); 917 #endif 918 919 HRESULT UpdateArchive( 920 CCodecs *codecs, 921 const CObjectVector<COpenType> &types, 922 const UString &cmdArcPath2, 923 NWildcard::CCensor &censor, 924 CUpdateOptions &options, 925 CUpdateErrorInfo &errorInfo, 926 IOpenCallbackUI *openCallback, 927 IUpdateCallbackUI2 *callback, 928 bool needSetPath) 929 { 930 if (options.StdOutMode && options.EMailMode) 931 return E_FAIL; 932 933 if (types.Size() > 1) 934 return E_NOTIMPL; 935 936 bool renameMode = !options.RenamePairs.IsEmpty(); 937 if (renameMode) 938 { 939 if (options.Commands.Size() != 1) 940 return E_FAIL; 941 } 942 943 if (options.DeleteAfterCompressing) 944 { 945 if (options.Commands.Size() != 1) 946 return E_NOTIMPL; 947 const CActionSet &as = options.Commands[0].ActionSet; 948 for (int i = 2; i < NPairState::kNumValues; i++) 949 if (as.StateActions[i] != NPairAction::kCompress) 950 return E_NOTIMPL; 951 } 952 953 censor.AddPathsToCensor(options.PathMode); 954 #ifdef _WIN32 955 ConvertToLongNames(censor); 956 #endif 957 censor.ExtendExclude(); 958 959 960 if (options.VolumesSizes.Size() > 0 && (options.EMailMode /* || options.SfxMode */)) 961 return E_NOTIMPL; 962 963 if (options.SfxMode) 964 { 965 CProperty property; 966 property.Name = L"rsfx"; 967 property.Value = L"on"; 968 options.MethodMode.Properties.Add(property); 969 if (options.SfxModule.IsEmpty()) 970 { 971 errorInfo.Message = L"SFX file is not specified"; 972 return E_FAIL; 973 } 974 bool found = false; 975 if (options.SfxModule.Find(FCHAR_PATH_SEPARATOR) < 0) 976 { 977 const FString fullName = NDLL::GetModuleDirPrefix() + options.SfxModule; 978 if (NFind::DoesFileExist(fullName)) 979 { 980 options.SfxModule = fullName; 981 found = true; 982 } 983 } 984 if (!found) 985 { 986 if (!NFind::DoesFileExist(options.SfxModule)) 987 { 988 errorInfo.SystemError = ::GetLastError(); 989 errorInfo.Message = L"7-Zip cannot find specified SFX module"; 990 errorInfo.FileName = options.SfxModule; 991 return E_FAIL; 992 } 993 } 994 } 995 996 CArchiveLink arcLink; 997 998 999 if (needSetPath) 1000 { 1001 if (!options.InitFormatIndex(codecs, types, cmdArcPath2) || 1002 !options.SetArcPath(codecs, cmdArcPath2)) 1003 return E_NOTIMPL; 1004 } 1005 UString arcPath = options.ArchivePath.GetFinalPath(); 1006 1007 if (cmdArcPath2.IsEmpty()) 1008 { 1009 if (options.MethodMode.Type.FormatIndex < 0) 1010 throw "type of archive is not specified"; 1011 } 1012 else 1013 { 1014 NFind::CFileInfo fi; 1015 if (!fi.Find(us2fs(arcPath))) 1016 { 1017 if (renameMode) 1018 throw "can't find archive";; 1019 if (options.MethodMode.Type.FormatIndex < 0) 1020 { 1021 if (!options.SetArcPath(codecs, cmdArcPath2)) 1022 return E_NOTIMPL; 1023 } 1024 } 1025 else 1026 { 1027 if (fi.IsDir()) 1028 throw "there is no such archive"; 1029 if (fi.IsDevice) 1030 return E_NOTIMPL; 1031 if (options.VolumesSizes.Size() > 0) 1032 return E_NOTIMPL; 1033 CObjectVector<COpenType> types; 1034 // change it. 1035 if (options.MethodMode.Type_Defined) 1036 types.Add(options.MethodMode.Type); 1037 // We need to set Properties to open archive only in some cases (WIM archives). 1038 1039 CIntVector excl; 1040 COpenOptions op; 1041 #ifndef _SFX 1042 op.props = &options.MethodMode.Properties; 1043 #endif 1044 op.codecs = codecs; 1045 op.types = &types; 1046 op.excludedFormats = ! 1047 op.stdInMode = false; 1048 op.stream = NULL; 1049 op.filePath = arcPath; 1050 1051 HRESULT result = arcLink.Open2(op, openCallback); 1052 1053 if (result == E_ABORT) 1054 return result; 1055 1056 const wchar_t *errorArcType = NULL; 1057 if (arcLink.NonOpen_ErrorInfo.ErrorFormatIndex > 0) 1058 errorArcType = codecs->Formats[arcLink.NonOpen_ErrorInfo.ErrorFormatIndex].Name; 1059 RINOK(callback->OpenResult(arcPath, result, errorArcType)); 1060 /* 1061 if (result == S_FALSE) 1062 return E_FAIL; 1063 */ 1064 RINOK(result); 1065 if (arcLink.VolumePaths.Size() > 1) 1066 { 1067 errorInfo.SystemError = (DWORD)E_NOTIMPL; 1068 errorInfo.Message = L"Updating for multivolume archives is not implemented"; 1069 return E_NOTIMPL; 1070 } 1071 1072 CArc &arc = arcLink.Arcs.Back(); 1073 arc.MTimeDefined = !fi.IsDevice; 1074 arc.MTime = fi.MTime; 1075 1076 if (arc.ErrorInfo.ThereIsTail) 1077 { 1078 errorInfo.SystemError = (DWORD)E_NOTIMPL; 1079 errorInfo.Message = L"There is some data block after the end of the archive"; 1080 return E_NOTIMPL; 1081 } 1082 if (options.MethodMode.Type.FormatIndex < 0) 1083 { 1084 options.MethodMode.Type.FormatIndex = arcLink.GetArc()->FormatIndex; 1085 if (!options.SetArcPath(codecs, cmdArcPath2)) 1086 return E_NOTIMPL; 1087 } 1088 } 1089 } 1090 1091 if (options.MethodMode.Type.FormatIndex < 0) 1092 { 1093 options.MethodMode.Type.FormatIndex = codecs->FindFormatForArchiveType(kDefaultArcType); 1094 if (options.MethodMode.Type.FormatIndex < 0) 1095 return E_NOTIMPL; 1096 } 1097 1098 bool thereIsInArchive = arcLink.IsOpen; 1099 if (!thereIsInArchive && renameMode) 1100 return E_FAIL; 1101 1102 CDirItems dirItems; 1103 CDirItem parentDirItem; 1104 CDirItem *parentDirItem_Ptr = NULL; 1105 1106 /* 1107 FStringVector requestedPaths; 1108 FStringVector *requestedPaths_Ptr = NULL; 1109 if (options.DeleteAfterCompressing) 1110 requestedPaths_Ptr = &requestedPaths; 1111 */ 1112 1113 if (options.StdInMode) 1114 { 1115 CDirItem di; 1116 di.Name = options.StdInFileName; 1117 di.Size = (UInt64)(Int64)-1; 1118 di.Attrib = 0; 1119 NTime::GetCurUtcFileTime(di.MTime); 1120 di.CTime = di.ATime = di.MTime; 1121 dirItems.Items.Add(di); 1122 } 1123 else 1124 { 1125 bool needScanning = false; 1126 if (!renameMode) 1127 FOR_VECTOR (i, options.Commands) 1128 if (options.Commands[i].ActionSet.NeedScanning()) 1129 needScanning = true; 1130 if (needScanning) 1131 { 1132 CEnumDirItemUpdateCallback enumCallback; 1133 enumCallback.Callback = callback; 1134 RINOK(callback->StartScanning()); 1135 1136 dirItems.SymLinks = options.SymLinks.Val; 1137 1138 #if defined(_WIN32) && !defined(UNDER_CE) 1139 dirItems.ReadSecure = options.NtSecurity.Val; 1140 #endif 1141 1142 dirItems.ScanAltStreams = options.AltStreams.Val; 1143 HRESULT res = EnumerateItems(censor, 1144 options.PathMode, 1145 options.AddPathPrefix, 1146 dirItems, &enumCallback); 1147 FOR_VECTOR (i, dirItems.ErrorPaths) 1148 { 1149 RINOK(callback->CanNotFindError(fs2us(dirItems.ErrorPaths[i]), dirItems.ErrorCodes[i])); 1150 } 1151 if (res != S_OK) 1152 { 1153 if (res != E_ABORT) 1154 errorInfo.Message = L"Scanning error"; 1155 return res; 1156 } 1157 RINOK(callback->FinishScanning()); 1158 1159 if (censor.Pairs.Size() == 1) 1160 { 1161 NFind::CFileInfo fi; 1162 FString prefix = us2fs(censor.Pairs[0].Prefix) + FTEXT("."); 1163 // UString prefix = censor.Pairs[0].Prefix; 1164 /* 1165 if (prefix.Back() == WCHAR_PATH_SEPARATOR) 1166 { 1167 prefix.DeleteBack(); 1168 } 1169 */ 1170 if (fi.Find(prefix)) 1171 if (fi.IsDir()) 1172 { 1173 parentDirItem.Size = fi.Size; 1174 parentDirItem.CTime = fi.CTime; 1175 parentDirItem.ATime = fi.ATime; 1176 parentDirItem.MTime = fi.MTime; 1177 parentDirItem.Attrib = fi.Attrib; 1178 parentDirItem_Ptr = &parentDirItem; 1179 1180 int secureIndex = -1; 1181 #if defined(_WIN32) && !defined(UNDER_CE) 1182 if (options.NtSecurity.Val) 1183 dirItems.AddSecurityItem(prefix, secureIndex); 1184 #endif 1185 parentDirItem.SecureIndex = secureIndex; 1186 1187 parentDirItem_Ptr = &parentDirItem; 1188 } 1189 } 1190 } 1191 } 1192 1193 FString tempDirPrefix; 1194 bool usesTempDir = false; 1195 1196 #ifdef _WIN32 1197 CTempDir tempDirectory; 1198 if (options.EMailMode && options.EMailRemoveAfter) 1199 { 1200 tempDirectory.Create(kTempFolderPrefix); 1201 tempDirPrefix = tempDirectory.GetPath(); 1202 NormalizeDirPathPrefix(tempDirPrefix); 1203 usesTempDir = true; 1204 } 1205 #endif 1206 1207 CTempFiles tempFiles; 1208 1209 bool createTempFile = false; 1210 1211 if (!options.StdOutMode && options.UpdateArchiveItself) 1212 { 1213 CArchivePath &ap = options.Commands[0].ArchivePath; 1214 ap = options.ArchivePath; 1215 // if ((archive != 0 && !usesTempDir) || !options.WorkingDir.IsEmpty()) 1216 if ((thereIsInArchive || !options.WorkingDir.IsEmpty()) && !usesTempDir && options.VolumesSizes.Size() == 0) 1217 { 1218 createTempFile = true; 1219 ap.Temp = true; 1220 if (!options.WorkingDir.IsEmpty()) 1221 ap.TempPrefix = options.WorkingDir; 1222 else 1223 ap.TempPrefix = us2fs(ap.Prefix); 1224 NormalizeDirPathPrefix(ap.TempPrefix); 1225 } 1226 } 1227 1228 unsigned i; 1229 for (i = 0; i < options.Commands.Size(); i++) 1230 { 1231 CArchivePath &ap = options.Commands[i].ArchivePath; 1232 if (usesTempDir) 1233 { 1234 // Check it 1235 ap.Prefix = fs2us(tempDirPrefix); 1236 // ap.Temp = true; 1237 // ap.TempPrefix = tempDirPrefix; 1238 } 1239 if (!options.StdOutMode && 1240 (i > 0 || !createTempFile)) 1241 { 1242 const FString path = us2fs(ap.GetFinalPath()); 1243 if (NFind::DoesFileOrDirExist(path)) 1244 { 1245 errorInfo.SystemError = 0; 1246 errorInfo.Message = L"The file already exists"; 1247 errorInfo.FileName = path; 1248 return E_FAIL; 1249 } 1250 } 1251 } 1252 1253 CObjectVector<CArcItem> arcItems; 1254 if (thereIsInArchive) 1255 { 1256 RINOK(EnumerateInArchiveItems( 1257 // options.StoreAltStreams, 1258 censor, arcLink.Arcs.Back(), arcItems)); 1259 } 1260 1261 /* 1262 FStringVector processedFilePaths; 1263 FStringVector *processedFilePaths_Ptr = NULL; 1264 if (options.DeleteAfterCompressing) 1265 processedFilePaths_Ptr = &processedFilePaths; 1266 */ 1267 1268 CByteBuffer processedItems; 1269 if (options.DeleteAfterCompressing) 1270 { 1271 unsigned num = dirItems.Items.Size(); 1272 processedItems.Alloc(num); 1273 for (i = 0; i < num; i++) 1274 processedItems[i] = 0; 1275 } 1276 1277 for (i = 0; i < options.Commands.Size(); i++) 1278 { 1279 const CArc *arc = thereIsInArchive ? arcLink.GetArc() : 0; 1280 // IInArchiveExtra *archiveExtra = thereIsInArchive ? arcLink.GetArchiveExtra() : 0; 1281 // IArchiveGetRootProps *archiveGetRootProps = thereIsInArchive ? arcLink.GetArchiveGetRootProps() : 0; 1282 CUpdateArchiveCommand &command = options.Commands[i]; 1283 UString name; 1284 bool isUpdating; 1285 if (options.StdOutMode) 1286 { 1287 name = L"stdout"; 1288 isUpdating = arc != 0; 1289 } 1290 else 1291 { 1292 name = command.ArchivePath.GetFinalPath(); 1293 isUpdating = (i == 0 && options.UpdateArchiveItself && arc != 0); 1294 } 1295 RINOK(callback->StartArchive(name, isUpdating)) 1296 1297 RINOK(Compress(options, 1298 codecs, 1299 command.ActionSet, 1300 arc, 1301 command.ArchivePath, 1302 arcItems, 1303 options.DeleteAfterCompressing ? (Byte *)processedItems : NULL, 1304 1305 dirItems, 1306 parentDirItem_Ptr, 1307 1308 tempFiles, 1309 errorInfo, callback)); 1310 1311 RINOK(callback->FinishArchive()); 1312 } 1313 1314 1315 if (thereIsInArchive) 1316 { 1317 RINOK(arcLink.Close()); 1318 arcLink.Release(); 1319 } 1320 1321 tempFiles.Paths.Clear(); 1322 if (createTempFile) 1323 { 1324 try 1325 { 1326 CArchivePath &ap = options.Commands[0].ArchivePath; 1327 const FString &tempPath = ap.GetTempPath(); 1328 if (thereIsInArchive) 1329 if (!DeleteFileAlways(us2fs(arcPath))) 1330 { 1331 errorInfo.SystemError = ::GetLastError(); 1332 errorInfo.Message = L"7-Zip cannot delete the file"; 1333 errorInfo.FileName = us2fs(arcPath); 1334 return E_FAIL; 1335 } 1336 if (!MyMoveFile(tempPath, us2fs(arcPath))) 1337 { 1338 errorInfo.SystemError = ::GetLastError(); 1339 errorInfo.Message = L"7-Zip cannot move the file"; 1340 errorInfo.FileName = tempPath; 1341 errorInfo.FileName2 = us2fs(arcPath); 1342 return E_FAIL; 1343 } 1344 } 1345 catch(...) 1346 { 1347 throw; 1348 } 1349 } 1350 1351 1352 #if defined(_WIN32) && !defined(UNDER_CE) 1353 if (options.EMailMode) 1354 { 1355 NDLL::CLibrary mapiLib; 1356 if (!mapiLib.Load(FTEXT("Mapi32.dll"))) 1357 { 1358 errorInfo.SystemError = ::GetLastError(); 1359 errorInfo.Message = L"7-Zip cannot load Mapi32.dll"; 1360 return E_FAIL; 1361 } 1362 1363 /* 1364 LPMAPISENDDOCUMENTS fnSend = (LPMAPISENDDOCUMENTS)mapiLib.GetProc("MAPISendDocuments"); 1365 if (fnSend == 0) 1366 { 1367 errorInfo.SystemError = ::GetLastError(); 1368 errorInfo.Message = L"7-Zip cannot find MAPISendDocuments function"; 1369 return E_FAIL; 1370 } 1371 */ 1372 LPMAPISENDMAIL sendMail = (LPMAPISENDMAIL)mapiLib.GetProc("MAPISendMail"); 1373 if (sendMail == 0) 1374 { 1375 errorInfo.SystemError = ::GetLastError(); 1376 errorInfo.Message = L"7-Zip cannot find MAPISendMail function"; 1377 return E_FAIL; 1378 } 1379 1380 FStringVector fullPaths; 1381 unsigned i; 1382 for (i = 0; i < options.Commands.Size(); i++) 1383 { 1384 CArchivePath &ap = options.Commands[i].ArchivePath; 1385 FString arcPath; 1386 if (!MyGetFullPathName(us2fs(ap.GetFinalPath()), arcPath)) 1387 { 1388 errorInfo.SystemError = ::GetLastError(); 1389 errorInfo.Message = L"GetFullPathName error"; 1390 return E_FAIL; 1391 } 1392 fullPaths.Add(arcPath); 1393 } 1394 CCurrentDirRestorer curDirRestorer; 1395 for (i = 0; i < fullPaths.Size(); i++) 1396 { 1397 UString arcPath = fs2us(fullPaths[i]); 1398 UString fileName = ExtractFileNameFromPath(arcPath); 1399 AString path = GetAnsiString(arcPath); 1400 AString name = GetAnsiString(fileName); 1401 // Warning!!! MAPISendDocuments function changes Current directory 1402 // fnSend(0, ";", (LPSTR)(LPCSTR)path, (LPSTR)(LPCSTR)name, 0); 1403 1404 MapiFileDesc f; 1405 memset(&f, 0, sizeof(f)); 1406 f.nPosition = 0xFFFFFFFF; 1407 f.lpszPathName = (char *)(const char *)path; 1408 f.lpszFileName = (char *)(const char *)name; 1409 1410 MapiMessage m; 1411 memset(&m, 0, sizeof(m)); 1412 m.nFileCount = 1; 1413 m.lpFiles = &f; 1414 1415 const AString addr = GetAnsiString(options.EMailAddress); 1416 MapiRecipDesc rec; 1417 if (!addr.IsEmpty()) 1418 { 1419 memset(&rec, 0, sizeof(rec)); 1420 rec.ulRecipClass = MAPI_TO; 1421 rec.lpszAddress = (char *)(const char *)addr; 1422 m.nRecipCount = 1; 1423 m.lpRecips = &rec; 1424 } 1425 1426 sendMail((LHANDLE)0, 0, &m, MAPI_DIALOG, 0); 1427 } 1428 } 1429 #endif 1430 1431 if (options.DeleteAfterCompressing) 1432 { 1433 CRecordVector<CRefSortPair> pairs; 1434 FStringVector foldersNames; 1435 for (i = 0; i < dirItems.Items.Size(); i++) 1436 { 1437 const CDirItem &dirItem = dirItems.Items[i]; 1438 FString phyPath = us2fs(dirItems.GetPhyPath(i)); 1439 if (dirItem.IsDir()) 1440 { 1441 CRefSortPair pair; 1442 pair.Index = i; 1443 pair.Len = GetNumSlashes(phyPath); 1444 pairs.Add(pair); 1445 } 1446 else 1447 { 1448 if (processedItems[i] != 0 || dirItem.Size == 0) 1449 { 1450 DeleteFileAlways(phyPath); 1451 } 1452 else 1453 { 1454 // file was skipped 1455 /* 1456 errorInfo.SystemError = 0; 1457 errorInfo.Message = L"file was not processed"; 1458 errorInfo.FileName = phyPath; 1459 return E_FAIL; 1460 */ 1461 } 1462 } 1463 } 1464 1465 pairs.Sort(CompareRefSortPair, NULL); 1466 for (i = 0; i < pairs.Size(); i++) 1467 { 1468 FString phyPath = us2fs(dirItems.GetPhyPath(pairs[i].Index)); 1469 if (NFind::DoesDirExist(phyPath)) 1470 { 1471 // printf("delete %S\n", phyPath); 1472 RemoveDir(phyPath); 1473 } 1474 } 1475 } 1476 return S_OK; 1477 } 1478