1// Update.cpp 2 3#include "StdAfx.h" 4 5// #include <stdio.h> 6 7#include "Update.h" 8 9#include "../../../Common/StringConvert.h" 10 11#include "../../../Windows/DLL.h" 12#include "../../../Windows/FileDir.h" 13#include "../../../Windows/FileFind.h" 14#include "../../../Windows/FileName.h" 15#include "../../../Windows/PropVariant.h" 16#include "../../../Windows/PropVariantConv.h" 17#include "../../../Windows/TimeUtils.h" 18 19#include "../../Common/FileStreams.h" 20#include "../../Common/LimitedStreams.h" 21#include "../../Common/MultiOutStream.h" 22#include "../../Common/StreamUtils.h" 23 24#include "../../Compress/CopyCoder.h" 25 26#include "../Common/DirItem.h" 27#include "../Common/EnumDirItems.h" 28#include "../Common/OpenArchive.h" 29#include "../Common/UpdateProduce.h" 30 31#include "EnumDirItems.h" 32#include "SetProperties.h" 33#include "TempFiles.h" 34#include "UpdateCallback.h" 35 36static const char * const kUpdateIsNotSupoorted = 37 "update operations are not supported for this archive"; 38 39static const char * const kUpdateIsNotSupported_MultiVol = 40 "Updating for multivolume archives is not implemented"; 41 42using namespace NWindows; 43using namespace NCOM; 44using namespace NFile; 45using namespace NDir; 46using namespace NName; 47 48#ifdef _WIN32 49static CFSTR const kTempFolderPrefix = FTEXT("7zE"); 50#endif 51 52void CUpdateErrorInfo::SetFromLastError(const char *message) 53{ 54 SystemError = ::GetLastError(); 55 Message = message; 56} 57 58HRESULT CUpdateErrorInfo::SetFromLastError(const char *message, const FString &fileName) 59{ 60 SetFromLastError(message); 61 FileNames.Add(fileName); 62 return Get_HRESULT_Error(); 63} 64 65HRESULT CUpdateErrorInfo::SetFromError_DWORD(const char *message, const FString &fileName, DWORD error) 66{ 67 Message = message; 68 FileNames.Add(fileName); 69 SystemError = error; 70 return Get_HRESULT_Error(); 71} 72 73 74using namespace NUpdateArchive; 75 76struct CMultiOutStream_Rec 77{ 78 CMultiOutStream *Spec; 79 CMyComPtr<IOutStream> Ref; 80}; 81 82struct CMultiOutStream_Bunch 83{ 84 CObjectVector<CMultiOutStream_Rec> Items; 85 86 HRESULT Destruct() 87 { 88 HRESULT hres = S_OK; 89 FOR_VECTOR (i, Items) 90 { 91 CMultiOutStream_Rec &rec = Items[i]; 92 if (rec.Ref) 93 { 94 const HRESULT hres2 = rec.Spec->Destruct(); 95 if (hres == S_OK) 96 hres = hres2; 97 } 98 } 99 Items.Clear(); 100 return hres; 101 } 102 103 void DisableDeletion() 104 { 105 FOR_VECTOR (i, Items) 106 { 107 CMultiOutStream_Rec &rec = Items[i]; 108 if (rec.Ref) 109 rec.Spec->NeedDelete = false; 110 } 111 } 112}; 113 114 115void CArchivePath::ParseFromPath(const UString &path, EArcNameMode mode) 116{ 117 OriginalPath = path; 118 119 SplitPathToParts_2(path, Prefix, Name); 120 121 if (mode == k_ArcNameMode_Add) 122 return; 123 124 if (mode != k_ArcNameMode_Exact) 125 { 126 int dotPos = Name.ReverseFind_Dot(); 127 if (dotPos < 0) 128 return; 129 if ((unsigned)dotPos == Name.Len() - 1) 130 Name.DeleteBack(); 131 else 132 { 133 const UString ext = Name.Ptr((unsigned)(dotPos + 1)); 134 if (BaseExtension.IsEqualTo_NoCase(ext)) 135 { 136 BaseExtension = ext; 137 Name.DeleteFrom((unsigned)dotPos); 138 return; 139 } 140 } 141 } 142 143 BaseExtension.Empty(); 144} 145 146UString CArchivePath::GetFinalPath() const 147{ 148 UString path = GetPathWithoutExt(); 149 if (!BaseExtension.IsEmpty()) 150 { 151 path.Add_Dot(); 152 path += BaseExtension; 153 } 154 return path; 155} 156 157UString CArchivePath::GetFinalVolPath() const 158{ 159 UString path = GetPathWithoutExt(); 160 // if BaseExtension is empty, we must ignore VolExtension also. 161 if (!BaseExtension.IsEmpty()) 162 { 163 path.Add_Dot(); 164 path += VolExtension; 165 } 166 return path; 167} 168 169FString CArchivePath::GetTempPath() const 170{ 171 FString path = TempPrefix; 172 path += us2fs(Name); 173 if (!BaseExtension.IsEmpty()) 174 { 175 path.Add_Dot(); 176 path += us2fs(BaseExtension); 177 } 178 path += ".tmp"; 179 path += TempPostfix; 180 return path; 181} 182 183static const char * const kDefaultArcType = "7z"; 184static const char * const kDefaultArcExt = "7z"; 185static const char * const kSFXExtension = 186 #ifdef _WIN32 187 "exe"; 188 #else 189 ""; 190 #endif 191 192bool CUpdateOptions::InitFormatIndex(const CCodecs *codecs, 193 const CObjectVector<COpenType> &types, const UString &arcPath) 194{ 195 if (types.Size() > 1) 196 return false; 197 // int arcTypeIndex = -1; 198 if (types.Size() != 0) 199 { 200 MethodMode.Type = types[0]; 201 MethodMode.Type_Defined = true; 202 } 203 if (MethodMode.Type.FormatIndex < 0) 204 { 205 // MethodMode.Type = -1; 206 MethodMode.Type = COpenType(); 207 if (ArcNameMode != k_ArcNameMode_Add) 208 { 209 MethodMode.Type.FormatIndex = codecs->FindFormatForArchiveName(arcPath); 210 if (MethodMode.Type.FormatIndex >= 0) 211 MethodMode.Type_Defined = true; 212 } 213 } 214 return true; 215} 216 217bool CUpdateOptions::SetArcPath(const CCodecs *codecs, const UString &arcPath) 218{ 219 UString typeExt; 220 int formatIndex = MethodMode.Type.FormatIndex; 221 if (formatIndex < 0) 222 { 223 typeExt = kDefaultArcExt; 224 } 225 else 226 { 227 const CArcInfoEx &arcInfo = codecs->Formats[(unsigned)formatIndex]; 228 if (!arcInfo.UpdateEnabled) 229 return false; 230 typeExt = arcInfo.GetMainExt(); 231 } 232 UString ext = typeExt; 233 if (SfxMode) 234 ext = kSFXExtension; 235 ArchivePath.BaseExtension = ext; 236 ArchivePath.VolExtension = typeExt; 237 ArchivePath.ParseFromPath(arcPath, ArcNameMode); 238 FOR_VECTOR (i, Commands) 239 { 240 CUpdateArchiveCommand &uc = Commands[i]; 241 uc.ArchivePath.BaseExtension = ext; 242 uc.ArchivePath.VolExtension = typeExt; 243 uc.ArchivePath.ParseFromPath(uc.UserArchivePath, ArcNameMode); 244 } 245 return true; 246} 247 248 249struct CUpdateProduceCallbackImp Z7_final: public IUpdateProduceCallback 250{ 251 const CObjectVector<CArcItem> *_arcItems; 252 CDirItemsStat *_stat; 253 IUpdateCallbackUI *_callback; 254 255 CUpdateProduceCallbackImp( 256 const CObjectVector<CArcItem> *a, 257 CDirItemsStat *stat, 258 IUpdateCallbackUI *callback): 259 _arcItems(a), 260 _stat(stat), 261 _callback(callback) {} 262 263 virtual HRESULT ShowDeleteFile(unsigned arcIndex) Z7_override; 264}; 265 266 267HRESULT CUpdateProduceCallbackImp::ShowDeleteFile(unsigned arcIndex) 268{ 269 const CArcItem &ai = (*_arcItems)[arcIndex]; 270 { 271 CDirItemsStat &stat = *_stat; 272 if (ai.IsDir) 273 stat.NumDirs++; 274 else if (ai.IsAltStream) 275 { 276 stat.NumAltStreams++; 277 stat.AltStreamsSize += ai.Size; 278 } 279 else 280 { 281 stat.NumFiles++; 282 stat.FilesSize += ai.Size; 283 } 284 } 285 return _callback->ShowDeleteFile(ai.Name, ai.IsDir); 286} 287 288bool CRenamePair::Prepare() 289{ 290 if (RecursedType != NRecursedType::kNonRecursed) 291 return false; 292 if (!WildcardParsing) 293 return true; 294 return !DoesNameContainWildcard(OldName); 295} 296 297extern bool g_CaseSensitive; 298 299static unsigned CompareTwoNames(const wchar_t *s1, const wchar_t *s2) 300{ 301 for (unsigned i = 0;; i++) 302 { 303 wchar_t c1 = s1[i]; 304 wchar_t c2 = s2[i]; 305 if (c1 == 0 || c2 == 0) 306 return i; 307 if (c1 == c2) 308 continue; 309 if (!g_CaseSensitive && (MyCharUpper(c1) == MyCharUpper(c2))) 310 continue; 311 if (IsPathSepar(c1) && IsPathSepar(c2)) 312 continue; 313 return i; 314 } 315} 316 317bool CRenamePair::GetNewPath(bool isFolder, const UString &src, UString &dest) const 318{ 319 unsigned num = CompareTwoNames(OldName, src); 320 if (OldName[num] == 0) 321 { 322 if (src[num] != 0 && !IsPathSepar(src[num]) && num != 0 && !IsPathSepar(src[num - 1])) 323 return false; 324 } 325 else 326 { 327 // OldName[num] != 0 328 // OldName = "1\1a.txt" 329 // src = "1" 330 331 if (!isFolder 332 || src[num] != 0 333 || !IsPathSepar(OldName[num]) 334 || OldName[num + 1] != 0) 335 return false; 336 } 337 dest = NewName + src.Ptr(num); 338 return true; 339} 340 341#ifdef SUPPORT_ALT_STREAMS 342int FindAltStreamColon_in_Path(const wchar_t *path); 343#endif 344 345 346 347static HRESULT Compress( 348 const CUpdateOptions &options, 349 bool isUpdatingItself, 350 CCodecs *codecs, 351 const CActionSet &actionSet, 352 const CArc *arc, 353 CArchivePath &archivePath, 354 const CObjectVector<CArcItem> &arcItems, 355 Byte *processedItemsStatuses, 356 const CDirItems &dirItems, 357 const CDirItem *parentDirItem, 358 CTempFiles &tempFiles, 359 CMultiOutStream_Bunch &multiStreams, 360 CUpdateErrorInfo &errorInfo, 361 IUpdateCallbackUI *callback, 362 CFinishArchiveStat &st) 363{ 364 CMyComPtr<IOutArchive> outArchive; 365 int formatIndex = options.MethodMode.Type.FormatIndex; 366 367 if (arc) 368 { 369 formatIndex = arc->FormatIndex; 370 if (formatIndex < 0) 371 return E_NOTIMPL; 372 CMyComPtr<IInArchive> archive2 = arc->Archive; 373 HRESULT result = archive2.QueryInterface(IID_IOutArchive, &outArchive); 374 if (result != S_OK) 375 throw kUpdateIsNotSupoorted; 376 } 377 else 378 { 379 RINOK(codecs->CreateOutArchive((unsigned)formatIndex, outArchive)) 380 381 #ifdef Z7_EXTERNAL_CODECS 382 { 383 CMyComPtr<ISetCompressCodecsInfo> setCompressCodecsInfo; 384 outArchive.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo); 385 if (setCompressCodecsInfo) 386 { 387 RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(codecs)) 388 } 389 } 390 #endif 391 } 392 393 if (!outArchive) 394 throw kUpdateIsNotSupoorted; 395 396 // we need to set properties to get fileTimeType. 397 RINOK(SetProperties(outArchive, options.MethodMode.Properties)) 398 399 NFileTimeType::EEnum fileTimeType; 400 { 401 /* 402 how we compare file_in_archive::MTime with dirItem.MTime 403 for GetUpdatePairInfoList(): 404 405 if (kpidMTime is not defined), external MTime of archive is used. 406 407 before 22.00: 408 if (kpidTimeType is defined) 409 { 410 kpidTimeType is used as precision. 411 (kpidTimeType > kDOS) is not allowed. 412 } 413 else GetFileTimeType() value is used as precision. 414 415 22.00: 416 if (kpidMTime is defined) 417 { 418 if (kpidMTime::precision != 0), then kpidMTime::precision is used as precision. 419 else 420 { 421 if (kpidTimeType is defined), kpidTimeType is used as precision. 422 else GetFileTimeType() value is used as precision. 423 } 424 } 425 else external MTime of archive is used as precision. 426 */ 427 428 UInt32 value; 429 RINOK(outArchive->GetFileTimeType(&value)) 430 431 // we support any future fileType here. 432 fileTimeType = (NFileTimeType::EEnum)value; 433 434 /* 435 old 21.07 code: 436 switch (value) 437 { 438 case NFileTimeType::kWindows: 439 case NFileTimeType::kUnix: 440 case NFileTimeType::kDOS: 441 fileTimeType = (NFileTimeType::EEnum)value; 442 break; 443 default: 444 return E_FAIL; 445 } 446 */ 447 } 448 449 // bool noTimestampExpected = false; 450 { 451 const CArcInfoEx &arcInfo = codecs->Formats[(unsigned)formatIndex]; 452 453 // if (arcInfo.Flags_KeepName()) noTimestampExpected = true; 454 if (arcInfo.Is_Xz() || 455 arcInfo.Is_BZip2()) 456 { 457 /* 7-zip before 22.00 returns NFileTimeType::kUnix for xz and bzip2, 458 but we want to set timestamp without reduction to unix. */ 459 // noTimestampExpected = true; 460 fileTimeType = NFileTimeType::kNotDefined; // it means not defined 461 } 462 463 if (options.AltStreams.Val && !arcInfo.Flags_AltStreams()) 464 return E_NOTIMPL; 465 if (options.NtSecurity.Val && !arcInfo.Flags_NtSecurity()) 466 return E_NOTIMPL; 467 if (options.DeleteAfterCompressing && arcInfo.Flags_HashHandler()) 468 return E_NOTIMPL; 469 } 470 471 CRecordVector<CUpdatePair2> updatePairs2; 472 473 UStringVector newNames; 474 475 CArcToDoStat stat2; 476 477 if (options.RenamePairs.Size() != 0) 478 { 479 FOR_VECTOR (i, arcItems) 480 { 481 const CArcItem &ai = arcItems[i]; 482 bool needRename = false; 483 UString dest; 484 485 if (ai.Censored) 486 { 487 FOR_VECTOR (j, options.RenamePairs) 488 { 489 const CRenamePair &rp = options.RenamePairs[j]; 490 if (rp.GetNewPath(ai.IsDir, ai.Name, dest)) 491 { 492 needRename = true; 493 break; 494 } 495 496 #ifdef SUPPORT_ALT_STREAMS 497 if (ai.IsAltStream) 498 { 499 int colonPos = FindAltStreamColon_in_Path(ai.Name); 500 if (colonPos >= 0) 501 { 502 UString mainName = ai.Name.Left((unsigned)colonPos); 503 /* 504 actually we must improve that code to support cases 505 with folder renaming like: rn arc dir1\ dir2\ 506 */ 507 if (rp.GetNewPath(false, mainName, dest)) 508 { 509 needRename = true; 510 dest += ':'; 511 dest += ai.Name.Ptr((unsigned)(colonPos + 1)); 512 break; 513 } 514 } 515 } 516 #endif 517 } 518 } 519 520 CUpdatePair2 up2; 521 up2.SetAs_NoChangeArcItem(ai.IndexInServer); 522 if (needRename) 523 { 524 up2.NewProps = true; 525 RINOK(arc->IsItem_Anti(i, up2.IsAnti)) 526 up2.NewNameIndex = (int)newNames.Add(dest); 527 } 528 updatePairs2.Add(up2); 529 } 530 } 531 else 532 { 533 CRecordVector<CUpdatePair> updatePairs; 534 GetUpdatePairInfoList(dirItems, arcItems, fileTimeType, updatePairs); // must be done only once!!! 535 CUpdateProduceCallbackImp upCallback(&arcItems, &stat2.DeleteData, callback); 536 537 UpdateProduce(updatePairs, actionSet, updatePairs2, isUpdatingItself ? &upCallback : NULL); 538 } 539 540 { 541 FOR_VECTOR (i, updatePairs2) 542 { 543 const CUpdatePair2 &up = updatePairs2[i]; 544 545 // 17.01: anti-item is (up.NewData && (p.UseArcProps in most cases)) 546 547 if (up.NewData && !up.UseArcProps) 548 { 549 if (up.ExistOnDisk()) 550 { 551 CDirItemsStat2 &stat = stat2.NewData; 552 const CDirItem &di = dirItems.Items[(unsigned)up.DirIndex]; 553 if (di.IsDir()) 554 { 555 if (up.IsAnti) 556 stat.Anti_NumDirs++; 557 else 558 stat.NumDirs++; 559 } 560 #ifdef _WIN32 561 else if (di.IsAltStream) 562 { 563 if (up.IsAnti) 564 stat.Anti_NumAltStreams++; 565 else 566 { 567 stat.NumAltStreams++; 568 stat.AltStreamsSize += di.Size; 569 } 570 } 571 #endif 572 else 573 { 574 if (up.IsAnti) 575 stat.Anti_NumFiles++; 576 else 577 { 578 stat.NumFiles++; 579 stat.FilesSize += di.Size; 580 } 581 } 582 } 583 } 584 else if (up.ArcIndex >= 0) 585 { 586 CDirItemsStat2 &stat = *(up.NewData ? &stat2.NewData : &stat2.OldData); 587 const CArcItem &ai = arcItems[(unsigned)up.ArcIndex]; 588 if (ai.IsDir) 589 { 590 if (up.IsAnti) 591 stat.Anti_NumDirs++; 592 else 593 stat.NumDirs++; 594 } 595 else if (ai.IsAltStream) 596 { 597 if (up.IsAnti) 598 stat.Anti_NumAltStreams++; 599 else 600 { 601 stat.NumAltStreams++; 602 stat.AltStreamsSize += ai.Size; 603 } 604 } 605 else 606 { 607 if (up.IsAnti) 608 stat.Anti_NumFiles++; 609 else 610 { 611 stat.NumFiles++; 612 stat.FilesSize += ai.Size; 613 } 614 } 615 } 616 } 617 RINOK(callback->SetNumItems(stat2)) 618 } 619 620 CArchiveUpdateCallback *updateCallbackSpec = new CArchiveUpdateCallback; 621 CMyComPtr<IArchiveUpdateCallback> updateCallback(updateCallbackSpec); 622 623 updateCallbackSpec->PreserveATime = options.PreserveATime; 624 updateCallbackSpec->ShareForWrite = options.OpenShareForWrite; 625 updateCallbackSpec->StopAfterOpenError = options.StopAfterOpenError; 626 updateCallbackSpec->StdInMode = options.StdInMode; 627 updateCallbackSpec->Callback = callback; 628 629 if (arc) 630 { 631 // we set Archive to allow to transfer GetProperty requests back to DLL. 632 updateCallbackSpec->Archive = arc->Archive; 633 } 634 635 updateCallbackSpec->DirItems = &dirItems; 636 updateCallbackSpec->ParentDirItem = parentDirItem; 637 638 updateCallbackSpec->StoreNtSecurity = options.NtSecurity.Val; 639 updateCallbackSpec->StoreHardLinks = options.HardLinks.Val; 640 updateCallbackSpec->StoreSymLinks = options.SymLinks.Val; 641 updateCallbackSpec->StoreOwnerName = options.StoreOwnerName.Val; 642 updateCallbackSpec->StoreOwnerId = options.StoreOwnerId.Val; 643 644 updateCallbackSpec->Arc = arc; 645 updateCallbackSpec->ArcItems = &arcItems; 646 updateCallbackSpec->UpdatePairs = &updatePairs2; 647 648 updateCallbackSpec->ProcessedItemsStatuses = processedItemsStatuses; 649 650 { 651 const UString arcPath = archivePath.GetFinalPath(); 652 updateCallbackSpec->ArcFileName = ExtractFileNameFromPath(arcPath); 653 } 654 655 if (options.RenamePairs.Size() != 0) 656 updateCallbackSpec->NewNames = &newNames; 657 658 if (options.SetArcMTime) 659 { 660 // updateCallbackSpec->Need_ArcMTime_Report = true; 661 updateCallbackSpec->Need_LatestMTime = true; 662 } 663 664 CMyComPtr<IOutStream> outSeekStream; 665 CMyComPtr<ISequentialOutStream> outStream; 666 667 if (!options.StdOutMode) 668 { 669 FString dirPrefix; 670 if (!GetOnlyDirPrefix(us2fs(archivePath.GetFinalPath()), dirPrefix)) 671 throw 1417161; 672 CreateComplexDir(dirPrefix); 673 } 674 675 COutFileStream *outStreamSpec = NULL; 676 CStdOutFileStream *stdOutFileStreamSpec = NULL; 677 CMultiOutStream *volStreamSpec = NULL; 678 679 if (options.VolumesSizes.Size() == 0) 680 { 681 if (options.StdOutMode) 682 { 683 stdOutFileStreamSpec = new CStdOutFileStream; 684 outStream = stdOutFileStreamSpec; 685 } 686 else 687 { 688 outStreamSpec = new COutFileStream; 689 outSeekStream = outStreamSpec; 690 outStream = outSeekStream; 691 bool isOK = false; 692 FString realPath; 693 694 for (unsigned i = 0; i < (1 << 16); i++) 695 { 696 if (archivePath.Temp) 697 { 698 if (i > 0) 699 { 700 archivePath.TempPostfix.Empty(); 701 archivePath.TempPostfix.Add_UInt32(i); 702 } 703 realPath = archivePath.GetTempPath(); 704 } 705 else 706 realPath = us2fs(archivePath.GetFinalPath()); 707 if (outStreamSpec->Create(realPath, false)) 708 { 709 tempFiles.Paths.Add(realPath); 710 isOK = true; 711 break; 712 } 713 if (::GetLastError() != ERROR_FILE_EXISTS) 714 break; 715 if (!archivePath.Temp) 716 break; 717 } 718 719 if (!isOK) 720 return errorInfo.SetFromLastError("cannot open file", realPath); 721 } 722 } 723 else 724 { 725 if (options.StdOutMode) 726 return E_FAIL; 727 if (arc && arc->GetGlobalOffset() > 0) 728 return E_NOTIMPL; 729 730 volStreamSpec = new CMultiOutStream(); 731 outSeekStream = volStreamSpec; 732 outStream = outSeekStream; 733 volStreamSpec->Prefix = us2fs(archivePath.GetFinalVolPath()); 734 volStreamSpec->Prefix.Add_Dot(); 735 volStreamSpec->Init(options.VolumesSizes); 736 { 737 CMultiOutStream_Rec &rec = multiStreams.Items.AddNew(); 738 rec.Spec = volStreamSpec; 739 rec.Ref = rec.Spec; 740 } 741 742 /* 743 updateCallbackSpec->VolumesSizes = volumesSizes; 744 updateCallbackSpec->VolName = archivePath.Prefix + archivePath.Name; 745 if (!archivePath.VolExtension.IsEmpty()) 746 updateCallbackSpec->VolExt = UString('.') + archivePath.VolExtension; 747 */ 748 } 749 750 if (options.SfxMode) 751 { 752 CInFileStream *sfxStreamSpec = new CInFileStream; 753 CMyComPtr<IInStream> sfxStream(sfxStreamSpec); 754 if (!sfxStreamSpec->Open(options.SfxModule)) 755 return errorInfo.SetFromLastError("cannot open SFX module", options.SfxModule); 756 757 CMyComPtr<ISequentialOutStream> sfxOutStream; 758 COutFileStream *outStreamSpec2 = NULL; 759 if (options.VolumesSizes.Size() == 0) 760 sfxOutStream = outStream; 761 else 762 { 763 outStreamSpec2 = new COutFileStream; 764 sfxOutStream = outStreamSpec2; 765 const FString realPath = us2fs(archivePath.GetFinalPath()); 766 if (!outStreamSpec2->Create(realPath, false)) 767 return errorInfo.SetFromLastError("cannot open file", realPath); 768 } 769 770 { 771 UInt64 sfxSize; 772 RINOK(sfxStreamSpec->GetSize(&sfxSize)) 773 RINOK(callback->WriteSfx(fs2us(options.SfxModule), sfxSize)) 774 } 775 776 RINOK(NCompress::CopyStream(sfxStream, sfxOutStream, NULL)) 777 778 if (outStreamSpec2) 779 { 780 RINOK(outStreamSpec2->Close()) 781 } 782 } 783 784 CMyComPtr<ISequentialOutStream> tailStream; 785 786 if (options.SfxMode || !arc || arc->ArcStreamOffset == 0) 787 tailStream = outStream; 788 else 789 { 790 // Int64 globalOffset = arc->GetGlobalOffset(); 791 RINOK(InStream_SeekToBegin(arc->InStream)) 792 RINOK(NCompress::CopyStream_ExactSize(arc->InStream, outStream, arc->ArcStreamOffset, NULL)) 793 if (options.StdOutMode) 794 tailStream = outStream; 795 else 796 { 797 CTailOutStream *tailStreamSpec = new CTailOutStream; 798 tailStream = tailStreamSpec; 799 tailStreamSpec->Stream = outSeekStream; 800 tailStreamSpec->Offset = arc->ArcStreamOffset; 801 tailStreamSpec->Init(); 802 } 803 } 804 805 CFiTime ft; 806 FiTime_Clear(ft); 807 bool ft_Defined = false; 808 { 809 FOR_VECTOR (i, updatePairs2) 810 { 811 const CUpdatePair2 &pair2 = updatePairs2[i]; 812 CFiTime ft2; 813 FiTime_Clear(ft2); 814 bool ft2_Defined = false; 815 /* we use full precision of dirItem, if dirItem is defined 816 and (dirItem will be used or dirItem is sameTime in dir and arc */ 817 if (pair2.DirIndex >= 0 && 818 (pair2.NewProps || pair2.IsSameTime)) 819 { 820 ft2 = dirItems.Items[(unsigned)pair2.DirIndex].MTime; 821 ft2_Defined = true; 822 } 823 else if (pair2.UseArcProps && pair2.ArcIndex >= 0) 824 { 825 const CArcItem &arcItem = arcItems[(unsigned)pair2.ArcIndex]; 826 if (arcItem.MTime.Def) 827 { 828 arcItem.MTime.Write_To_FiTime(ft2); 829 ft2_Defined = true; 830 } 831 } 832 if (ft2_Defined) 833 { 834 if (!ft_Defined || Compare_FiTime(&ft, &ft2) < 0) 835 { 836 ft = ft2; 837 ft_Defined = true; 838 } 839 } 840 } 841 /* 842 if (fileTimeType != NFileTimeType::kNotDefined) 843 FiTime_Normalize_With_Prec(ft, fileTimeType); 844 */ 845 } 846 847 if (volStreamSpec && options.SetArcMTime && ft_Defined) 848 { 849 volStreamSpec->MTime = ft; 850 volStreamSpec->MTime_Defined = true; 851 } 852 853 HRESULT result = outArchive->UpdateItems(tailStream, updatePairs2.Size(), updateCallback); 854 // callback->Finalize(); 855 RINOK(result) 856 857 if (!updateCallbackSpec->AreAllFilesClosed()) 858 { 859 errorInfo.Message = "There are unclosed input file:"; 860 errorInfo.FileNames = updateCallbackSpec->_openFiles_Paths; 861 return E_FAIL; 862 } 863 864 if (options.SetArcMTime) 865 { 866 // bool needNormalizeAfterStream; 867 // needParse; 868 /* 869 if (updateCallbackSpec->ArcMTime_WasReported) 870 { 871 isDefined = updateCallbackSpec->Reported_ArcMTime.Def; 872 if (isDefined) 873 updateCallbackSpec->Reported_ArcMTime.Write_To_FiTime(ft); 874 else 875 fileTimeType = NFileTimeType::kNotDefined; 876 } 877 if (!isDefined) 878 */ 879 { 880 if (updateCallbackSpec->LatestMTime_Defined) 881 { 882 // CArcTime at = StreamCallback_ArcMTime; 883 // updateCallbackSpec->StreamCallback_ArcMTime.Write_To_FiTime(ft); 884 // we must normalize with precision from archive; 885 if (!ft_Defined || Compare_FiTime(&ft, &updateCallbackSpec->LatestMTime) < 0) 886 ft = updateCallbackSpec->LatestMTime; 887 ft_Defined = true; 888 } 889 /* 890 if (fileTimeType != NFileTimeType::kNotDefined) 891 FiTime_Normalize_With_Prec(ft, fileTimeType); 892 */ 893 } 894 // if (ft.dwLowDateTime != 0 || ft.dwHighDateTime != 0) 895 if (ft_Defined) 896 { 897 // we ignore set time errors here. 898 // note that user could move some finished volumes to another folder. 899 if (outStreamSpec) 900 outStreamSpec->SetMTime(&ft); 901 else if (volStreamSpec) 902 volStreamSpec->SetMTime_Final(ft); 903 } 904 } 905 906 if (callback) 907 { 908 UInt64 size = 0; 909 if (outStreamSpec) 910 outStreamSpec->GetSize(&size); 911 else if (stdOutFileStreamSpec) 912 size = stdOutFileStreamSpec->GetSize(); 913 else 914 size = volStreamSpec->GetSize(); 915 916 st.OutArcFileSize = size; 917 } 918 919 if (outStreamSpec) 920 result = outStreamSpec->Close(); 921 else if (volStreamSpec) 922 { 923 result = volStreamSpec->FinalFlush_and_CloseFiles(st.NumVolumes); 924 st.IsMultiVolMode = true; 925 } 926 927 RINOK(result) 928 929 if (processedItemsStatuses) 930 { 931 FOR_VECTOR (i, updatePairs2) 932 { 933 const CUpdatePair2 &up = updatePairs2[i]; 934 if (up.NewData && up.DirIndex >= 0) 935 { 936 const CDirItem &di = dirItems.Items[(unsigned)up.DirIndex]; 937 if (di.AreReparseData() || (!di.IsDir() && di.Size == 0)) 938 processedItemsStatuses[(unsigned)up.DirIndex] = 1; 939 } 940 } 941 } 942 943 return result; 944} 945 946 947 948static bool Censor_AreAllAllowed(const NWildcard::CCensor &censor) 949{ 950 if (censor.Pairs.Size() != 1) 951 return false; 952 const NWildcard::CPair &pair = censor.Pairs[0]; 953 /* Censor_CheckPath() ignores (CPair::Prefix). 954 So we also ignore (CPair::Prefix) here */ 955 // if (!pair.Prefix.IsEmpty()) return false; 956 return pair.Head.AreAllAllowed(); 957} 958 959bool CensorNode_CheckPath2(const NWildcard::CCensorNode &node, const CReadArcItem &item, bool &include); 960 961static bool Censor_CheckPath(const NWildcard::CCensor &censor, const CReadArcItem &item) 962{ 963 bool finded = false; 964 FOR_VECTOR (i, censor.Pairs) 965 { 966 /* (CPair::Prefix) in not used for matching items in archive. 967 So we ignore (CPair::Prefix) here */ 968 bool include; 969 if (CensorNode_CheckPath2(censor.Pairs[i].Head, item, include)) 970 { 971 // Check it and FIXME !!!! 972 // here we can exclude item via some Pair, that is still allowed by another Pair 973 if (!include) 974 return false; 975 finded = true; 976 } 977 } 978 return finded; 979} 980 981static HRESULT EnumerateInArchiveItems( 982 // bool storeStreamsMode, 983 const NWildcard::CCensor &censor, 984 const CArc &arc, 985 CObjectVector<CArcItem> &arcItems) 986{ 987 arcItems.Clear(); 988 UInt32 numItems; 989 IInArchive *archive = arc.Archive; 990 RINOK(archive->GetNumberOfItems(&numItems)) 991 arcItems.ClearAndReserve(numItems); 992 993 CReadArcItem item; 994 995 const bool allFilesAreAllowed = Censor_AreAllAllowed(censor); 996 997 for (UInt32 i = 0; i < numItems; i++) 998 { 999 CArcItem ai; 1000 1001 RINOK(arc.GetItem(i, item)) 1002 ai.Name = item.Path; 1003 ai.IsDir = item.IsDir; 1004 ai.IsAltStream = 1005 #ifdef SUPPORT_ALT_STREAMS 1006 item.IsAltStream; 1007 #else 1008 false; 1009 #endif 1010 1011 /* 1012 if (!storeStreamsMode && ai.IsAltStream) 1013 continue; 1014 */ 1015 if (allFilesAreAllowed) 1016 ai.Censored = true; 1017 else 1018 ai.Censored = Censor_CheckPath(censor, item); 1019 1020 // ai.MTime will be set to archive MTime, if not present in archive item 1021 RINOK(arc.GetItem_MTime(i, ai.MTime)) 1022 RINOK(arc.GetItem_Size(i, ai.Size, ai.Size_Defined)) 1023 1024 ai.IndexInServer = i; 1025 arcItems.AddInReserved(ai); 1026 } 1027 return S_OK; 1028} 1029 1030#if defined(_WIN32) && !defined(UNDER_CE) 1031 1032#if defined(__MINGW32__) || defined(__MINGW64__) 1033#include <mapi.h> 1034#else 1035#include <MAPI.h> 1036#endif 1037 1038extern "C" { 1039 1040#ifdef MAPI_FORCE_UNICODE 1041 1042#define Z7_WIN_LPMAPISENDMAILW LPMAPISENDMAILW 1043#define Z7_WIN_MapiFileDescW MapiFileDescW 1044#define Z7_WIN_MapiMessageW MapiMessageW 1045#define Z7_WIN_MapiRecipDescW MapiRecipDescW 1046 1047#else 1048 1049typedef struct 1050{ 1051 ULONG ulReserved; 1052 ULONG ulRecipClass; 1053 PWSTR lpszName; 1054 PWSTR lpszAddress; 1055 ULONG ulEIDSize; 1056 PVOID lpEntryID; 1057} Z7_WIN_MapiRecipDescW, *Z7_WIN_lpMapiRecipDescW; 1058 1059typedef struct 1060{ 1061 ULONG ulReserved; 1062 ULONG flFlags; 1063 ULONG nPosition; 1064 PWSTR lpszPathName; 1065 PWSTR lpszFileName; 1066 PVOID lpFileType; 1067} Z7_WIN_MapiFileDescW, *Z7_WIN_lpMapiFileDescW; 1068 1069typedef struct 1070{ 1071 ULONG ulReserved; 1072 PWSTR lpszSubject; 1073 PWSTR lpszNoteText; 1074 PWSTR lpszMessageType; 1075 PWSTR lpszDateReceived; 1076 PWSTR lpszConversationID; 1077 FLAGS flFlags; 1078 Z7_WIN_lpMapiRecipDescW lpOriginator; 1079 ULONG nRecipCount; 1080 Z7_WIN_lpMapiRecipDescW lpRecips; 1081 ULONG nFileCount; 1082 Z7_WIN_lpMapiFileDescW lpFiles; 1083} Z7_WIN_MapiMessageW, *Z7_WIN_lpMapiMessageW; 1084 1085typedef ULONG (FAR PASCAL Z7_WIN_MAPISENDMAILW)( 1086 LHANDLE lhSession, 1087 ULONG_PTR ulUIParam, 1088 Z7_WIN_lpMapiMessageW lpMessage, 1089 FLAGS flFlags, 1090 ULONG ulReserved 1091); 1092typedef Z7_WIN_MAPISENDMAILW FAR *Z7_WIN_LPMAPISENDMAILW; 1093 1094#endif // MAPI_FORCE_UNICODE 1095} 1096#endif // _WIN32 1097 1098 1099HRESULT UpdateArchive( 1100 CCodecs *codecs, 1101 const CObjectVector<COpenType> &types, 1102 const UString &cmdArcPath2, 1103 NWildcard::CCensor &censor, 1104 CUpdateOptions &options, 1105 CUpdateErrorInfo &errorInfo, 1106 IOpenCallbackUI *openCallback, 1107 IUpdateCallbackUI2 *callback, 1108 bool needSetPath) 1109{ 1110 if (options.StdOutMode && options.EMailMode) 1111 return E_FAIL; 1112 1113 if (types.Size() > 1) 1114 return E_NOTIMPL; 1115 1116 bool renameMode = !options.RenamePairs.IsEmpty(); 1117 if (renameMode) 1118 { 1119 if (options.Commands.Size() != 1) 1120 return E_FAIL; 1121 } 1122 1123 if (options.DeleteAfterCompressing) 1124 { 1125 if (options.Commands.Size() != 1) 1126 return E_NOTIMPL; 1127 const CActionSet &as = options.Commands[0].ActionSet; 1128 for (unsigned i = 2; i < NPairState::kNumValues; i++) 1129 if (as.StateActions[i] != NPairAction::kCompress) 1130 return E_NOTIMPL; 1131 } 1132 1133 censor.AddPathsToCensor(options.PathMode); 1134 #ifdef _WIN32 1135 ConvertToLongNames(censor); 1136 #endif 1137 censor.ExtendExclude(); 1138 1139 1140 if (options.VolumesSizes.Size() > 0 && (options.EMailMode /* || options.SfxMode */)) 1141 return E_NOTIMPL; 1142 1143 if (options.SfxMode) 1144 { 1145 CProperty property; 1146 property.Name = "rsfx"; 1147 options.MethodMode.Properties.Add(property); 1148 if (options.SfxModule.IsEmpty()) 1149 { 1150 errorInfo.Message = "SFX file is not specified"; 1151 return E_FAIL; 1152 } 1153 bool found = false; 1154 if (options.SfxModule.Find(FCHAR_PATH_SEPARATOR) < 0) 1155 { 1156 const FString fullName = NDLL::GetModuleDirPrefix() + options.SfxModule; 1157 if (NFind::DoesFileExist_FollowLink(fullName)) 1158 { 1159 options.SfxModule = fullName; 1160 found = true; 1161 } 1162 } 1163 if (!found) 1164 { 1165 if (!NFind::DoesFileExist_FollowLink(options.SfxModule)) 1166 return errorInfo.SetFromLastError("cannot find specified SFX module", options.SfxModule); 1167 } 1168 } 1169 1170 CArchiveLink arcLink; 1171 1172 1173 if (needSetPath) 1174 { 1175 if (!options.InitFormatIndex(codecs, types, cmdArcPath2) || 1176 !options.SetArcPath(codecs, cmdArcPath2)) 1177 return E_NOTIMPL; 1178 } 1179 1180 UString arcPath = options.ArchivePath.GetFinalPath(); 1181 1182 if (!options.VolumesSizes.IsEmpty()) 1183 { 1184 arcPath = options.ArchivePath.GetFinalVolPath(); 1185 arcPath += ".001"; 1186 } 1187 1188 if (cmdArcPath2.IsEmpty()) 1189 { 1190 if (options.MethodMode.Type.FormatIndex < 0) 1191 throw "type of archive is not specified"; 1192 } 1193 else 1194 { 1195 NFind::CFileInfo fi; 1196 if (!fi.Find_FollowLink(us2fs(arcPath))) 1197 { 1198 if (renameMode) 1199 throw "can't find archive"; 1200 if (options.MethodMode.Type.FormatIndex < 0) 1201 { 1202 if (!options.SetArcPath(codecs, cmdArcPath2)) 1203 return E_NOTIMPL; 1204 } 1205 } 1206 else 1207 { 1208 if (fi.IsDir()) 1209 return errorInfo.SetFromError_DWORD("There is a folder with the name of archive", 1210 us2fs(arcPath), 1211 #ifdef _WIN32 1212 ERROR_ACCESS_DENIED 1213 #else 1214 EISDIR 1215 #endif 1216 ); 1217 #ifdef _WIN32 1218 if (fi.IsDevice) 1219 return E_NOTIMPL; 1220 #endif 1221 1222 if (!options.StdOutMode && options.UpdateArchiveItself) 1223 if (fi.IsReadOnly()) 1224 { 1225 return errorInfo.SetFromError_DWORD("The file is read-only", 1226 us2fs(arcPath), 1227 #ifdef _WIN32 1228 ERROR_ACCESS_DENIED 1229 #else 1230 EACCES 1231 #endif 1232 ); 1233 } 1234 1235 if (options.VolumesSizes.Size() > 0) 1236 { 1237 errorInfo.FileNames.Add(us2fs(arcPath)); 1238 // errorInfo.SystemError = (DWORD)E_NOTIMPL; 1239 errorInfo.Message = kUpdateIsNotSupported_MultiVol; 1240 return E_NOTIMPL; 1241 } 1242 CObjectVector<COpenType> types2; 1243 // change it. 1244 if (options.MethodMode.Type_Defined) 1245 types2.Add(options.MethodMode.Type); 1246 // We need to set Properties to open archive only in some cases (WIM archives). 1247 1248 CIntVector excl; 1249 COpenOptions op; 1250 #ifndef Z7_SFX 1251 op.props = &options.MethodMode.Properties; 1252 #endif 1253 op.codecs = codecs; 1254 op.types = &types2; 1255 op.excludedFormats = ! 1256 op.stdInMode = false; 1257 op.stream = NULL; 1258 op.filePath = arcPath; 1259 1260 RINOK(callback->StartOpenArchive(arcPath)) 1261 1262 HRESULT result = arcLink.Open_Strict(op, openCallback); 1263 1264 if (result == E_ABORT) 1265 return result; 1266 1267 HRESULT res2 = callback->OpenResult(codecs, arcLink, arcPath, result); 1268 /* 1269 if (result == S_FALSE) 1270 return E_FAIL; 1271 */ 1272 RINOK(res2) 1273 RINOK(result) 1274 1275 if (arcLink.VolumePaths.Size() > 1) 1276 { 1277 // errorInfo.SystemError = (DWORD)E_NOTIMPL; 1278 errorInfo.Message = kUpdateIsNotSupported_MultiVol; 1279 return E_NOTIMPL; 1280 } 1281 1282 CArc &arc = arcLink.Arcs.Back(); 1283 arc.MTime.Def = 1284 #ifdef _WIN32 1285 !fi.IsDevice; 1286 #else 1287 true; 1288 #endif 1289 if (arc.MTime.Def) 1290 arc.MTime.Set_From_FiTime(fi.MTime); 1291 1292 if (arc.ErrorInfo.ThereIsTail) 1293 { 1294 // errorInfo.SystemError = (DWORD)E_NOTIMPL; 1295 errorInfo.Message = "There is some data block after the end of the archive"; 1296 return E_NOTIMPL; 1297 } 1298 if (options.MethodMode.Type.FormatIndex < 0) 1299 { 1300 options.MethodMode.Type.FormatIndex = arcLink.GetArc()->FormatIndex; 1301 if (!options.SetArcPath(codecs, cmdArcPath2)) 1302 return E_NOTIMPL; 1303 } 1304 } 1305 } 1306 1307 if (options.MethodMode.Type.FormatIndex < 0) 1308 { 1309 options.MethodMode.Type.FormatIndex = codecs->FindFormatForArchiveType((UString)kDefaultArcType); 1310 if (options.MethodMode.Type.FormatIndex < 0) 1311 return E_NOTIMPL; 1312 } 1313 1314 bool thereIsInArchive = arcLink.IsOpen; 1315 if (!thereIsInArchive && renameMode) 1316 return E_FAIL; 1317 1318 CDirItems dirItems; 1319 dirItems.Callback = callback; 1320 1321 CDirItem parentDirItem; 1322 CDirItem *parentDirItem_Ptr = NULL; 1323 1324 /* 1325 FStringVector requestedPaths; 1326 FStringVector *requestedPaths_Ptr = NULL; 1327 if (options.DeleteAfterCompressing) 1328 requestedPaths_Ptr = &requestedPaths; 1329 */ 1330 1331 if (options.StdInMode) 1332 { 1333 CDirItem di; 1334 di.ClearBase(); 1335 di.Name = options.StdInFileName; 1336 di.Size = (UInt64)(Int64)-1; 1337 di.SetAsFile(); 1338 NTime::GetCurUtc_FiTime(di.MTime); 1339 di.CTime = di.ATime = di.MTime; 1340 dirItems.Items.Add(di); 1341 } 1342 else 1343 { 1344 bool needScanning = false; 1345 1346 if (!renameMode) 1347 FOR_VECTOR (i, options.Commands) 1348 if (options.Commands[i].ActionSet.NeedScanning()) 1349 needScanning = true; 1350 1351 if (needScanning) 1352 { 1353 RINOK(callback->StartScanning()) 1354 1355 dirItems.SymLinks = options.SymLinks.Val; 1356 1357 #if defined(_WIN32) && !defined(UNDER_CE) 1358 dirItems.ReadSecure = options.NtSecurity.Val; 1359 #endif 1360 1361 dirItems.ScanAltStreams = options.AltStreams.Val; 1362 dirItems.ExcludeDirItems = censor.ExcludeDirItems; 1363 dirItems.ExcludeFileItems = censor.ExcludeFileItems; 1364 1365 dirItems.ShareForWrite = options.OpenShareForWrite; 1366 1367 #ifndef _WIN32 1368 dirItems.StoreOwnerName = options.StoreOwnerName.Val; 1369 #endif 1370 1371 const HRESULT res = EnumerateItems(censor, 1372 options.PathMode, 1373 UString(), // options.AddPathPrefix, 1374 dirItems); 1375 1376 if (res != S_OK) 1377 { 1378 if (res != E_ABORT) 1379 errorInfo.Message = "Scanning error"; 1380 return res; 1381 } 1382 1383 RINOK(callback->FinishScanning(dirItems.Stat)) 1384 1385 // 22.00: we don't need parent folder, if absolute path mode 1386 if (options.PathMode != NWildcard::k_AbsPath) 1387 if (censor.Pairs.Size() == 1) 1388 { 1389 NFind::CFileInfo fi; 1390 FString prefix = us2fs(censor.Pairs[0].Prefix); 1391 prefix.Add_Dot(); 1392 // UString prefix = censor.Pairs[0].Prefix; 1393 /* 1394 if (prefix.Back() == WCHAR_PATH_SEPARATOR) 1395 { 1396 prefix.DeleteBack(); 1397 } 1398 */ 1399 if (fi.Find(prefix)) 1400 if (fi.IsDir()) 1401 { 1402 parentDirItem.Copy_From_FileInfoBase(fi); 1403 parentDirItem_Ptr = &parentDirItem; 1404 1405 int secureIndex = -1; 1406 #if defined(_WIN32) && !defined(UNDER_CE) 1407 if (options.NtSecurity.Val) 1408 dirItems.AddSecurityItem(prefix, secureIndex); 1409 #endif 1410 parentDirItem.SecureIndex = secureIndex; 1411 } 1412 } 1413 } 1414 } 1415 1416 FString tempDirPrefix; 1417 bool usesTempDir = false; 1418 1419 #ifdef _WIN32 1420 CTempDir tempDirectory; 1421 if (options.EMailMode && options.EMailRemoveAfter) 1422 { 1423 tempDirectory.Create(kTempFolderPrefix); 1424 tempDirPrefix = tempDirectory.GetPath(); 1425 NormalizeDirPathPrefix(tempDirPrefix); 1426 usesTempDir = true; 1427 } 1428 #endif 1429 1430 CTempFiles tempFiles; 1431 1432 bool createTempFile = false; 1433 1434 if (!options.StdOutMode && options.UpdateArchiveItself) 1435 { 1436 CArchivePath &ap = options.Commands[0].ArchivePath; 1437 ap = options.ArchivePath; 1438 // if ((archive != 0 && !usesTempDir) || !options.WorkingDir.IsEmpty()) 1439 if ((thereIsInArchive || !options.WorkingDir.IsEmpty()) && !usesTempDir && options.VolumesSizes.Size() == 0) 1440 { 1441 createTempFile = true; 1442 ap.Temp = true; 1443 if (!options.WorkingDir.IsEmpty()) 1444 ap.TempPrefix = options.WorkingDir; 1445 else 1446 ap.TempPrefix = us2fs(ap.Prefix); 1447 NormalizeDirPathPrefix(ap.TempPrefix); 1448 } 1449 } 1450 1451 unsigned ci; 1452 1453 1454 // self including protection 1455 if (options.DeleteAfterCompressing) 1456 { 1457 for (ci = 0; ci < options.Commands.Size(); ci++) 1458 { 1459 CArchivePath &ap = options.Commands[ci].ArchivePath; 1460 const FString path = us2fs(ap.GetFinalPath()); 1461 // maybe we must compare absolute paths path here 1462 FOR_VECTOR (i, dirItems.Items) 1463 { 1464 const FString phyPath = dirItems.GetPhyPath(i); 1465 if (phyPath == path) 1466 { 1467 UString s; 1468 s = "It is not allowed to include archive to itself"; 1469 s.Add_LF(); 1470 s += fs2us(path); 1471 throw s; 1472 } 1473 } 1474 } 1475 } 1476 1477 1478 for (ci = 0; ci < options.Commands.Size(); ci++) 1479 { 1480 CArchivePath &ap = options.Commands[ci].ArchivePath; 1481 if (usesTempDir) 1482 { 1483 // Check it 1484 ap.Prefix = fs2us(tempDirPrefix); 1485 // ap.Temp = true; 1486 // ap.TempPrefix = tempDirPrefix; 1487 } 1488 if (!options.StdOutMode && 1489 (ci > 0 || !createTempFile)) 1490 { 1491 const FString path = us2fs(ap.GetFinalPath()); 1492 if (NFind::DoesFileOrDirExist(path)) 1493 { 1494 errorInfo.SystemError = ERROR_FILE_EXISTS; 1495 errorInfo.Message = "The file already exists"; 1496 errorInfo.FileNames.Add(path); 1497 return errorInfo.Get_HRESULT_Error(); 1498 } 1499 } 1500 } 1501 1502 CObjectVector<CArcItem> arcItems; 1503 if (thereIsInArchive) 1504 { 1505 RINOK(EnumerateInArchiveItems( 1506 // options.StoreAltStreams, 1507 censor, arcLink.Arcs.Back(), arcItems)) 1508 } 1509 1510 /* 1511 FStringVector processedFilePaths; 1512 FStringVector *processedFilePaths_Ptr = NULL; 1513 if (options.DeleteAfterCompressing) 1514 processedFilePaths_Ptr = &processedFilePaths; 1515 */ 1516 1517 CByteBuffer processedItems; 1518 if (options.DeleteAfterCompressing) 1519 { 1520 const unsigned num = dirItems.Items.Size(); 1521 processedItems.Alloc(num); 1522 for (unsigned i = 0; i < num; i++) 1523 processedItems[i] = 0; 1524 } 1525 1526 CMultiOutStream_Bunch multiStreams; 1527 1528 /* 1529 #ifndef Z7_NO_CRYPTO 1530 if (arcLink.PasswordWasAsked) 1531 { 1532 // We set password, if open have requested password 1533 RINOK(callback->SetPassword(arcLink.Password)); 1534 } 1535 #endif 1536 */ 1537 1538 for (ci = 0; ci < options.Commands.Size(); ci++) 1539 { 1540 const CArc *arc = thereIsInArchive ? arcLink.GetArc() : NULL; 1541 CUpdateArchiveCommand &command = options.Commands[ci]; 1542 UString name; 1543 bool isUpdating; 1544 1545 if (options.StdOutMode) 1546 { 1547 name = "stdout"; 1548 isUpdating = thereIsInArchive; 1549 } 1550 else 1551 { 1552 name = command.ArchivePath.GetFinalPath(); 1553 isUpdating = (ci == 0 && options.UpdateArchiveItself && thereIsInArchive); 1554 } 1555 1556 RINOK(callback->StartArchive(name, isUpdating)) 1557 1558 CFinishArchiveStat st; 1559 1560 RINOK(Compress(options, 1561 isUpdating, 1562 codecs, 1563 command.ActionSet, 1564 arc, 1565 command.ArchivePath, 1566 arcItems, 1567 options.DeleteAfterCompressing ? (Byte *)processedItems : NULL, 1568 1569 dirItems, 1570 parentDirItem_Ptr, 1571 1572 tempFiles, 1573 multiStreams, 1574 errorInfo, callback, st)) 1575 1576 RINOK(callback->FinishArchive(st)) 1577 } 1578 1579 1580 if (thereIsInArchive) 1581 { 1582 RINOK(arcLink.Close()) 1583 arcLink.Release(); 1584 } 1585 1586 multiStreams.DisableDeletion(); 1587 RINOK(multiStreams.Destruct()) 1588 1589 tempFiles.Paths.Clear(); 1590 if (createTempFile) 1591 { 1592 try 1593 { 1594 CArchivePath &ap = options.Commands[0].ArchivePath; 1595 const FString &tempPath = ap.GetTempPath(); 1596 1597 // DWORD attrib = 0; 1598 if (thereIsInArchive) 1599 { 1600 // attrib = NFind::GetFileAttrib(us2fs(arcPath)); 1601 if (!DeleteFileAlways(us2fs(arcPath))) 1602 return errorInfo.SetFromLastError("cannot delete the file", us2fs(arcPath)); 1603 } 1604 1605 if (!MyMoveFile(tempPath, us2fs(arcPath))) 1606 { 1607 errorInfo.SetFromLastError("cannot move the file", tempPath); 1608 errorInfo.FileNames.Add(us2fs(arcPath)); 1609 return errorInfo.Get_HRESULT_Error(); 1610 } 1611 1612 /* 1613 if (attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_READONLY)) 1614 { 1615 DWORD attrib2 = NFind::GetFileAttrib(us2fs(arcPath)); 1616 if (attrib2 != INVALID_FILE_ATTRIBUTES) 1617 NDir::SetFileAttrib(us2fs(arcPath), attrib2 | FILE_ATTRIBUTE_READONLY); 1618 } 1619 */ 1620 } 1621 catch(...) 1622 { 1623 throw; 1624 } 1625 } 1626 1627 1628 #if defined(_WIN32) && !defined(UNDER_CE) 1629 1630 if (options.EMailMode) 1631 { 1632 NDLL::CLibrary mapiLib; 1633 if (!mapiLib.Load(FTEXT("Mapi32.dll"))) 1634 { 1635 errorInfo.SetFromLastError("cannot load Mapi32.dll"); 1636 return errorInfo.Get_HRESULT_Error(); 1637 } 1638 1639 FStringVector fullPaths; 1640 unsigned i; 1641 1642 for (i = 0; i < options.Commands.Size(); i++) 1643 { 1644 CArchivePath &ap = options.Commands[i].ArchivePath; 1645 const FString finalPath = us2fs(ap.GetFinalPath()); 1646 FString arcPath2; 1647 if (!MyGetFullPathName(finalPath, arcPath2)) 1648 return errorInfo.SetFromLastError("GetFullPathName error", finalPath); 1649 fullPaths.Add(arcPath2); 1650 } 1651 1652 /* 1653 LPMAPISENDDOCUMENTS fnSend = (LPMAPISENDDOCUMENTS)mapiLib.GetProc("MAPISendDocuments"); 1654 if (fnSend == 0) 1655 { 1656 errorInfo.SetFromLastError)("7-Zip cannot find MAPISendDocuments function"); 1657 return errorInfo.Get_HRESULT_Error(); 1658 } 1659 */ 1660 const 1661 Z7_WIN_LPMAPISENDMAILW sendMailW = Z7_GET_PROC_ADDRESS( 1662 Z7_WIN_LPMAPISENDMAILW, mapiLib.Get_HMODULE(), 1663 "MAPISendMailW"); 1664 if (sendMailW) 1665 { 1666 1667 CCurrentDirRestorer curDirRestorer; 1668 1669 UStringVector paths; 1670 UStringVector names; 1671 1672 for (i = 0; i < fullPaths.Size(); i++) 1673 { 1674 const UString arcPath2 = fs2us(fullPaths[i]); 1675 const UString fileName = ExtractFileNameFromPath(arcPath2); 1676 paths.Add(arcPath2); 1677 names.Add(fileName); 1678 // Warning!!! MAPISendDocuments function changes Current directory 1679 // fnSend(0, ";", (LPSTR)(LPCSTR)path, (LPSTR)(LPCSTR)name, 0); 1680 } 1681 1682 CRecordVector<Z7_WIN_MapiFileDescW> files; 1683 files.ClearAndSetSize(paths.Size()); 1684 1685 for (i = 0; i < paths.Size(); i++) 1686 { 1687 Z7_WIN_MapiFileDescW &f = files[i]; 1688 memset(&f, 0, sizeof(f)); 1689 f.nPosition = 0xFFFFFFFF; 1690 f.lpszPathName = paths[i].Ptr_non_const(); 1691 f.lpszFileName = names[i].Ptr_non_const(); 1692 } 1693 1694 { 1695 Z7_WIN_MapiMessageW m; 1696 memset(&m, 0, sizeof(m)); 1697 m.nFileCount = files.Size(); 1698 m.lpFiles = &files.Front(); 1699 1700 const UString addr (options.EMailAddress); 1701 Z7_WIN_MapiRecipDescW rec; 1702 if (!addr.IsEmpty()) 1703 { 1704 memset(&rec, 0, sizeof(rec)); 1705 rec.ulRecipClass = MAPI_TO; 1706 rec.lpszAddress = addr.Ptr_non_const(); 1707 m.nRecipCount = 1; 1708 m.lpRecips = &rec; 1709 } 1710 1711 sendMailW((LHANDLE)0, 0, &m, MAPI_DIALOG, 0); 1712 } 1713 } 1714 else 1715 { 1716 const 1717 LPMAPISENDMAIL sendMail = Z7_GET_PROC_ADDRESS( 1718 LPMAPISENDMAIL, mapiLib.Get_HMODULE(), 1719 "MAPISendMail"); 1720 if (!sendMail) 1721 { 1722 errorInfo.SetFromLastError("7-Zip cannot find MAPISendMail function"); 1723 return errorInfo.Get_HRESULT_Error(); 1724 } 1725 1726 CCurrentDirRestorer curDirRestorer; 1727 1728 AStringVector paths; 1729 AStringVector names; 1730 1731 for (i = 0; i < fullPaths.Size(); i++) 1732 { 1733 const UString arcPath2 = fs2us(fullPaths[i]); 1734 const UString fileName = ExtractFileNameFromPath(arcPath2); 1735 paths.Add(GetAnsiString(arcPath2)); 1736 names.Add(GetAnsiString(fileName)); 1737 // const AString path (GetAnsiString(arcPath2)); 1738 // const AString name (GetAnsiString(fileName)); 1739 // Warning!!! MAPISendDocuments function changes Current directory 1740 // fnSend(0, ";", (LPSTR)(LPCSTR)path, (LPSTR)(LPCSTR)name, 0); 1741 } 1742 1743 CRecordVector<MapiFileDesc> files; 1744 files.ClearAndSetSize(paths.Size()); 1745 1746 for (i = 0; i < paths.Size(); i++) 1747 { 1748 MapiFileDesc &f = files[i]; 1749 memset(&f, 0, sizeof(f)); 1750 f.nPosition = 0xFFFFFFFF; 1751 f.lpszPathName = paths[i].Ptr_non_const(); 1752 f.lpszFileName = names[i].Ptr_non_const(); 1753 } 1754 1755 { 1756 MapiMessage m; 1757 memset(&m, 0, sizeof(m)); 1758 m.nFileCount = files.Size(); 1759 m.lpFiles = &files.Front(); 1760 1761 const AString addr (GetAnsiString(options.EMailAddress)); 1762 MapiRecipDesc rec; 1763 if (!addr.IsEmpty()) 1764 { 1765 memset(&rec, 0, sizeof(rec)); 1766 rec.ulRecipClass = MAPI_TO; 1767 rec.lpszAddress = addr.Ptr_non_const(); 1768 m.nRecipCount = 1; 1769 m.lpRecips = &rec; 1770 } 1771 1772 sendMail((LHANDLE)0, 0, &m, MAPI_DIALOG, 0); 1773 } 1774 } 1775 } 1776 1777 #endif 1778 1779 if (options.DeleteAfterCompressing) 1780 { 1781 CRecordVector<CDirPathSortPair> pairs; 1782 FStringVector foldersNames; 1783 1784 unsigned i; 1785 1786 for (i = 0; i < dirItems.Items.Size(); i++) 1787 { 1788 const CDirItem &dirItem = dirItems.Items[i]; 1789 const FString phyPath = dirItems.GetPhyPath(i); 1790 if (dirItem.IsDir()) 1791 { 1792 CDirPathSortPair pair; 1793 pair.Index = i; 1794 pair.SetNumSlashes(phyPath); 1795 pairs.Add(pair); 1796 } 1797 else 1798 { 1799 // 21.04: we have set processedItems[*] before for all required items 1800 if (processedItems[i] != 0 1801 // || dirItem.Size == 0 1802 // || dirItem.AreReparseData() 1803 ) 1804 { 1805 NFind::CFileInfo fileInfo; 1806 /* if (!SymLinks), we follow link here, similar to (dirItem) filling */ 1807 if (fileInfo.Find(phyPath, !options.SymLinks.Val)) 1808 { 1809 bool is_SameSize = false; 1810 if (options.SymLinks.Val && dirItem.AreReparseData()) 1811 { 1812 /* (dirItem.Size = dirItem.ReparseData.Size()) was set before. 1813 So we don't compare sizes for that case here */ 1814 is_SameSize = fileInfo.IsOsSymLink(); 1815 } 1816 else 1817 is_SameSize = (fileInfo.Size == dirItem.Size); 1818 1819 if (is_SameSize 1820 && Compare_FiTime(&fileInfo.MTime, &dirItem.MTime) == 0 1821 && Compare_FiTime(&fileInfo.CTime, &dirItem.CTime) == 0) 1822 { 1823 RINOK(callback->DeletingAfterArchiving(phyPath, false)) 1824 DeleteFileAlways(phyPath); 1825 } 1826 } 1827 } 1828 else 1829 { 1830 // file was skipped by some reason. We can throw error for debug: 1831 /* 1832 errorInfo.SystemError = 0; 1833 errorInfo.Message = "file was not processed"; 1834 errorInfo.FileNames.Add(phyPath); 1835 return E_FAIL; 1836 */ 1837 } 1838 } 1839 } 1840 1841 pairs.Sort2(); 1842 1843 for (i = 0; i < pairs.Size(); i++) 1844 { 1845 const FString phyPath = dirItems.GetPhyPath(pairs[i].Index); 1846 if (NFind::DoesDirExist(phyPath)) 1847 { 1848 RINOK(callback->DeletingAfterArchiving(phyPath, true)) 1849 RemoveDir(phyPath); 1850 } 1851 } 1852 1853 RINOK(callback->FinishDeletingAfterArchiving()) 1854 } 1855 1856 return S_OK; 1857} 1858