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