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)(Int64 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(Int64 newSize)207 STDMETHODIMP COutMultiVolStream::SetSize(Int64 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 const UString &archiveName = archivePath.GetFinalPath();
391 if (!stdOutMode)
392 {
393 UString resultPath;
394 int pos;
395 if (!NFile::NDirectory::MyGetFullPathName(archiveName, resultPath, pos))
396 throw 1417161;
397 NFile::NDirectory::CreateComplexDirectory(resultPath.Left(pos));
398 }
399
400 COutFileStream *outStreamSpec = NULL;
401 COutMultiVolStream *volStreamSpec = NULL;
402
403 if (volumesSizes.Size() == 0)
404 {
405 if (stdOutMode)
406 outStream = new CStdOutFileStream;
407 else
408 {
409 outStreamSpec = new COutFileStream;
410 outStream = outStreamSpec;
411 bool isOK = false;
412 UString realPath;
413 for (int i = 0; i < (1 << 16); i++)
414 {
415 if (archivePath.Temp)
416 {
417 if (i > 0)
418 {
419 wchar_t s[16];
420 ConvertUInt32ToString(i, s);
421 archivePath.TempPostfix = s;
422 }
423 realPath = archivePath.GetTempPath();
424 }
425 else
426 realPath = archivePath.GetFinalPath();
427 if (outStreamSpec->Create(realPath, false))
428 {
429 tempFiles.Paths.Add(realPath);
430 isOK = true;
431 break;
432 }
433 if (::GetLastError() != ERROR_FILE_EXISTS)
434 break;
435 if (!archivePath.Temp)
436 break;
437 }
438 if (!isOK)
439 {
440 errorInfo.SystemError = ::GetLastError();
441 errorInfo.FileName = realPath;
442 errorInfo.Message = L"Can not open file";
443 return E_FAIL;
444 }
445 }
446 }
447 else
448 {
449 if (stdOutMode)
450 return E_FAIL;
451 volStreamSpec = new COutMultiVolStream;
452 outStream = volStreamSpec;
453 volStreamSpec->Sizes = volumesSizes;
454 volStreamSpec->Prefix = archivePath.GetFinalPath() + UString(L".");
455 volStreamSpec->TempFiles = &tempFiles;
456 volStreamSpec->Init();
457
458 /*
459 updateCallbackSpec->VolumesSizes = volumesSizes;
460 updateCallbackSpec->VolName = archivePath.Prefix + archivePath.Name;
461 if (!archivePath.VolExtension.IsEmpty())
462 updateCallbackSpec->VolExt = UString(L'.') + archivePath.VolExtension;
463 */
464 }
465
466 RINOK(SetProperties(outArchive, compressionMethod.Properties));
467
468 if (sfxMode)
469 {
470 CInFileStream *sfxStreamSpec = new CInFileStream;
471 CMyComPtr<IInStream> sfxStream(sfxStreamSpec);
472 if (!sfxStreamSpec->Open(sfxModule))
473 {
474 errorInfo.SystemError = ::GetLastError();
475 errorInfo.Message = L"Can't open sfx module";
476 errorInfo.FileName = sfxModule;
477 return E_FAIL;
478 }
479
480 CMyComPtr<ISequentialOutStream> sfxOutStream;
481 COutFileStream *outStreamSpec = NULL;
482 if (volumesSizes.Size() == 0)
483 sfxOutStream = outStream;
484 else
485 {
486 outStreamSpec = new COutFileStream;
487 sfxOutStream = outStreamSpec;
488 UString realPath = archivePath.GetFinalPath();
489 if (!outStreamSpec->Create(realPath, false))
490 {
491 errorInfo.SystemError = ::GetLastError();
492 errorInfo.FileName = realPath;
493 errorInfo.Message = L"Can not open file";
494 return E_FAIL;
495 }
496 }
497 RINOK(NCompress::CopyStream(sfxStream, sfxOutStream, NULL));
498 if (outStreamSpec)
499 {
500 RINOK(outStreamSpec->Close());
501 }
502 }
503
504 HRESULT result = outArchive->UpdateItems(outStream, updatePairs2.Size(), updateCallback);
505 callback->Finilize();
506 RINOK(result);
507 if (outStreamSpec)
508 result = outStreamSpec->Close();
509 else if (volStreamSpec)
510 result = volStreamSpec->Close();
511 return result;
512 }
513
EnumerateInArchiveItems(const NWildcard::CCensor & censor,const CArc & arc,CObjectVector<CArcItem> & arcItems)514 HRESULT EnumerateInArchiveItems(const NWildcard::CCensor &censor,
515 const CArc &arc,
516 CObjectVector<CArcItem> &arcItems)
517 {
518 arcItems.Clear();
519 UInt32 numItems;
520 IInArchive *archive = arc.Archive;
521 RINOK(archive->GetNumberOfItems(&numItems));
522 arcItems.Reserve(numItems);
523 for (UInt32 i = 0; i < numItems; i++)
524 {
525 CArcItem ai;
526
527 RINOK(arc.GetItemPath(i, ai.Name));
528 RINOK(IsArchiveItemFolder(archive, i, ai.IsDir));
529 ai.Censored = censor.CheckPath(ai.Name, !ai.IsDir);
530 RINOK(arc.GetItemMTime(i, ai.MTime, ai.MTimeDefined));
531
532 {
533 CPropVariant prop;
534 RINOK(archive->GetProperty(i, kpidSize, &prop));
535 ai.SizeDefined = (prop.vt != VT_EMPTY);
536 if (ai.SizeDefined)
537 ai.Size = ConvertPropVariantToUInt64(prop);
538 }
539
540 {
541 CPropVariant prop;
542 RINOK(archive->GetProperty(i, kpidTimeType, &prop));
543 if (prop.vt == VT_UI4)
544 {
545 ai.TimeType = (int)(NFileTimeType::EEnum)prop.ulVal;
546 switch(ai.TimeType)
547 {
548 case NFileTimeType::kWindows:
549 case NFileTimeType::kUnix:
550 case NFileTimeType::kDOS:
551 break;
552 default:
553 return E_FAIL;
554 }
555 }
556 }
557
558 ai.IndexInServer = i;
559 arcItems.Add(ai);
560 }
561 return S_OK;
562 }
563
564
UpdateWithItemLists(CCodecs * codecs,CUpdateOptions & options,IInArchive * archive,const CObjectVector<CArcItem> & arcItems,CDirItems & dirItems,CTempFiles & tempFiles,CUpdateErrorInfo & errorInfo,IUpdateCallbackUI2 * callback)565 static HRESULT UpdateWithItemLists(
566 CCodecs *codecs,
567 CUpdateOptions &options,
568 IInArchive *archive,
569 const CObjectVector<CArcItem> &arcItems,
570 CDirItems &dirItems,
571 CTempFiles &tempFiles,
572 CUpdateErrorInfo &errorInfo,
573 IUpdateCallbackUI2 *callback)
574 {
575 for(int i = 0; i < options.Commands.Size(); i++)
576 {
577 CUpdateArchiveCommand &command = options.Commands[i];
578 if (options.StdOutMode)
579 {
580 RINOK(callback->StartArchive(L"stdout", archive != 0));
581 }
582 else
583 {
584 RINOK(callback->StartArchive(command.ArchivePath.GetFinalPath(),
585 i == 0 && options.UpdateArchiveItself && archive != 0));
586 }
587
588 RINOK(Compress(
589 codecs,
590 command.ActionSet, archive,
591 options.MethodMode,
592 command.ArchivePath,
593 arcItems,
594 options.OpenShareForWrite,
595 options.StdInMode,
596 /* options.StdInFileName, */
597 options.StdOutMode,
598 dirItems,
599 options.SfxMode, options.SfxModule,
600 options.VolumesSizes,
601 tempFiles,
602 errorInfo, callback));
603
604 RINOK(callback->FinishArchive());
605 }
606 return S_OK;
607 }
608
609 #ifdef _WIN32
610 class CCurrentDirRestorer
611 {
612 UString m_CurrentDirectory;
613 public:
CCurrentDirRestorer()614 CCurrentDirRestorer()
615 { NFile::NDirectory::MyGetCurrentDirectory(m_CurrentDirectory); }
~CCurrentDirRestorer()616 ~CCurrentDirRestorer()
617 { RestoreDirectory();}
RestoreDirectory()618 bool RestoreDirectory()
619 { return BOOLToBool(NFile::NDirectory::MySetCurrentDirectory(m_CurrentDirectory)); }
620 };
621 #endif
622
623 struct CEnumDirItemUpdateCallback: public IEnumDirItemCallback
624 {
625 IUpdateCallbackUI2 *Callback;
ScanProgressCEnumDirItemUpdateCallback626 HRESULT ScanProgress(UInt64 numFolders, UInt64 numFiles, const wchar_t *path)
627 {
628 return Callback->ScanProgress(numFolders, numFiles, path);
629 }
630 };
631
632 #ifdef _WIN32
633 typedef ULONG (FAR PASCAL MY_MAPISENDDOCUMENTS)(
634 ULONG_PTR ulUIParam,
635 LPSTR lpszDelimChar,
636 LPSTR lpszFilePaths,
637 LPSTR lpszFileNames,
638 ULONG ulReserved
639 );
640 typedef MY_MAPISENDDOCUMENTS FAR *MY_LPMAPISENDDOCUMENTS;
641 #endif
642
UpdateArchive(CCodecs * codecs,const NWildcard::CCensor & censor,CUpdateOptions & options,CUpdateErrorInfo & errorInfo,IOpenCallbackUI * openCallback,IUpdateCallbackUI2 * callback)643 HRESULT UpdateArchive(
644 CCodecs *codecs,
645 const NWildcard::CCensor &censor,
646 CUpdateOptions &options,
647 CUpdateErrorInfo &errorInfo,
648 IOpenCallbackUI *openCallback,
649 IUpdateCallbackUI2 *callback)
650 {
651 if (options.StdOutMode && options.EMailMode)
652 return E_FAIL;
653
654 if (options.VolumesSizes.Size() > 0 && (options.EMailMode || options.SfxMode))
655 return E_NOTIMPL;
656
657 if (options.SfxMode)
658 {
659 CProperty property;
660 property.Name = L"rsfx";
661 property.Value = L"on";
662 options.MethodMode.Properties.Add(property);
663 if (options.SfxModule.IsEmpty())
664 {
665 errorInfo.Message = L"sfx file is not specified";
666 return E_FAIL;
667 }
668 UString name = options.SfxModule;
669 if (!NDirectory::MySearchPath(NULL, name, NULL, options.SfxModule))
670 {
671 errorInfo.Message = L"can't find specified sfx module";
672 return E_FAIL;
673 }
674 }
675
676 const UString archiveName = options.ArchivePath.GetFinalPath();
677
678 CArchiveLink archiveLink;
679 NFind::CFileInfoW archiveFileInfo;
680
681 if (archiveFileInfo.Find(archiveName))
682 {
683 if (archiveFileInfo.IsDir())
684 throw "there is no such archive";
685 if (options.VolumesSizes.Size() > 0)
686 return E_NOTIMPL;
687 CIntVector formatIndices;
688 if (options.MethodMode.FormatIndex >= 0)
689 formatIndices.Add(options.MethodMode.FormatIndex);
690 HRESULT result = archiveLink.Open2(codecs, formatIndices, false, NULL, archiveName, openCallback);
691 if (result == E_ABORT)
692 return result;
693 RINOK(callback->OpenResult(archiveName, result));
694 RINOK(result);
695 if (archiveLink.VolumePaths.Size() > 1)
696 {
697 errorInfo.SystemError = (DWORD)E_NOTIMPL;
698 errorInfo.Message = L"Updating for multivolume archives is not implemented";
699 return E_NOTIMPL;
700 }
701
702 CArc &arc = archiveLink.Arcs.Back();
703 arc.MTimeDefined = !archiveFileInfo.IsDevice;
704 arc.MTime = archiveFileInfo.MTime;
705 }
706 else
707 {
708 /*
709 if (archiveType.IsEmpty())
710 throw "type of archive is not specified";
711 */
712 }
713
714 CDirItems dirItems;
715 if (options.StdInMode)
716 {
717 CDirItem di;
718 di.Name = options.StdInFileName;
719 di.Size = (UInt64)(Int64)-1;
720 di.Attrib = 0;
721 NTime::GetCurUtcFileTime(di.MTime);
722 di.CTime = di.ATime = di.MTime;
723 dirItems.Items.Add(di);
724 }
725 else
726 {
727 bool needScanning = false;
728 for(int i = 0; i < options.Commands.Size(); i++)
729 if (options.Commands[i].ActionSet.NeedScanning())
730 needScanning = true;
731 if (needScanning)
732 {
733 CEnumDirItemUpdateCallback enumCallback;
734 enumCallback.Callback = callback;
735 RINOK(callback->StartScanning());
736 UStringVector errorPaths;
737 CRecordVector<DWORD> errorCodes;
738 HRESULT res = EnumerateItems(censor, dirItems, &enumCallback, errorPaths, errorCodes);
739 for (int i = 0; i < errorPaths.Size(); i++)
740 {
741 RINOK(callback->CanNotFindError(errorPaths[i], errorCodes[i]));
742 }
743 if (res != S_OK)
744 {
745 if (res != E_ABORT)
746 errorInfo.Message = L"Scanning error";
747 // errorInfo.FileName = errorPath;
748 return res;
749 }
750 RINOK(callback->FinishScanning());
751 }
752 }
753
754 UString tempDirPrefix;
755 bool usesTempDir = false;
756
757 #ifdef _WIN32
758 NDirectory::CTempDirectoryW tempDirectory;
759 if (options.EMailMode && options.EMailRemoveAfter)
760 {
761 tempDirectory.Create(kTempFolderPrefix);
762 tempDirPrefix = tempDirectory.GetPath();
763 NormalizeDirPathPrefix(tempDirPrefix);
764 usesTempDir = true;
765 }
766 #endif
767
768 CTempFiles tempFiles;
769
770 bool createTempFile = false;
771
772 bool thereIsInArchive = archiveLink.IsOpen;
773
774 if (!options.StdOutMode && options.UpdateArchiveItself)
775 {
776 CArchivePath &ap = options.Commands[0].ArchivePath;
777 ap = options.ArchivePath;
778 // if ((archive != 0 && !usesTempDir) || !options.WorkingDir.IsEmpty())
779 if ((thereIsInArchive || !options.WorkingDir.IsEmpty()) && !usesTempDir && options.VolumesSizes.Size() == 0)
780 {
781 createTempFile = true;
782 ap.Temp = true;
783 if (!options.WorkingDir.IsEmpty())
784 {
785 ap.TempPrefix = options.WorkingDir;
786 NormalizeDirPathPrefix(ap.TempPrefix);
787 }
788 }
789 }
790
791 for(int i = 0; i < options.Commands.Size(); i++)
792 {
793 CArchivePath &ap = options.Commands[i].ArchivePath;
794 if (usesTempDir)
795 {
796 // Check it
797 ap.Prefix = tempDirPrefix;
798 // ap.Temp = true;
799 // ap.TempPrefix = tempDirPrefix;
800 }
801 if (i > 0 || !createTempFile)
802 {
803 const UString &path = ap.GetFinalPath();
804 if (NFind::DoesFileOrDirExist(path))
805 {
806 errorInfo.SystemError = 0;
807 errorInfo.Message = L"File already exists";
808 errorInfo.FileName = path;
809 return E_FAIL;
810 }
811 }
812 }
813
814 CObjectVector<CArcItem> arcItems;
815 if (thereIsInArchive)
816 {
817 RINOK(EnumerateInArchiveItems(censor, archiveLink.Arcs.Back(), arcItems));
818 }
819
820 RINOK(UpdateWithItemLists(codecs, options,
821 thereIsInArchive ? archiveLink.GetArchive() : 0,
822 arcItems, dirItems,
823 tempFiles, errorInfo, callback));
824
825 if (thereIsInArchive)
826 {
827 RINOK(archiveLink.Close());
828 archiveLink.Release();
829 }
830
831 tempFiles.Paths.Clear();
832 if (createTempFile)
833 {
834 try
835 {
836 CArchivePath &ap = options.Commands[0].ArchivePath;
837 const UString &tempPath = ap.GetTempPath();
838 if (thereIsInArchive)
839 if (!NDirectory::DeleteFileAlways(archiveName))
840 {
841 errorInfo.SystemError = ::GetLastError();
842 errorInfo.Message = L"delete file error";
843 errorInfo.FileName = archiveName;
844 return E_FAIL;
845 }
846 if (!NDirectory::MyMoveFile(tempPath, archiveName))
847 {
848 errorInfo.SystemError = ::GetLastError();
849 errorInfo.Message = L"move file error";
850 errorInfo.FileName = tempPath;
851 errorInfo.FileName2 = archiveName;
852 return E_FAIL;
853 }
854 }
855 catch(...)
856 {
857 throw;
858 }
859 }
860
861 #ifdef _WIN32
862 if (options.EMailMode)
863 {
864 NDLL::CLibrary mapiLib;
865 if (!mapiLib.Load(TEXT("Mapi32.dll")))
866 {
867 errorInfo.SystemError = ::GetLastError();
868 errorInfo.Message = L"can not load Mapi32.dll";
869 return E_FAIL;
870 }
871 MY_LPMAPISENDDOCUMENTS fnSend = (MY_LPMAPISENDDOCUMENTS)
872 mapiLib.GetProcAddress("MAPISendDocuments");
873 if (fnSend == 0)
874 {
875 errorInfo.SystemError = ::GetLastError();
876 errorInfo.Message = L"can not find MAPISendDocuments function";
877 return E_FAIL;
878 }
879 UStringVector fullPaths;
880 int i;
881 for(i = 0; i < options.Commands.Size(); i++)
882 {
883 CArchivePath &ap = options.Commands[i].ArchivePath;
884 UString arcPath;
885 if (!NFile::NDirectory::MyGetFullPathName(ap.GetFinalPath(), arcPath))
886 {
887 errorInfo.SystemError = ::GetLastError();
888 return E_FAIL;
889 }
890 fullPaths.Add(arcPath);
891 }
892 CCurrentDirRestorer curDirRestorer;
893 for(i = 0; i < fullPaths.Size(); i++)
894 {
895 UString arcPath = fullPaths[i];
896 UString fileName = ExtractFileNameFromPath(arcPath);
897 AString path = GetAnsiString(arcPath);
898 AString name = GetAnsiString(fileName);
899 // Warning!!! MAPISendDocuments function changes Current directory
900 fnSend(0, ";", (LPSTR)(LPCSTR)path, (LPSTR)(LPCSTR)name, 0);
901 }
902 }
903 #endif
904 return S_OK;
905 }
906
907