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