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