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
Init()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
Close()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
Write(const void * data,UInt32 size,UInt32 * processedSize)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
Seek(Int64 offset,UInt32 seekOrigin,UInt64 * newPosition)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
SetSize(UInt64 newSize)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
Init(const CCodecs * codecs,const CIntVector & formatIndices,const UString & arcPath)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
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 UString & sfxModule,const CRecordVector<UInt64> & volumesSizes,CTempFiles & tempFiles,CUpdateErrorInfo & errorInfo,IUpdateCallbackUI * callback)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
EnumerateInArchiveItems(const NWildcard::CCensor & censor,const CArc & arc,CObjectVector<CArcItem> & arcItems)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
UpdateWithItemLists(CCodecs * codecs,CUpdateOptions & options,IInArchive * archive,const CObjectVector<CArcItem> & arcItems,CDirItems & dirItems,CTempFiles & tempFiles,CUpdateErrorInfo & errorInfo,IUpdateCallbackUI2 * callback)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:
CCurrentDirRestorer()613 CCurrentDirRestorer() { NFile::NDirectory::MyGetCurrentDirectory(_path); }
~CCurrentDirRestorer()614 ~CCurrentDirRestorer() { RestoreDirectory();}
RestoreDirectory()615 bool RestoreDirectory() { return BOOLToBool(NFile::NDirectory::MySetCurrentDirectory(_path)); }
616 };
617 #endif
618
619 struct CEnumDirItemUpdateCallback: public IEnumDirItemCallback
620 {
621 IUpdateCallbackUI2 *Callback;
ScanProgressCEnumDirItemUpdateCallback622 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
UpdateArchive(CCodecs * codecs,const NWildcard::CCensor & censor,CUpdateOptions & options,CUpdateErrorInfo & errorInfo,IOpenCallbackUI * openCallback,IUpdateCallbackUI2 * callback)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