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 #ifdef _WIN32 11 #include "Windows/DLL.h" 12 #endif 13 14 #include "Windows/FileDir.h" 15 #include "Windows/FileFind.h" 16 #include "Windows/FileName.h" 17 #include "Windows/PropVariant.h" 18 #include "Windows/PropVariantConversions.h" 19 #include "Windows/Time.h" 20 21 #include "../../Common/FileStreams.h" 22 23 #include "../../Compress/CopyCoder.h" 24 25 #include "../Common/DirItem.h" 26 #include "../Common/EnumDirItems.h" 27 #include "../Common/OpenArchive.h" 28 #include "../Common/UpdateProduce.h" 29 30 #include "EnumDirItems.h" 31 #include "SetProperties.h" 32 #include "TempFiles.h" 33 #include "UpdateCallback.h" 34 35 static const char *kUpdateIsNotSupoorted = 36 "update operations are not supported for this archive"; 37 38 using namespace NWindows; 39 using namespace NCOM; 40 using namespace NFile; 41 using namespace NName; 42 43 static const wchar_t *kTempFolderPrefix = L"7zE"; 44 45 using namespace NUpdateArchive; 46 47 class COutMultiVolStream: 48 public IOutStream, 49 public CMyUnknownImp 50 { 51 int _streamIndex; // required stream 52 UInt64 _offsetPos; // offset from start of _streamIndex index 53 UInt64 _absPos; 54 UInt64 _length; 55 56 struct CSubStreamInfo 57 { 58 COutFileStream *StreamSpec; 59 CMyComPtr<IOutStream> Stream; 60 UString Name; 61 UInt64 Pos; 62 UInt64 RealSize; 63 }; 64 CObjectVector<CSubStreamInfo> Streams; 65 public: 66 // CMyComPtr<IArchiveUpdateCallback2> VolumeCallback; 67 CRecordVector<UInt64> Sizes; 68 UString Prefix; 69 CTempFiles *TempFiles; 70 71 void Init() 72 { 73 _streamIndex = 0; 74 _offsetPos = 0; 75 _absPos = 0; 76 _length = 0; 77 } 78 79 HRESULT Close(); 80 81 MY_UNKNOWN_IMP1(IOutStream) 82 83 STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); 84 STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); 85 STDMETHOD(SetSize)(UInt64 newSize); 86 }; 87 88 // static NSynchronization::CCriticalSection g_TempPathsCS; 89 90 HRESULT COutMultiVolStream::Close() 91 { 92 HRESULT res = S_OK; 93 for (int i = 0; i < Streams.Size(); i++) 94 { 95 CSubStreamInfo &s = Streams[i]; 96 if (s.StreamSpec) 97 { 98 HRESULT res2 = s.StreamSpec->Close(); 99 if (res2 != S_OK) 100 res = res2; 101 } 102 } 103 return res; 104 } 105 106 STDMETHODIMP COutMultiVolStream::Write(const void *data, UInt32 size, UInt32 *processedSize) 107 { 108 if (processedSize != NULL) 109 *processedSize = 0; 110 while(size > 0) 111 { 112 if (_streamIndex >= Streams.Size()) 113 { 114 CSubStreamInfo subStream; 115 116 wchar_t temp[16]; 117 ConvertUInt32ToString(_streamIndex + 1, temp); 118 UString res = temp; 119 while (res.Length() < 3) 120 res = UString(L'0') + res; 121 UString name = Prefix + res; 122 subStream.StreamSpec = new COutFileStream; 123 subStream.Stream = subStream.StreamSpec; 124 if (!subStream.StreamSpec->Create(name, false)) 125 return ::GetLastError(); 126 { 127 // NSynchronization::CCriticalSectionLock lock(g_TempPathsCS); 128 TempFiles->Paths.Add(name); 129 } 130 131 subStream.Pos = 0; 132 subStream.RealSize = 0; 133 subStream.Name = name; 134 Streams.Add(subStream); 135 continue; 136 } 137 CSubStreamInfo &subStream = Streams[_streamIndex]; 138 139 int index = _streamIndex; 140 if (index >= Sizes.Size()) 141 index = Sizes.Size() - 1; 142 UInt64 volSize = Sizes[index]; 143 144 if (_offsetPos >= volSize) 145 { 146 _offsetPos -= volSize; 147 _streamIndex++; 148 continue; 149 } 150 if (_offsetPos != subStream.Pos) 151 { 152 // CMyComPtr<IOutStream> outStream; 153 // RINOK(subStream.Stream.QueryInterface(IID_IOutStream, &outStream)); 154 RINOK(subStream.Stream->Seek(_offsetPos, STREAM_SEEK_SET, NULL)); 155 subStream.Pos = _offsetPos; 156 } 157 158 UInt32 curSize = (UInt32)MyMin((UInt64)size, volSize - subStream.Pos); 159 UInt32 realProcessed; 160 RINOK(subStream.Stream->Write(data, curSize, &realProcessed)); 161 data = (void *)((Byte *)data + realProcessed); 162 size -= realProcessed; 163 subStream.Pos += realProcessed; 164 _offsetPos += realProcessed; 165 _absPos += realProcessed; 166 if (_absPos > _length) 167 _length = _absPos; 168 if (_offsetPos > subStream.RealSize) 169 subStream.RealSize = _offsetPos; 170 if (processedSize != NULL) 171 *processedSize += realProcessed; 172 if (subStream.Pos == volSize) 173 { 174 _streamIndex++; 175 _offsetPos = 0; 176 } 177 if (realProcessed == 0 && curSize != 0) 178 return E_FAIL; 179 break; 180 } 181 return S_OK; 182 } 183 184 STDMETHODIMP COutMultiVolStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) 185 { 186 if (seekOrigin >= 3) 187 return STG_E_INVALIDFUNCTION; 188 switch(seekOrigin) 189 { 190 case STREAM_SEEK_SET: 191 _absPos = offset; 192 break; 193 case STREAM_SEEK_CUR: 194 _absPos += offset; 195 break; 196 case STREAM_SEEK_END: 197 _absPos = _length + offset; 198 break; 199 } 200 _offsetPos = _absPos; 201 if (newPosition != NULL) 202 *newPosition = _absPos; 203 _streamIndex = 0; 204 return S_OK; 205 } 206 207 STDMETHODIMP COutMultiVolStream::SetSize(UInt64 newSize) 208 { 209 if (newSize < 0) 210 return E_INVALIDARG; 211 int i = 0; 212 while (i < Streams.Size()) 213 { 214 CSubStreamInfo &subStream = Streams[i++]; 215 if ((UInt64)newSize < subStream.RealSize) 216 { 217 RINOK(subStream.Stream->SetSize(newSize)); 218 subStream.RealSize = newSize; 219 break; 220 } 221 newSize -= subStream.RealSize; 222 } 223 while (i < Streams.Size()) 224 { 225 { 226 CSubStreamInfo &subStream = Streams.Back(); 227 subStream.Stream.Release(); 228 NDirectory::DeleteFileAlways(subStream.Name); 229 } 230 Streams.DeleteBack(); 231 } 232 _offsetPos = _absPos; 233 _streamIndex = 0; 234 _length = newSize; 235 return S_OK; 236 } 237 238 static const wchar_t *kDefaultArchiveType = L"7z"; 239 static const wchar_t *kSFXExtension = 240 #ifdef _WIN32 241 L"exe"; 242 #else 243 L""; 244 #endif 245 246 bool CUpdateOptions::Init(const CCodecs *codecs, const CIntVector &formatIndices, const UString &arcPath) 247 { 248 if (formatIndices.Size() > 1) 249 return false; 250 int arcTypeIndex = -1; 251 if (formatIndices.Size() != 0) 252 arcTypeIndex = formatIndices[0]; 253 if (arcTypeIndex >= 0) 254 MethodMode.FormatIndex = arcTypeIndex; 255 else 256 { 257 MethodMode.FormatIndex = codecs->FindFormatForArchiveName(arcPath); 258 // It works incorrectly for update command if archive has some non-default extension! 259 if (MethodMode.FormatIndex < 0) 260 MethodMode.FormatIndex = codecs->FindFormatForArchiveType(kDefaultArchiveType); 261 } 262 if (MethodMode.FormatIndex < 0) 263 return false; 264 const CArcInfoEx &arcInfo = codecs->Formats[MethodMode.FormatIndex]; 265 if (!arcInfo.UpdateEnabled) 266 return false; 267 UString typeExt = arcInfo.GetMainExt(); 268 UString ext = typeExt; 269 if (SfxMode) 270 ext = kSFXExtension; 271 ArchivePath.BaseExtension = ext; 272 ArchivePath.VolExtension = typeExt; 273 ArchivePath.ParseFromPath(arcPath); 274 for (int i = 0; i < Commands.Size(); i++) 275 { 276 CUpdateArchiveCommand &uc = Commands[i]; 277 uc.ArchivePath.BaseExtension = ext; 278 uc.ArchivePath.VolExtension = typeExt; 279 uc.ArchivePath.ParseFromPath(uc.UserArchivePath); 280 } 281 return true; 282 } 283 284 /* 285 struct CUpdateProduceCallbackImp: public IUpdateProduceCallback 286 { 287 const CObjectVector<CArcItem> *_arcItems; 288 IUpdateCallbackUI *_callback; 289 290 CUpdateProduceCallbackImp(const CObjectVector<CArcItem> *a, 291 IUpdateCallbackUI *callback): _arcItems(a), _callback(callback) {} 292 virtual HRESULT ShowDeleteFile(int arcIndex); 293 }; 294 295 HRESULT CUpdateProduceCallbackImp::ShowDeleteFile(int arcIndex) 296 { 297 return _callback->ShowDeleteFile((*_arcItems)[arcIndex].Name); 298 } 299 */ 300 301 static HRESULT Compress( 302 CCodecs *codecs, 303 const CActionSet &actionSet, 304 IInArchive *archive, 305 const CCompressionMethodMode &compressionMethod, 306 CArchivePath &archivePath, 307 const CObjectVector<CArcItem> &arcItems, 308 bool shareForWrite, 309 bool stdInMode, 310 /* const UString & stdInFileName, */ 311 bool stdOutMode, 312 const CDirItems &dirItems, 313 bool sfxMode, 314 const UString &sfxModule, 315 const CRecordVector<UInt64> &volumesSizes, 316 CTempFiles &tempFiles, 317 CUpdateErrorInfo &errorInfo, 318 IUpdateCallbackUI *callback) 319 { 320 CMyComPtr<IOutArchive> outArchive; 321 if (archive != NULL) 322 { 323 CMyComPtr<IInArchive> archive2 = archive; 324 HRESULT result = archive2.QueryInterface(IID_IOutArchive, &outArchive); 325 if (result != S_OK) 326 throw kUpdateIsNotSupoorted; 327 } 328 else 329 { 330 RINOK(codecs->CreateOutArchive(compressionMethod.FormatIndex, outArchive)); 331 332 #ifdef EXTERNAL_CODECS 333 { 334 CMyComPtr<ISetCompressCodecsInfo> setCompressCodecsInfo; 335 outArchive.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo); 336 if (setCompressCodecsInfo) 337 { 338 RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(codecs)); 339 } 340 } 341 #endif 342 } 343 if (outArchive == 0) 344 throw kUpdateIsNotSupoorted; 345 346 NFileTimeType::EEnum fileTimeType; 347 UInt32 value; 348 RINOK(outArchive->GetFileTimeType(&value)); 349 350 switch(value) 351 { 352 case NFileTimeType::kWindows: 353 case NFileTimeType::kUnix: 354 case NFileTimeType::kDOS: 355 fileTimeType = (NFileTimeType::EEnum)value; 356 break; 357 default: 358 return E_FAIL; 359 } 360 361 CRecordVector<CUpdatePair2> updatePairs2; 362 363 { 364 CRecordVector<CUpdatePair> updatePairs; 365 GetUpdatePairInfoList(dirItems, arcItems, fileTimeType, updatePairs); // must be done only once!!! 366 // CUpdateProduceCallbackImp upCallback(&arcItems, callback); 367 UpdateProduce(updatePairs, actionSet, updatePairs2, NULL /* &upCallback */); 368 } 369 370 UInt32 numFiles = 0; 371 for (int i = 0; i < updatePairs2.Size(); i++) 372 if (updatePairs2[i].NewData) 373 numFiles++; 374 375 RINOK(callback->SetNumFiles(numFiles)); 376 377 378 CArchiveUpdateCallback *updateCallbackSpec = new CArchiveUpdateCallback; 379 CMyComPtr<IArchiveUpdateCallback> updateCallback(updateCallbackSpec); 380 381 updateCallbackSpec->ShareForWrite = shareForWrite; 382 updateCallbackSpec->StdInMode = stdInMode; 383 updateCallbackSpec->Callback = callback; 384 updateCallbackSpec->DirItems = &dirItems; 385 updateCallbackSpec->ArcItems = &arcItems; 386 updateCallbackSpec->UpdatePairs = &updatePairs2; 387 388 CMyComPtr<ISequentialOutStream> outStream; 389 390 if (!stdOutMode) 391 { 392 UString resultPath; 393 int pos; 394 if (!NFile::NDirectory::MyGetFullPathName(archivePath.GetFinalPath(), resultPath, pos)) 395 throw 1417161; 396 NFile::NDirectory::CreateComplexDirectory(resultPath.Left(pos)); 397 } 398 399 COutFileStream *outStreamSpec = NULL; 400 COutMultiVolStream *volStreamSpec = NULL; 401 402 if (volumesSizes.Size() == 0) 403 { 404 if (stdOutMode) 405 outStream = new CStdOutFileStream; 406 else 407 { 408 outStreamSpec = new COutFileStream; 409 outStream = outStreamSpec; 410 bool isOK = false; 411 UString realPath; 412 for (int i = 0; i < (1 << 16); i++) 413 { 414 if (archivePath.Temp) 415 { 416 if (i > 0) 417 { 418 wchar_t s[16]; 419 ConvertUInt32ToString(i, s); 420 archivePath.TempPostfix = s; 421 } 422 realPath = archivePath.GetTempPath(); 423 } 424 else 425 realPath = archivePath.GetFinalPath(); 426 if (outStreamSpec->Create(realPath, false)) 427 { 428 tempFiles.Paths.Add(realPath); 429 isOK = true; 430 break; 431 } 432 if (::GetLastError() != ERROR_FILE_EXISTS) 433 break; 434 if (!archivePath.Temp) 435 break; 436 } 437 if (!isOK) 438 { 439 errorInfo.SystemError = ::GetLastError(); 440 errorInfo.FileName = realPath; 441 errorInfo.Message = L"7-Zip cannot open file"; 442 return E_FAIL; 443 } 444 } 445 } 446 else 447 { 448 if (stdOutMode) 449 return E_FAIL; 450 volStreamSpec = new COutMultiVolStream; 451 outStream = volStreamSpec; 452 volStreamSpec->Sizes = volumesSizes; 453 volStreamSpec->Prefix = archivePath.GetFinalPath() + UString(L"."); 454 volStreamSpec->TempFiles = &tempFiles; 455 volStreamSpec->Init(); 456 457 /* 458 updateCallbackSpec->VolumesSizes = volumesSizes; 459 updateCallbackSpec->VolName = archivePath.Prefix + archivePath.Name; 460 if (!archivePath.VolExtension.IsEmpty()) 461 updateCallbackSpec->VolExt = UString(L'.') + archivePath.VolExtension; 462 */ 463 } 464 465 RINOK(SetProperties(outArchive, compressionMethod.Properties)); 466 467 if (sfxMode) 468 { 469 CInFileStream *sfxStreamSpec = new CInFileStream; 470 CMyComPtr<IInStream> sfxStream(sfxStreamSpec); 471 if (!sfxStreamSpec->Open(sfxModule)) 472 { 473 errorInfo.SystemError = ::GetLastError(); 474 errorInfo.Message = L"7-Zip cannot open SFX module"; 475 errorInfo.FileName = sfxModule; 476 return E_FAIL; 477 } 478 479 CMyComPtr<ISequentialOutStream> sfxOutStream; 480 COutFileStream *outStreamSpec = NULL; 481 if (volumesSizes.Size() == 0) 482 sfxOutStream = outStream; 483 else 484 { 485 outStreamSpec = new COutFileStream; 486 sfxOutStream = outStreamSpec; 487 UString realPath = archivePath.GetFinalPath(); 488 if (!outStreamSpec->Create(realPath, false)) 489 { 490 errorInfo.SystemError = ::GetLastError(); 491 errorInfo.FileName = realPath; 492 errorInfo.Message = L"7-Zip cannot open file"; 493 return E_FAIL; 494 } 495 } 496 RINOK(NCompress::CopyStream(sfxStream, sfxOutStream, NULL)); 497 if (outStreamSpec) 498 { 499 RINOK(outStreamSpec->Close()); 500 } 501 } 502 503 HRESULT result = outArchive->UpdateItems(outStream, updatePairs2.Size(), updateCallback); 504 callback->Finilize(); 505 RINOK(result); 506 if (outStreamSpec) 507 result = outStreamSpec->Close(); 508 else if (volStreamSpec) 509 result = volStreamSpec->Close(); 510 return result; 511 } 512 513 HRESULT EnumerateInArchiveItems(const NWildcard::CCensor &censor, 514 const CArc &arc, 515 CObjectVector<CArcItem> &arcItems) 516 { 517 arcItems.Clear(); 518 UInt32 numItems; 519 IInArchive *archive = arc.Archive; 520 RINOK(archive->GetNumberOfItems(&numItems)); 521 arcItems.Reserve(numItems); 522 for (UInt32 i = 0; i < numItems; i++) 523 { 524 CArcItem ai; 525 526 RINOK(arc.GetItemPath(i, ai.Name)); 527 RINOK(IsArchiveItemFolder(archive, i, ai.IsDir)); 528 ai.Censored = censor.CheckPath(ai.Name, !ai.IsDir); 529 RINOK(arc.GetItemMTime(i, ai.MTime, ai.MTimeDefined)); 530 531 { 532 CPropVariant prop; 533 RINOK(archive->GetProperty(i, kpidSize, &prop)); 534 ai.SizeDefined = (prop.vt != VT_EMPTY); 535 if (ai.SizeDefined) 536 ai.Size = ConvertPropVariantToUInt64(prop); 537 } 538 539 { 540 CPropVariant prop; 541 RINOK(archive->GetProperty(i, kpidTimeType, &prop)); 542 if (prop.vt == VT_UI4) 543 { 544 ai.TimeType = (int)(NFileTimeType::EEnum)prop.ulVal; 545 switch(ai.TimeType) 546 { 547 case NFileTimeType::kWindows: 548 case NFileTimeType::kUnix: 549 case NFileTimeType::kDOS: 550 break; 551 default: 552 return E_FAIL; 553 } 554 } 555 } 556 557 ai.IndexInServer = i; 558 arcItems.Add(ai); 559 } 560 return S_OK; 561 } 562 563 564 static HRESULT UpdateWithItemLists( 565 CCodecs *codecs, 566 CUpdateOptions &options, 567 IInArchive *archive, 568 const CObjectVector<CArcItem> &arcItems, 569 CDirItems &dirItems, 570 CTempFiles &tempFiles, 571 CUpdateErrorInfo &errorInfo, 572 IUpdateCallbackUI2 *callback) 573 { 574 for(int i = 0; i < options.Commands.Size(); i++) 575 { 576 CUpdateArchiveCommand &command = options.Commands[i]; 577 if (options.StdOutMode) 578 { 579 RINOK(callback->StartArchive(L"stdout", archive != 0)); 580 } 581 else 582 { 583 RINOK(callback->StartArchive(command.ArchivePath.GetFinalPath(), 584 i == 0 && options.UpdateArchiveItself && archive != 0)); 585 } 586 587 RINOK(Compress( 588 codecs, 589 command.ActionSet, archive, 590 options.MethodMode, 591 command.ArchivePath, 592 arcItems, 593 options.OpenShareForWrite, 594 options.StdInMode, 595 /* options.StdInFileName, */ 596 options.StdOutMode, 597 dirItems, 598 options.SfxMode, options.SfxModule, 599 options.VolumesSizes, 600 tempFiles, 601 errorInfo, callback)); 602 603 RINOK(callback->FinishArchive()); 604 } 605 return S_OK; 606 } 607 608 #if defined(_WIN32) && !defined(UNDER_CE) 609 class CCurrentDirRestorer 610 { 611 UString _path; 612 public: 613 CCurrentDirRestorer() { NFile::NDirectory::MyGetCurrentDirectory(_path); } 614 ~CCurrentDirRestorer() { RestoreDirectory();} 615 bool RestoreDirectory() { return BOOLToBool(NFile::NDirectory::MySetCurrentDirectory(_path)); } 616 }; 617 #endif 618 619 struct CEnumDirItemUpdateCallback: public IEnumDirItemCallback 620 { 621 IUpdateCallbackUI2 *Callback; 622 HRESULT ScanProgress(UInt64 numFolders, UInt64 numFiles, const wchar_t *path) 623 { 624 return Callback->ScanProgress(numFolders, numFiles, path); 625 } 626 }; 627 628 #ifdef _WIN32 629 typedef ULONG (FAR PASCAL MY_MAPISENDDOCUMENTS)( 630 ULONG_PTR ulUIParam, 631 LPSTR lpszDelimChar, 632 LPSTR lpszFilePaths, 633 LPSTR lpszFileNames, 634 ULONG ulReserved 635 ); 636 typedef MY_MAPISENDDOCUMENTS FAR *MY_LPMAPISENDDOCUMENTS; 637 #endif 638 639 HRESULT UpdateArchive( 640 CCodecs *codecs, 641 const NWildcard::CCensor &censor, 642 CUpdateOptions &options, 643 CUpdateErrorInfo &errorInfo, 644 IOpenCallbackUI *openCallback, 645 IUpdateCallbackUI2 *callback) 646 { 647 if (options.StdOutMode && options.EMailMode) 648 return E_FAIL; 649 650 if (options.VolumesSizes.Size() > 0 && (options.EMailMode || options.SfxMode)) 651 return E_NOTIMPL; 652 653 if (options.SfxMode) 654 { 655 CProperty property; 656 property.Name = L"rsfx"; 657 property.Value = L"on"; 658 options.MethodMode.Properties.Add(property); 659 if (options.SfxModule.IsEmpty()) 660 { 661 errorInfo.Message = L"SFX file is not specified"; 662 return E_FAIL; 663 } 664 UString name = options.SfxModule; 665 #ifdef UNDER_CE 666 if (!NFind::DoesFileExist(name)) 667 #else 668 if (!NDirectory::MySearchPath(NULL, name, NULL, options.SfxModule)) 669 #endif 670 { 671 errorInfo.SystemError = ::GetLastError(); 672 errorInfo.Message = L"7-Zip cannot find specified SFX module"; 673 errorInfo.FileName = name; 674 return E_FAIL; 675 } 676 } 677 678 679 CArchiveLink arcLink; 680 const UString arcPath = options.ArchivePath.GetFinalPath(); 681 682 if (!options.ArchivePath.OriginalPath.IsEmpty()) 683 { 684 NFind::CFileInfoW fi; 685 if (fi.Find(arcPath)) 686 { 687 if (fi.IsDir()) 688 throw "there is no such archive"; 689 if (options.VolumesSizes.Size() > 0) 690 return E_NOTIMPL; 691 CIntVector formatIndices; 692 if (options.MethodMode.FormatIndex >= 0) 693 formatIndices.Add(options.MethodMode.FormatIndex); 694 HRESULT result = arcLink.Open2(codecs, formatIndices, false, NULL, arcPath, openCallback); 695 if (result == E_ABORT) 696 return result; 697 RINOK(callback->OpenResult(arcPath, result)); 698 RINOK(result); 699 if (arcLink.VolumePaths.Size() > 1) 700 { 701 errorInfo.SystemError = (DWORD)E_NOTIMPL; 702 errorInfo.Message = L"Updating for multivolume archives is not implemented"; 703 return E_NOTIMPL; 704 } 705 706 CArc &arc = arcLink.Arcs.Back(); 707 arc.MTimeDefined = !fi.IsDevice; 708 arc.MTime = fi.MTime; 709 } 710 } 711 else 712 { 713 /* 714 if (archiveType.IsEmpty()) 715 throw "type of archive is not specified"; 716 */ 717 } 718 719 CDirItems dirItems; 720 if (options.StdInMode) 721 { 722 CDirItem di; 723 di.Name = options.StdInFileName; 724 di.Size = (UInt64)(Int64)-1; 725 di.Attrib = 0; 726 NTime::GetCurUtcFileTime(di.MTime); 727 di.CTime = di.ATime = di.MTime; 728 dirItems.Items.Add(di); 729 } 730 else 731 { 732 bool needScanning = false; 733 for(int i = 0; i < options.Commands.Size(); i++) 734 if (options.Commands[i].ActionSet.NeedScanning()) 735 needScanning = true; 736 if (needScanning) 737 { 738 CEnumDirItemUpdateCallback enumCallback; 739 enumCallback.Callback = callback; 740 RINOK(callback->StartScanning()); 741 UStringVector errorPaths; 742 CRecordVector<DWORD> errorCodes; 743 HRESULT res = EnumerateItems(censor, dirItems, &enumCallback, errorPaths, errorCodes); 744 for (int i = 0; i < errorPaths.Size(); i++) 745 { 746 RINOK(callback->CanNotFindError(errorPaths[i], errorCodes[i])); 747 } 748 if (res != S_OK) 749 { 750 if (res != E_ABORT) 751 errorInfo.Message = L"Scanning error"; 752 return res; 753 } 754 RINOK(callback->FinishScanning()); 755 } 756 } 757 758 UString tempDirPrefix; 759 bool usesTempDir = false; 760 761 #ifdef _WIN32 762 NDirectory::CTempDirectoryW tempDirectory; 763 if (options.EMailMode && options.EMailRemoveAfter) 764 { 765 tempDirectory.Create(kTempFolderPrefix); 766 tempDirPrefix = tempDirectory.GetPath(); 767 NormalizeDirPathPrefix(tempDirPrefix); 768 usesTempDir = true; 769 } 770 #endif 771 772 CTempFiles tempFiles; 773 774 bool createTempFile = false; 775 776 bool thereIsInArchive = arcLink.IsOpen; 777 778 if (!options.StdOutMode && options.UpdateArchiveItself) 779 { 780 CArchivePath &ap = options.Commands[0].ArchivePath; 781 ap = options.ArchivePath; 782 // if ((archive != 0 && !usesTempDir) || !options.WorkingDir.IsEmpty()) 783 if ((thereIsInArchive || !options.WorkingDir.IsEmpty()) && !usesTempDir && options.VolumesSizes.Size() == 0) 784 { 785 createTempFile = true; 786 ap.Temp = true; 787 if (!options.WorkingDir.IsEmpty()) 788 { 789 ap.TempPrefix = options.WorkingDir; 790 NormalizeDirPathPrefix(ap.TempPrefix); 791 } 792 } 793 } 794 795 for(int i = 0; i < options.Commands.Size(); i++) 796 { 797 CArchivePath &ap = options.Commands[i].ArchivePath; 798 if (usesTempDir) 799 { 800 // Check it 801 ap.Prefix = tempDirPrefix; 802 // ap.Temp = true; 803 // ap.TempPrefix = tempDirPrefix; 804 } 805 if (!options.StdOutMode && 806 (i > 0 || !createTempFile)) 807 { 808 const UString &path = ap.GetFinalPath(); 809 if (NFind::DoesFileOrDirExist(path)) 810 { 811 errorInfo.SystemError = 0; 812 errorInfo.Message = L"The file already exists"; 813 errorInfo.FileName = path; 814 return E_FAIL; 815 } 816 } 817 } 818 819 CObjectVector<CArcItem> arcItems; 820 if (thereIsInArchive) 821 { 822 RINOK(EnumerateInArchiveItems(censor, arcLink.Arcs.Back(), arcItems)); 823 } 824 825 RINOK(UpdateWithItemLists(codecs, options, 826 thereIsInArchive ? arcLink.GetArchive() : 0, 827 arcItems, dirItems, 828 tempFiles, errorInfo, callback)); 829 830 if (thereIsInArchive) 831 { 832 RINOK(arcLink.Close()); 833 arcLink.Release(); 834 } 835 836 tempFiles.Paths.Clear(); 837 if (createTempFile) 838 { 839 try 840 { 841 CArchivePath &ap = options.Commands[0].ArchivePath; 842 const UString &tempPath = ap.GetTempPath(); 843 if (thereIsInArchive) 844 if (!NDirectory::DeleteFileAlways(arcPath)) 845 { 846 errorInfo.SystemError = ::GetLastError(); 847 errorInfo.Message = L"7-Zip cannot delete the file"; 848 errorInfo.FileName = arcPath; 849 return E_FAIL; 850 } 851 if (!NDirectory::MyMoveFile(tempPath, arcPath)) 852 { 853 errorInfo.SystemError = ::GetLastError(); 854 errorInfo.Message = L"7-Zip cannot move the file"; 855 errorInfo.FileName = tempPath; 856 errorInfo.FileName2 = arcPath; 857 return E_FAIL; 858 } 859 } 860 catch(...) 861 { 862 throw; 863 } 864 } 865 866 #if defined(_WIN32) && !defined(UNDER_CE) 867 if (options.EMailMode) 868 { 869 NDLL::CLibrary mapiLib; 870 if (!mapiLib.Load(TEXT("Mapi32.dll"))) 871 { 872 errorInfo.SystemError = ::GetLastError(); 873 errorInfo.Message = L"7-Zip cannot load Mapi32.dll"; 874 return E_FAIL; 875 } 876 MY_LPMAPISENDDOCUMENTS fnSend = (MY_LPMAPISENDDOCUMENTS)mapiLib.GetProc("MAPISendDocuments"); 877 if (fnSend == 0) 878 { 879 errorInfo.SystemError = ::GetLastError(); 880 errorInfo.Message = L"7-Zip cannot find MAPISendDocuments function"; 881 return E_FAIL; 882 } 883 UStringVector fullPaths; 884 int i; 885 for(i = 0; i < options.Commands.Size(); i++) 886 { 887 CArchivePath &ap = options.Commands[i].ArchivePath; 888 UString arcPath; 889 if (!NFile::NDirectory::MyGetFullPathName(ap.GetFinalPath(), arcPath)) 890 { 891 errorInfo.SystemError = ::GetLastError(); 892 errorInfo.Message = L"GetFullPathName error"; 893 return E_FAIL; 894 } 895 fullPaths.Add(arcPath); 896 } 897 CCurrentDirRestorer curDirRestorer; 898 for(i = 0; i < fullPaths.Size(); i++) 899 { 900 UString arcPath = fullPaths[i]; 901 UString fileName = ExtractFileNameFromPath(arcPath); 902 AString path = GetAnsiString(arcPath); 903 AString name = GetAnsiString(fileName); 904 // Warning!!! MAPISendDocuments function changes Current directory 905 fnSend(0, ";", (LPSTR)(LPCSTR)path, (LPSTR)(LPCSTR)name, 0); 906 } 907 } 908 #endif 909 return S_OK; 910 } 911