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