1 // Update.cpp
2
3 #include "StdAfx.h"
4
5 // #include <stdio.h>
6
7 #include "Update.h"
8
9 #include "../../../Common/StringConvert.h"
10
11 #include "../../../Windows/DLL.h"
12 #include "../../../Windows/FileDir.h"
13 #include "../../../Windows/FileFind.h"
14 #include "../../../Windows/FileName.h"
15 #include "../../../Windows/PropVariant.h"
16 #include "../../../Windows/PropVariantConv.h"
17 #include "../../../Windows/TimeUtils.h"
18
19 #include "../../Common/FileStreams.h"
20 #include "../../Common/LimitedStreams.h"
21 #include "../../Common/MultiOutStream.h"
22 #include "../../Common/StreamUtils.h"
23
24 #include "../../Compress/CopyCoder.h"
25
26 #include "../Common/DirItem.h"
27 #include "../Common/EnumDirItems.h"
28 #include "../Common/OpenArchive.h"
29 #include "../Common/UpdateProduce.h"
30
31 #include "EnumDirItems.h"
32 #include "SetProperties.h"
33 #include "TempFiles.h"
34 #include "UpdateCallback.h"
35
36 static const char * const kUpdateIsNotSupoorted =
37 "update operations are not supported for this archive";
38
39 static const char * const kUpdateIsNotSupported_MultiVol =
40 "Updating for multivolume archives is not implemented";
41
42 using namespace NWindows;
43 using namespace NCOM;
44 using namespace NFile;
45 using namespace NDir;
46 using namespace NName;
47
48 #ifdef _WIN32
49 static CFSTR const kTempFolderPrefix = FTEXT("7zE");
50 #endif
51
SetFromLastError(const char * message)52 void CUpdateErrorInfo::SetFromLastError(const char *message)
53 {
54 SystemError = ::GetLastError();
55 Message = message;
56 }
57
SetFromLastError(const char * message,const FString & fileName)58 HRESULT CUpdateErrorInfo::SetFromLastError(const char *message, const FString &fileName)
59 {
60 SetFromLastError(message);
61 FileNames.Add(fileName);
62 return Get_HRESULT_Error();
63 }
64
SetFromError_DWORD(const char * message,const FString & fileName,DWORD error)65 HRESULT CUpdateErrorInfo::SetFromError_DWORD(const char *message, const FString &fileName, DWORD error)
66 {
67 Message = message;
68 FileNames.Add(fileName);
69 SystemError = error;
70 return Get_HRESULT_Error();
71 }
72
73
74 using namespace NUpdateArchive;
75
76 struct CMultiOutStream_Rec
77 {
78 CMultiOutStream *Spec;
79 CMyComPtr<IOutStream> Ref;
80 };
81
82 struct CMultiOutStream_Bunch
83 {
84 CObjectVector<CMultiOutStream_Rec> Items;
85
DestructCMultiOutStream_Bunch86 HRESULT Destruct()
87 {
88 HRESULT hres = S_OK;
89 FOR_VECTOR (i, Items)
90 {
91 CMultiOutStream_Rec &rec = Items[i];
92 if (rec.Ref)
93 {
94 const HRESULT hres2 = rec.Spec->Destruct();
95 if (hres == S_OK)
96 hres = hres2;
97 }
98 }
99 Items.Clear();
100 return hres;
101 }
102
DisableDeletionCMultiOutStream_Bunch103 void DisableDeletion()
104 {
105 FOR_VECTOR (i, Items)
106 {
107 CMultiOutStream_Rec &rec = Items[i];
108 if (rec.Ref)
109 rec.Spec->NeedDelete = false;
110 }
111 }
112 };
113
114
ParseFromPath(const UString & path,EArcNameMode mode)115 void CArchivePath::ParseFromPath(const UString &path, EArcNameMode mode)
116 {
117 OriginalPath = path;
118
119 SplitPathToParts_2(path, Prefix, Name);
120
121 if (mode == k_ArcNameMode_Add)
122 return;
123
124 if (mode != k_ArcNameMode_Exact)
125 {
126 int dotPos = Name.ReverseFind_Dot();
127 if (dotPos < 0)
128 return;
129 if ((unsigned)dotPos == Name.Len() - 1)
130 Name.DeleteBack();
131 else
132 {
133 const UString ext = Name.Ptr((unsigned)(dotPos + 1));
134 if (BaseExtension.IsEqualTo_NoCase(ext))
135 {
136 BaseExtension = ext;
137 Name.DeleteFrom((unsigned)dotPos);
138 return;
139 }
140 }
141 }
142
143 BaseExtension.Empty();
144 }
145
GetFinalPath() const146 UString CArchivePath::GetFinalPath() const
147 {
148 UString path = GetPathWithoutExt();
149 if (!BaseExtension.IsEmpty())
150 {
151 path.Add_Dot();
152 path += BaseExtension;
153 }
154 return path;
155 }
156
GetFinalVolPath() const157 UString CArchivePath::GetFinalVolPath() const
158 {
159 UString path = GetPathWithoutExt();
160 // if BaseExtension is empty, we must ignore VolExtension also.
161 if (!BaseExtension.IsEmpty())
162 {
163 path.Add_Dot();
164 path += VolExtension;
165 }
166 return path;
167 }
168
GetTempPath() const169 FString CArchivePath::GetTempPath() const
170 {
171 FString path = TempPrefix;
172 path += us2fs(Name);
173 if (!BaseExtension.IsEmpty())
174 {
175 path.Add_Dot();
176 path += us2fs(BaseExtension);
177 }
178 path += ".tmp";
179 path += TempPostfix;
180 return path;
181 }
182
183 static const char * const kDefaultArcType = "7z";
184 static const char * const kDefaultArcExt = "7z";
185 static const char * const kSFXExtension =
186 #ifdef _WIN32
187 "exe";
188 #else
189 "";
190 #endif
191
InitFormatIndex(const CCodecs * codecs,const CObjectVector<COpenType> & types,const UString & arcPath)192 bool CUpdateOptions::InitFormatIndex(const CCodecs *codecs,
193 const CObjectVector<COpenType> &types, const UString &arcPath)
194 {
195 if (types.Size() > 1)
196 return false;
197 // int arcTypeIndex = -1;
198 if (types.Size() != 0)
199 {
200 MethodMode.Type = types[0];
201 MethodMode.Type_Defined = true;
202 }
203 if (MethodMode.Type.FormatIndex < 0)
204 {
205 // MethodMode.Type = -1;
206 MethodMode.Type = COpenType();
207 if (ArcNameMode != k_ArcNameMode_Add)
208 {
209 MethodMode.Type.FormatIndex = codecs->FindFormatForArchiveName(arcPath);
210 if (MethodMode.Type.FormatIndex >= 0)
211 MethodMode.Type_Defined = true;
212 }
213 }
214 return true;
215 }
216
SetArcPath(const CCodecs * codecs,const UString & arcPath)217 bool CUpdateOptions::SetArcPath(const CCodecs *codecs, const UString &arcPath)
218 {
219 UString typeExt;
220 int formatIndex = MethodMode.Type.FormatIndex;
221 if (formatIndex < 0)
222 {
223 typeExt = kDefaultArcExt;
224 }
225 else
226 {
227 const CArcInfoEx &arcInfo = codecs->Formats[(unsigned)formatIndex];
228 if (!arcInfo.UpdateEnabled)
229 return false;
230 typeExt = arcInfo.GetMainExt();
231 }
232 UString ext = typeExt;
233 if (SfxMode)
234 ext = kSFXExtension;
235 ArchivePath.BaseExtension = ext;
236 ArchivePath.VolExtension = typeExt;
237 ArchivePath.ParseFromPath(arcPath, ArcNameMode);
238 FOR_VECTOR (i, Commands)
239 {
240 CUpdateArchiveCommand &uc = Commands[i];
241 uc.ArchivePath.BaseExtension = ext;
242 uc.ArchivePath.VolExtension = typeExt;
243 uc.ArchivePath.ParseFromPath(uc.UserArchivePath, ArcNameMode);
244 }
245 return true;
246 }
247
248
249 struct CUpdateProduceCallbackImp Z7_final: public IUpdateProduceCallback
250 {
251 const CObjectVector<CArcItem> *_arcItems;
252 CDirItemsStat *_stat;
253 IUpdateCallbackUI *_callback;
254
CUpdateProduceCallbackImpZ7_final255 CUpdateProduceCallbackImp(
256 const CObjectVector<CArcItem> *a,
257 CDirItemsStat *stat,
258 IUpdateCallbackUI *callback):
259 _arcItems(a),
260 _stat(stat),
261 _callback(callback) {}
262
263 virtual HRESULT ShowDeleteFile(unsigned arcIndex) Z7_override;
264 };
265
266
ShowDeleteFile(unsigned arcIndex)267 HRESULT CUpdateProduceCallbackImp::ShowDeleteFile(unsigned arcIndex)
268 {
269 const CArcItem &ai = (*_arcItems)[arcIndex];
270 {
271 CDirItemsStat &stat = *_stat;
272 if (ai.IsDir)
273 stat.NumDirs++;
274 else if (ai.IsAltStream)
275 {
276 stat.NumAltStreams++;
277 stat.AltStreamsSize += ai.Size;
278 }
279 else
280 {
281 stat.NumFiles++;
282 stat.FilesSize += ai.Size;
283 }
284 }
285 return _callback->ShowDeleteFile(ai.Name, ai.IsDir);
286 }
287
Prepare()288 bool CRenamePair::Prepare()
289 {
290 if (RecursedType != NRecursedType::kNonRecursed)
291 return false;
292 if (!WildcardParsing)
293 return true;
294 return !DoesNameContainWildcard(OldName);
295 }
296
297 extern bool g_CaseSensitive;
298
CompareTwoNames(const wchar_t * s1,const wchar_t * s2)299 static unsigned CompareTwoNames(const wchar_t *s1, const wchar_t *s2)
300 {
301 for (unsigned i = 0;; i++)
302 {
303 wchar_t c1 = s1[i];
304 wchar_t c2 = s2[i];
305 if (c1 == 0 || c2 == 0)
306 return i;
307 if (c1 == c2)
308 continue;
309 if (!g_CaseSensitive && (MyCharUpper(c1) == MyCharUpper(c2)))
310 continue;
311 if (IsPathSepar(c1) && IsPathSepar(c2))
312 continue;
313 return i;
314 }
315 }
316
GetNewPath(bool isFolder,const UString & src,UString & dest) const317 bool CRenamePair::GetNewPath(bool isFolder, const UString &src, UString &dest) const
318 {
319 unsigned num = CompareTwoNames(OldName, src);
320 if (OldName[num] == 0)
321 {
322 if (src[num] != 0 && !IsPathSepar(src[num]) && num != 0 && !IsPathSepar(src[num - 1]))
323 return false;
324 }
325 else
326 {
327 // OldName[num] != 0
328 // OldName = "1\1a.txt"
329 // src = "1"
330
331 if (!isFolder
332 || src[num] != 0
333 || !IsPathSepar(OldName[num])
334 || OldName[num + 1] != 0)
335 return false;
336 }
337 dest = NewName + src.Ptr(num);
338 return true;
339 }
340
341 #ifdef SUPPORT_ALT_STREAMS
342 int FindAltStreamColon_in_Path(const wchar_t *path);
343 #endif
344
345
346
Compress(const CUpdateOptions & options,bool isUpdatingItself,CCodecs * codecs,const CActionSet & actionSet,const CArc * arc,CArchivePath & archivePath,const CObjectVector<CArcItem> & arcItems,Byte * processedItemsStatuses,const CDirItems & dirItems,const CDirItem * parentDirItem,CTempFiles & tempFiles,CMultiOutStream_Bunch & multiStreams,CUpdateErrorInfo & errorInfo,IUpdateCallbackUI * callback,CFinishArchiveStat & st)347 static HRESULT Compress(
348 const CUpdateOptions &options,
349 bool isUpdatingItself,
350 CCodecs *codecs,
351 const CActionSet &actionSet,
352 const CArc *arc,
353 CArchivePath &archivePath,
354 const CObjectVector<CArcItem> &arcItems,
355 Byte *processedItemsStatuses,
356 const CDirItems &dirItems,
357 const CDirItem *parentDirItem,
358 CTempFiles &tempFiles,
359 CMultiOutStream_Bunch &multiStreams,
360 CUpdateErrorInfo &errorInfo,
361 IUpdateCallbackUI *callback,
362 CFinishArchiveStat &st)
363 {
364 CMyComPtr<IOutArchive> outArchive;
365 int formatIndex = options.MethodMode.Type.FormatIndex;
366
367 if (arc)
368 {
369 formatIndex = arc->FormatIndex;
370 if (formatIndex < 0)
371 return E_NOTIMPL;
372 CMyComPtr<IInArchive> archive2 = arc->Archive;
373 HRESULT result = archive2.QueryInterface(IID_IOutArchive, &outArchive);
374 if (result != S_OK)
375 throw kUpdateIsNotSupoorted;
376 }
377 else
378 {
379 RINOK(codecs->CreateOutArchive((unsigned)formatIndex, outArchive))
380
381 #ifdef Z7_EXTERNAL_CODECS
382 {
383 CMyComPtr<ISetCompressCodecsInfo> setCompressCodecsInfo;
384 outArchive.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo);
385 if (setCompressCodecsInfo)
386 {
387 RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(codecs))
388 }
389 }
390 #endif
391 }
392
393 if (!outArchive)
394 throw kUpdateIsNotSupoorted;
395
396 // we need to set properties to get fileTimeType.
397 RINOK(SetProperties(outArchive, options.MethodMode.Properties))
398
399 NFileTimeType::EEnum fileTimeType;
400 {
401 /*
402 how we compare file_in_archive::MTime with dirItem.MTime
403 for GetUpdatePairInfoList():
404
405 if (kpidMTime is not defined), external MTime of archive is used.
406
407 before 22.00:
408 if (kpidTimeType is defined)
409 {
410 kpidTimeType is used as precision.
411 (kpidTimeType > kDOS) is not allowed.
412 }
413 else GetFileTimeType() value is used as precision.
414
415 22.00:
416 if (kpidMTime is defined)
417 {
418 if (kpidMTime::precision != 0), then kpidMTime::precision is used as precision.
419 else
420 {
421 if (kpidTimeType is defined), kpidTimeType is used as precision.
422 else GetFileTimeType() value is used as precision.
423 }
424 }
425 else external MTime of archive is used as precision.
426 */
427
428 UInt32 value;
429 RINOK(outArchive->GetFileTimeType(&value))
430
431 // we support any future fileType here.
432 fileTimeType = (NFileTimeType::EEnum)value;
433
434 /*
435 old 21.07 code:
436 switch (value)
437 {
438 case NFileTimeType::kWindows:
439 case NFileTimeType::kUnix:
440 case NFileTimeType::kDOS:
441 fileTimeType = (NFileTimeType::EEnum)value;
442 break;
443 default:
444 return E_FAIL;
445 }
446 */
447 }
448
449 // bool noTimestampExpected = false;
450 {
451 const CArcInfoEx &arcInfo = codecs->Formats[(unsigned)formatIndex];
452
453 // if (arcInfo.Flags_KeepName()) noTimestampExpected = true;
454 if (arcInfo.Is_Xz() ||
455 arcInfo.Is_BZip2())
456 {
457 /* 7-zip before 22.00 returns NFileTimeType::kUnix for xz and bzip2,
458 but we want to set timestamp without reduction to unix. */
459 // noTimestampExpected = true;
460 fileTimeType = NFileTimeType::kNotDefined; // it means not defined
461 }
462
463 if (options.AltStreams.Val && !arcInfo.Flags_AltStreams())
464 return E_NOTIMPL;
465 if (options.NtSecurity.Val && !arcInfo.Flags_NtSecurity())
466 return E_NOTIMPL;
467 if (options.DeleteAfterCompressing && arcInfo.Flags_HashHandler())
468 return E_NOTIMPL;
469 }
470
471 CRecordVector<CUpdatePair2> updatePairs2;
472
473 UStringVector newNames;
474
475 CArcToDoStat stat2;
476
477 if (options.RenamePairs.Size() != 0)
478 {
479 FOR_VECTOR (i, arcItems)
480 {
481 const CArcItem &ai = arcItems[i];
482 bool needRename = false;
483 UString dest;
484
485 if (ai.Censored)
486 {
487 FOR_VECTOR (j, options.RenamePairs)
488 {
489 const CRenamePair &rp = options.RenamePairs[j];
490 if (rp.GetNewPath(ai.IsDir, ai.Name, dest))
491 {
492 needRename = true;
493 break;
494 }
495
496 #ifdef SUPPORT_ALT_STREAMS
497 if (ai.IsAltStream)
498 {
499 int colonPos = FindAltStreamColon_in_Path(ai.Name);
500 if (colonPos >= 0)
501 {
502 UString mainName = ai.Name.Left((unsigned)colonPos);
503 /*
504 actually we must improve that code to support cases
505 with folder renaming like: rn arc dir1\ dir2\
506 */
507 if (rp.GetNewPath(false, mainName, dest))
508 {
509 needRename = true;
510 dest += ':';
511 dest += ai.Name.Ptr((unsigned)(colonPos + 1));
512 break;
513 }
514 }
515 }
516 #endif
517 }
518 }
519
520 CUpdatePair2 up2;
521 up2.SetAs_NoChangeArcItem(ai.IndexInServer);
522 if (needRename)
523 {
524 up2.NewProps = true;
525 RINOK(arc->IsItem_Anti(i, up2.IsAnti))
526 up2.NewNameIndex = (int)newNames.Add(dest);
527 }
528 updatePairs2.Add(up2);
529 }
530 }
531 else
532 {
533 CRecordVector<CUpdatePair> updatePairs;
534 GetUpdatePairInfoList(dirItems, arcItems, fileTimeType, updatePairs); // must be done only once!!!
535 CUpdateProduceCallbackImp upCallback(&arcItems, &stat2.DeleteData, callback);
536
537 UpdateProduce(updatePairs, actionSet, updatePairs2, isUpdatingItself ? &upCallback : NULL);
538 }
539
540 {
541 FOR_VECTOR (i, updatePairs2)
542 {
543 const CUpdatePair2 &up = updatePairs2[i];
544
545 // 17.01: anti-item is (up.NewData && (p.UseArcProps in most cases))
546
547 if (up.NewData && !up.UseArcProps)
548 {
549 if (up.ExistOnDisk())
550 {
551 CDirItemsStat2 &stat = stat2.NewData;
552 const CDirItem &di = dirItems.Items[(unsigned)up.DirIndex];
553 if (di.IsDir())
554 {
555 if (up.IsAnti)
556 stat.Anti_NumDirs++;
557 else
558 stat.NumDirs++;
559 }
560 #ifdef _WIN32
561 else if (di.IsAltStream)
562 {
563 if (up.IsAnti)
564 stat.Anti_NumAltStreams++;
565 else
566 {
567 stat.NumAltStreams++;
568 stat.AltStreamsSize += di.Size;
569 }
570 }
571 #endif
572 else
573 {
574 if (up.IsAnti)
575 stat.Anti_NumFiles++;
576 else
577 {
578 stat.NumFiles++;
579 stat.FilesSize += di.Size;
580 }
581 }
582 }
583 }
584 else if (up.ArcIndex >= 0)
585 {
586 CDirItemsStat2 &stat = *(up.NewData ? &stat2.NewData : &stat2.OldData);
587 const CArcItem &ai = arcItems[(unsigned)up.ArcIndex];
588 if (ai.IsDir)
589 {
590 if (up.IsAnti)
591 stat.Anti_NumDirs++;
592 else
593 stat.NumDirs++;
594 }
595 else if (ai.IsAltStream)
596 {
597 if (up.IsAnti)
598 stat.Anti_NumAltStreams++;
599 else
600 {
601 stat.NumAltStreams++;
602 stat.AltStreamsSize += ai.Size;
603 }
604 }
605 else
606 {
607 if (up.IsAnti)
608 stat.Anti_NumFiles++;
609 else
610 {
611 stat.NumFiles++;
612 stat.FilesSize += ai.Size;
613 }
614 }
615 }
616 }
617 RINOK(callback->SetNumItems(stat2))
618 }
619
620 CArchiveUpdateCallback *updateCallbackSpec = new CArchiveUpdateCallback;
621 CMyComPtr<IArchiveUpdateCallback> updateCallback(updateCallbackSpec);
622
623 updateCallbackSpec->PreserveATime = options.PreserveATime;
624 updateCallbackSpec->ShareForWrite = options.OpenShareForWrite;
625 updateCallbackSpec->StopAfterOpenError = options.StopAfterOpenError;
626 updateCallbackSpec->StdInMode = options.StdInMode;
627 updateCallbackSpec->Callback = callback;
628
629 if (arc)
630 {
631 // we set Archive to allow to transfer GetProperty requests back to DLL.
632 updateCallbackSpec->Archive = arc->Archive;
633 }
634
635 updateCallbackSpec->DirItems = &dirItems;
636 updateCallbackSpec->ParentDirItem = parentDirItem;
637
638 updateCallbackSpec->StoreNtSecurity = options.NtSecurity.Val;
639 updateCallbackSpec->StoreHardLinks = options.HardLinks.Val;
640 updateCallbackSpec->StoreSymLinks = options.SymLinks.Val;
641 updateCallbackSpec->StoreOwnerName = options.StoreOwnerName.Val;
642 updateCallbackSpec->StoreOwnerId = options.StoreOwnerId.Val;
643
644 updateCallbackSpec->Arc = arc;
645 updateCallbackSpec->ArcItems = &arcItems;
646 updateCallbackSpec->UpdatePairs = &updatePairs2;
647
648 updateCallbackSpec->ProcessedItemsStatuses = processedItemsStatuses;
649
650 {
651 const UString arcPath = archivePath.GetFinalPath();
652 updateCallbackSpec->ArcFileName = ExtractFileNameFromPath(arcPath);
653 }
654
655 if (options.RenamePairs.Size() != 0)
656 updateCallbackSpec->NewNames = &newNames;
657
658 if (options.SetArcMTime)
659 {
660 // updateCallbackSpec->Need_ArcMTime_Report = true;
661 updateCallbackSpec->Need_LatestMTime = true;
662 }
663
664 CMyComPtr<IOutStream> outSeekStream;
665 CMyComPtr<ISequentialOutStream> outStream;
666
667 if (!options.StdOutMode)
668 {
669 FString dirPrefix;
670 if (!GetOnlyDirPrefix(us2fs(archivePath.GetFinalPath()), dirPrefix))
671 throw 1417161;
672 CreateComplexDir(dirPrefix);
673 }
674
675 COutFileStream *outStreamSpec = NULL;
676 CStdOutFileStream *stdOutFileStreamSpec = NULL;
677 CMultiOutStream *volStreamSpec = NULL;
678
679 if (options.VolumesSizes.Size() == 0)
680 {
681 if (options.StdOutMode)
682 {
683 stdOutFileStreamSpec = new CStdOutFileStream;
684 outStream = stdOutFileStreamSpec;
685 }
686 else
687 {
688 outStreamSpec = new COutFileStream;
689 outSeekStream = outStreamSpec;
690 outStream = outSeekStream;
691 bool isOK = false;
692 FString realPath;
693
694 for (unsigned i = 0; i < (1 << 16); i++)
695 {
696 if (archivePath.Temp)
697 {
698 if (i > 0)
699 {
700 archivePath.TempPostfix.Empty();
701 archivePath.TempPostfix.Add_UInt32(i);
702 }
703 realPath = archivePath.GetTempPath();
704 }
705 else
706 realPath = us2fs(archivePath.GetFinalPath());
707 if (outStreamSpec->Create(realPath, false))
708 {
709 tempFiles.Paths.Add(realPath);
710 isOK = true;
711 break;
712 }
713 if (::GetLastError() != ERROR_FILE_EXISTS)
714 break;
715 if (!archivePath.Temp)
716 break;
717 }
718
719 if (!isOK)
720 return errorInfo.SetFromLastError("cannot open file", realPath);
721 }
722 }
723 else
724 {
725 if (options.StdOutMode)
726 return E_FAIL;
727 if (arc && arc->GetGlobalOffset() > 0)
728 return E_NOTIMPL;
729
730 volStreamSpec = new CMultiOutStream();
731 outSeekStream = volStreamSpec;
732 outStream = outSeekStream;
733 volStreamSpec->Prefix = us2fs(archivePath.GetFinalVolPath());
734 volStreamSpec->Prefix.Add_Dot();
735 volStreamSpec->Init(options.VolumesSizes);
736 {
737 CMultiOutStream_Rec &rec = multiStreams.Items.AddNew();
738 rec.Spec = volStreamSpec;
739 rec.Ref = rec.Spec;
740 }
741
742 /*
743 updateCallbackSpec->VolumesSizes = volumesSizes;
744 updateCallbackSpec->VolName = archivePath.Prefix + archivePath.Name;
745 if (!archivePath.VolExtension.IsEmpty())
746 updateCallbackSpec->VolExt = UString('.') + archivePath.VolExtension;
747 */
748 }
749
750 if (options.SfxMode)
751 {
752 CInFileStream *sfxStreamSpec = new CInFileStream;
753 CMyComPtr<IInStream> sfxStream(sfxStreamSpec);
754 if (!sfxStreamSpec->Open(options.SfxModule))
755 return errorInfo.SetFromLastError("cannot open SFX module", options.SfxModule);
756
757 CMyComPtr<ISequentialOutStream> sfxOutStream;
758 COutFileStream *outStreamSpec2 = NULL;
759 if (options.VolumesSizes.Size() == 0)
760 sfxOutStream = outStream;
761 else
762 {
763 outStreamSpec2 = new COutFileStream;
764 sfxOutStream = outStreamSpec2;
765 const FString realPath = us2fs(archivePath.GetFinalPath());
766 if (!outStreamSpec2->Create(realPath, false))
767 return errorInfo.SetFromLastError("cannot open file", realPath);
768 }
769
770 {
771 UInt64 sfxSize;
772 RINOK(sfxStreamSpec->GetSize(&sfxSize))
773 RINOK(callback->WriteSfx(fs2us(options.SfxModule), sfxSize))
774 }
775
776 RINOK(NCompress::CopyStream(sfxStream, sfxOutStream, NULL))
777
778 if (outStreamSpec2)
779 {
780 RINOK(outStreamSpec2->Close())
781 }
782 }
783
784 CMyComPtr<ISequentialOutStream> tailStream;
785
786 if (options.SfxMode || !arc || arc->ArcStreamOffset == 0)
787 tailStream = outStream;
788 else
789 {
790 // Int64 globalOffset = arc->GetGlobalOffset();
791 RINOK(InStream_SeekToBegin(arc->InStream))
792 RINOK(NCompress::CopyStream_ExactSize(arc->InStream, outStream, arc->ArcStreamOffset, NULL))
793 if (options.StdOutMode)
794 tailStream = outStream;
795 else
796 {
797 CTailOutStream *tailStreamSpec = new CTailOutStream;
798 tailStream = tailStreamSpec;
799 tailStreamSpec->Stream = outSeekStream;
800 tailStreamSpec->Offset = arc->ArcStreamOffset;
801 tailStreamSpec->Init();
802 }
803 }
804
805 CFiTime ft;
806 FiTime_Clear(ft);
807 bool ft_Defined = false;
808 {
809 FOR_VECTOR (i, updatePairs2)
810 {
811 const CUpdatePair2 &pair2 = updatePairs2[i];
812 CFiTime ft2;
813 FiTime_Clear(ft2);
814 bool ft2_Defined = false;
815 /* we use full precision of dirItem, if dirItem is defined
816 and (dirItem will be used or dirItem is sameTime in dir and arc */
817 if (pair2.DirIndex >= 0 &&
818 (pair2.NewProps || pair2.IsSameTime))
819 {
820 ft2 = dirItems.Items[(unsigned)pair2.DirIndex].MTime;
821 ft2_Defined = true;
822 }
823 else if (pair2.UseArcProps && pair2.ArcIndex >= 0)
824 {
825 const CArcItem &arcItem = arcItems[(unsigned)pair2.ArcIndex];
826 if (arcItem.MTime.Def)
827 {
828 arcItem.MTime.Write_To_FiTime(ft2);
829 ft2_Defined = true;
830 }
831 }
832 if (ft2_Defined)
833 {
834 if (!ft_Defined || Compare_FiTime(&ft, &ft2) < 0)
835 {
836 ft = ft2;
837 ft_Defined = true;
838 }
839 }
840 }
841 /*
842 if (fileTimeType != NFileTimeType::kNotDefined)
843 FiTime_Normalize_With_Prec(ft, fileTimeType);
844 */
845 }
846
847 if (volStreamSpec && options.SetArcMTime && ft_Defined)
848 {
849 volStreamSpec->MTime = ft;
850 volStreamSpec->MTime_Defined = true;
851 }
852
853 HRESULT result = outArchive->UpdateItems(tailStream, updatePairs2.Size(), updateCallback);
854 // callback->Finalize();
855 RINOK(result)
856
857 if (!updateCallbackSpec->AreAllFilesClosed())
858 {
859 errorInfo.Message = "There are unclosed input file:";
860 errorInfo.FileNames = updateCallbackSpec->_openFiles_Paths;
861 return E_FAIL;
862 }
863
864 if (options.SetArcMTime)
865 {
866 // bool needNormalizeAfterStream;
867 // needParse;
868 /*
869 if (updateCallbackSpec->ArcMTime_WasReported)
870 {
871 isDefined = updateCallbackSpec->Reported_ArcMTime.Def;
872 if (isDefined)
873 updateCallbackSpec->Reported_ArcMTime.Write_To_FiTime(ft);
874 else
875 fileTimeType = NFileTimeType::kNotDefined;
876 }
877 if (!isDefined)
878 */
879 {
880 if (updateCallbackSpec->LatestMTime_Defined)
881 {
882 // CArcTime at = StreamCallback_ArcMTime;
883 // updateCallbackSpec->StreamCallback_ArcMTime.Write_To_FiTime(ft);
884 // we must normalize with precision from archive;
885 if (!ft_Defined || Compare_FiTime(&ft, &updateCallbackSpec->LatestMTime) < 0)
886 ft = updateCallbackSpec->LatestMTime;
887 ft_Defined = true;
888 }
889 /*
890 if (fileTimeType != NFileTimeType::kNotDefined)
891 FiTime_Normalize_With_Prec(ft, fileTimeType);
892 */
893 }
894 // if (ft.dwLowDateTime != 0 || ft.dwHighDateTime != 0)
895 if (ft_Defined)
896 {
897 // we ignore set time errors here.
898 // note that user could move some finished volumes to another folder.
899 if (outStreamSpec)
900 outStreamSpec->SetMTime(&ft);
901 else if (volStreamSpec)
902 volStreamSpec->SetMTime_Final(ft);
903 }
904 }
905
906 if (callback)
907 {
908 UInt64 size = 0;
909 if (outStreamSpec)
910 outStreamSpec->GetSize(&size);
911 else if (stdOutFileStreamSpec)
912 size = stdOutFileStreamSpec->GetSize();
913 else
914 size = volStreamSpec->GetSize();
915
916 st.OutArcFileSize = size;
917 }
918
919 if (outStreamSpec)
920 result = outStreamSpec->Close();
921 else if (volStreamSpec)
922 {
923 result = volStreamSpec->FinalFlush_and_CloseFiles(st.NumVolumes);
924 st.IsMultiVolMode = true;
925 }
926
927 RINOK(result)
928
929 if (processedItemsStatuses)
930 {
931 FOR_VECTOR (i, updatePairs2)
932 {
933 const CUpdatePair2 &up = updatePairs2[i];
934 if (up.NewData && up.DirIndex >= 0)
935 {
936 const CDirItem &di = dirItems.Items[(unsigned)up.DirIndex];
937 if (di.AreReparseData() || (!di.IsDir() && di.Size == 0))
938 processedItemsStatuses[(unsigned)up.DirIndex] = 1;
939 }
940 }
941 }
942
943 return result;
944 }
945
946
947
Censor_AreAllAllowed(const NWildcard::CCensor & censor)948 static bool Censor_AreAllAllowed(const NWildcard::CCensor &censor)
949 {
950 if (censor.Pairs.Size() != 1)
951 return false;
952 const NWildcard::CPair &pair = censor.Pairs[0];
953 /* Censor_CheckPath() ignores (CPair::Prefix).
954 So we also ignore (CPair::Prefix) here */
955 // if (!pair.Prefix.IsEmpty()) return false;
956 return pair.Head.AreAllAllowed();
957 }
958
959 bool CensorNode_CheckPath2(const NWildcard::CCensorNode &node, const CReadArcItem &item, bool &include);
960
Censor_CheckPath(const NWildcard::CCensor & censor,const CReadArcItem & item)961 static bool Censor_CheckPath(const NWildcard::CCensor &censor, const CReadArcItem &item)
962 {
963 bool finded = false;
964 FOR_VECTOR (i, censor.Pairs)
965 {
966 /* (CPair::Prefix) in not used for matching items in archive.
967 So we ignore (CPair::Prefix) here */
968 bool include;
969 if (CensorNode_CheckPath2(censor.Pairs[i].Head, item, include))
970 {
971 // Check it and FIXME !!!!
972 // here we can exclude item via some Pair, that is still allowed by another Pair
973 if (!include)
974 return false;
975 finded = true;
976 }
977 }
978 return finded;
979 }
980
EnumerateInArchiveItems(const NWildcard::CCensor & censor,const CArc & arc,CObjectVector<CArcItem> & arcItems)981 static HRESULT EnumerateInArchiveItems(
982 // bool storeStreamsMode,
983 const NWildcard::CCensor &censor,
984 const CArc &arc,
985 CObjectVector<CArcItem> &arcItems)
986 {
987 arcItems.Clear();
988 UInt32 numItems;
989 IInArchive *archive = arc.Archive;
990 RINOK(archive->GetNumberOfItems(&numItems))
991 arcItems.ClearAndReserve(numItems);
992
993 CReadArcItem item;
994
995 const bool allFilesAreAllowed = Censor_AreAllAllowed(censor);
996
997 for (UInt32 i = 0; i < numItems; i++)
998 {
999 CArcItem ai;
1000
1001 RINOK(arc.GetItem(i, item))
1002 ai.Name = item.Path;
1003 ai.IsDir = item.IsDir;
1004 ai.IsAltStream =
1005 #ifdef SUPPORT_ALT_STREAMS
1006 item.IsAltStream;
1007 #else
1008 false;
1009 #endif
1010
1011 /*
1012 if (!storeStreamsMode && ai.IsAltStream)
1013 continue;
1014 */
1015 if (allFilesAreAllowed)
1016 ai.Censored = true;
1017 else
1018 ai.Censored = Censor_CheckPath(censor, item);
1019
1020 // ai.MTime will be set to archive MTime, if not present in archive item
1021 RINOK(arc.GetItem_MTime(i, ai.MTime))
1022 RINOK(arc.GetItem_Size(i, ai.Size, ai.Size_Defined))
1023
1024 ai.IndexInServer = i;
1025 arcItems.AddInReserved(ai);
1026 }
1027 return S_OK;
1028 }
1029
1030 #if defined(_WIN32) && !defined(UNDER_CE)
1031
1032 #if defined(__MINGW32__) || defined(__MINGW64__)
1033 #include <mapi.h>
1034 #else
1035 #include <MAPI.h>
1036 #endif
1037
1038 extern "C" {
1039
1040 #ifdef MAPI_FORCE_UNICODE
1041
1042 #define Z7_WIN_LPMAPISENDMAILW LPMAPISENDMAILW
1043 #define Z7_WIN_MapiFileDescW MapiFileDescW
1044 #define Z7_WIN_MapiMessageW MapiMessageW
1045 #define Z7_WIN_MapiRecipDescW MapiRecipDescW
1046
1047 #else
1048
1049 typedef struct
1050 {
1051 ULONG ulReserved;
1052 ULONG ulRecipClass;
1053 PWSTR lpszName;
1054 PWSTR lpszAddress;
1055 ULONG ulEIDSize;
1056 PVOID lpEntryID;
1057 } Z7_WIN_MapiRecipDescW, *Z7_WIN_lpMapiRecipDescW;
1058
1059 typedef struct
1060 {
1061 ULONG ulReserved;
1062 ULONG flFlags;
1063 ULONG nPosition;
1064 PWSTR lpszPathName;
1065 PWSTR lpszFileName;
1066 PVOID lpFileType;
1067 } Z7_WIN_MapiFileDescW, *Z7_WIN_lpMapiFileDescW;
1068
1069 typedef struct
1070 {
1071 ULONG ulReserved;
1072 PWSTR lpszSubject;
1073 PWSTR lpszNoteText;
1074 PWSTR lpszMessageType;
1075 PWSTR lpszDateReceived;
1076 PWSTR lpszConversationID;
1077 FLAGS flFlags;
1078 Z7_WIN_lpMapiRecipDescW lpOriginator;
1079 ULONG nRecipCount;
1080 Z7_WIN_lpMapiRecipDescW lpRecips;
1081 ULONG nFileCount;
1082 Z7_WIN_lpMapiFileDescW lpFiles;
1083 } Z7_WIN_MapiMessageW, *Z7_WIN_lpMapiMessageW;
1084
1085 typedef ULONG (FAR PASCAL Z7_WIN_MAPISENDMAILW)(
1086 LHANDLE lhSession,
1087 ULONG_PTR ulUIParam,
1088 Z7_WIN_lpMapiMessageW lpMessage,
1089 FLAGS flFlags,
1090 ULONG ulReserved
1091 );
1092 typedef Z7_WIN_MAPISENDMAILW FAR *Z7_WIN_LPMAPISENDMAILW;
1093
1094 #endif // MAPI_FORCE_UNICODE
1095 }
1096 #endif // _WIN32
1097
1098
UpdateArchive(CCodecs * codecs,const CObjectVector<COpenType> & types,const UString & cmdArcPath2,NWildcard::CCensor & censor,CUpdateOptions & options,CUpdateErrorInfo & errorInfo,IOpenCallbackUI * openCallback,IUpdateCallbackUI2 * callback,bool needSetPath)1099 HRESULT UpdateArchive(
1100 CCodecs *codecs,
1101 const CObjectVector<COpenType> &types,
1102 const UString &cmdArcPath2,
1103 NWildcard::CCensor &censor,
1104 CUpdateOptions &options,
1105 CUpdateErrorInfo &errorInfo,
1106 IOpenCallbackUI *openCallback,
1107 IUpdateCallbackUI2 *callback,
1108 bool needSetPath)
1109 {
1110 if (options.StdOutMode && options.EMailMode)
1111 return E_FAIL;
1112
1113 if (types.Size() > 1)
1114 return E_NOTIMPL;
1115
1116 bool renameMode = !options.RenamePairs.IsEmpty();
1117 if (renameMode)
1118 {
1119 if (options.Commands.Size() != 1)
1120 return E_FAIL;
1121 }
1122
1123 if (options.DeleteAfterCompressing)
1124 {
1125 if (options.Commands.Size() != 1)
1126 return E_NOTIMPL;
1127 const CActionSet &as = options.Commands[0].ActionSet;
1128 for (unsigned i = 2; i < NPairState::kNumValues; i++)
1129 if (as.StateActions[i] != NPairAction::kCompress)
1130 return E_NOTIMPL;
1131 }
1132
1133 censor.AddPathsToCensor(options.PathMode);
1134 #ifdef _WIN32
1135 ConvertToLongNames(censor);
1136 #endif
1137 censor.ExtendExclude();
1138
1139
1140 if (options.VolumesSizes.Size() > 0 && (options.EMailMode /* || options.SfxMode */))
1141 return E_NOTIMPL;
1142
1143 if (options.SfxMode)
1144 {
1145 CProperty property;
1146 property.Name = "rsfx";
1147 options.MethodMode.Properties.Add(property);
1148 if (options.SfxModule.IsEmpty())
1149 {
1150 errorInfo.Message = "SFX file is not specified";
1151 return E_FAIL;
1152 }
1153 bool found = false;
1154 if (options.SfxModule.Find(FCHAR_PATH_SEPARATOR) < 0)
1155 {
1156 const FString fullName = NDLL::GetModuleDirPrefix() + options.SfxModule;
1157 if (NFind::DoesFileExist_FollowLink(fullName))
1158 {
1159 options.SfxModule = fullName;
1160 found = true;
1161 }
1162 }
1163 if (!found)
1164 {
1165 if (!NFind::DoesFileExist_FollowLink(options.SfxModule))
1166 return errorInfo.SetFromLastError("cannot find specified SFX module", options.SfxModule);
1167 }
1168 }
1169
1170 CArchiveLink arcLink;
1171
1172
1173 if (needSetPath)
1174 {
1175 if (!options.InitFormatIndex(codecs, types, cmdArcPath2) ||
1176 !options.SetArcPath(codecs, cmdArcPath2))
1177 return E_NOTIMPL;
1178 }
1179
1180 UString arcPath = options.ArchivePath.GetFinalPath();
1181
1182 if (!options.VolumesSizes.IsEmpty())
1183 {
1184 arcPath = options.ArchivePath.GetFinalVolPath();
1185 arcPath += ".001";
1186 }
1187
1188 if (cmdArcPath2.IsEmpty())
1189 {
1190 if (options.MethodMode.Type.FormatIndex < 0)
1191 throw "type of archive is not specified";
1192 }
1193 else
1194 {
1195 NFind::CFileInfo fi;
1196 if (!fi.Find_FollowLink(us2fs(arcPath)))
1197 {
1198 if (renameMode)
1199 throw "can't find archive";
1200 if (options.MethodMode.Type.FormatIndex < 0)
1201 {
1202 if (!options.SetArcPath(codecs, cmdArcPath2))
1203 return E_NOTIMPL;
1204 }
1205 }
1206 else
1207 {
1208 if (fi.IsDir())
1209 return errorInfo.SetFromError_DWORD("There is a folder with the name of archive",
1210 us2fs(arcPath),
1211 #ifdef _WIN32
1212 ERROR_ACCESS_DENIED
1213 #else
1214 EISDIR
1215 #endif
1216 );
1217 #ifdef _WIN32
1218 if (fi.IsDevice)
1219 return E_NOTIMPL;
1220 #endif
1221
1222 if (!options.StdOutMode && options.UpdateArchiveItself)
1223 if (fi.IsReadOnly())
1224 {
1225 return errorInfo.SetFromError_DWORD("The file is read-only",
1226 us2fs(arcPath),
1227 #ifdef _WIN32
1228 ERROR_ACCESS_DENIED
1229 #else
1230 EACCES
1231 #endif
1232 );
1233 }
1234
1235 if (options.VolumesSizes.Size() > 0)
1236 {
1237 errorInfo.FileNames.Add(us2fs(arcPath));
1238 // errorInfo.SystemError = (DWORD)E_NOTIMPL;
1239 errorInfo.Message = kUpdateIsNotSupported_MultiVol;
1240 return E_NOTIMPL;
1241 }
1242 CObjectVector<COpenType> types2;
1243 // change it.
1244 if (options.MethodMode.Type_Defined)
1245 types2.Add(options.MethodMode.Type);
1246 // We need to set Properties to open archive only in some cases (WIM archives).
1247
1248 CIntVector excl;
1249 COpenOptions op;
1250 #ifndef Z7_SFX
1251 op.props = &options.MethodMode.Properties;
1252 #endif
1253 op.codecs = codecs;
1254 op.types = &types2;
1255 op.excludedFormats = !
1256 op.stdInMode = false;
1257 op.stream = NULL;
1258 op.filePath = arcPath;
1259
1260 RINOK(callback->StartOpenArchive(arcPath))
1261
1262 HRESULT result = arcLink.Open_Strict(op, openCallback);
1263
1264 if (result == E_ABORT)
1265 return result;
1266
1267 HRESULT res2 = callback->OpenResult(codecs, arcLink, arcPath, result);
1268 /*
1269 if (result == S_FALSE)
1270 return E_FAIL;
1271 */
1272 RINOK(res2)
1273 RINOK(result)
1274
1275 if (arcLink.VolumePaths.Size() > 1)
1276 {
1277 // errorInfo.SystemError = (DWORD)E_NOTIMPL;
1278 errorInfo.Message = kUpdateIsNotSupported_MultiVol;
1279 return E_NOTIMPL;
1280 }
1281
1282 CArc &arc = arcLink.Arcs.Back();
1283 arc.MTime.Def =
1284 #ifdef _WIN32
1285 !fi.IsDevice;
1286 #else
1287 true;
1288 #endif
1289 if (arc.MTime.Def)
1290 arc.MTime.Set_From_FiTime(fi.MTime);
1291
1292 if (arc.ErrorInfo.ThereIsTail)
1293 {
1294 // errorInfo.SystemError = (DWORD)E_NOTIMPL;
1295 errorInfo.Message = "There is some data block after the end of the archive";
1296 return E_NOTIMPL;
1297 }
1298 if (options.MethodMode.Type.FormatIndex < 0)
1299 {
1300 options.MethodMode.Type.FormatIndex = arcLink.GetArc()->FormatIndex;
1301 if (!options.SetArcPath(codecs, cmdArcPath2))
1302 return E_NOTIMPL;
1303 }
1304 }
1305 }
1306
1307 if (options.MethodMode.Type.FormatIndex < 0)
1308 {
1309 options.MethodMode.Type.FormatIndex = codecs->FindFormatForArchiveType((UString)kDefaultArcType);
1310 if (options.MethodMode.Type.FormatIndex < 0)
1311 return E_NOTIMPL;
1312 }
1313
1314 bool thereIsInArchive = arcLink.IsOpen;
1315 if (!thereIsInArchive && renameMode)
1316 return E_FAIL;
1317
1318 CDirItems dirItems;
1319 dirItems.Callback = callback;
1320
1321 CDirItem parentDirItem;
1322 CDirItem *parentDirItem_Ptr = NULL;
1323
1324 /*
1325 FStringVector requestedPaths;
1326 FStringVector *requestedPaths_Ptr = NULL;
1327 if (options.DeleteAfterCompressing)
1328 requestedPaths_Ptr = &requestedPaths;
1329 */
1330
1331 if (options.StdInMode)
1332 {
1333 CDirItem di;
1334 di.ClearBase();
1335 di.Name = options.StdInFileName;
1336 di.Size = (UInt64)(Int64)-1;
1337 di.SetAsFile();
1338 NTime::GetCurUtc_FiTime(di.MTime);
1339 di.CTime = di.ATime = di.MTime;
1340 dirItems.Items.Add(di);
1341 }
1342 else
1343 {
1344 bool needScanning = false;
1345
1346 if (!renameMode)
1347 FOR_VECTOR (i, options.Commands)
1348 if (options.Commands[i].ActionSet.NeedScanning())
1349 needScanning = true;
1350
1351 if (needScanning)
1352 {
1353 RINOK(callback->StartScanning())
1354
1355 dirItems.SymLinks = options.SymLinks.Val;
1356
1357 #if defined(_WIN32) && !defined(UNDER_CE)
1358 dirItems.ReadSecure = options.NtSecurity.Val;
1359 #endif
1360
1361 dirItems.ScanAltStreams = options.AltStreams.Val;
1362 dirItems.ExcludeDirItems = censor.ExcludeDirItems;
1363 dirItems.ExcludeFileItems = censor.ExcludeFileItems;
1364
1365 dirItems.ShareForWrite = options.OpenShareForWrite;
1366
1367 #ifndef _WIN32
1368 dirItems.StoreOwnerName = options.StoreOwnerName.Val;
1369 #endif
1370
1371 const HRESULT res = EnumerateItems(censor,
1372 options.PathMode,
1373 UString(), // options.AddPathPrefix,
1374 dirItems);
1375
1376 if (res != S_OK)
1377 {
1378 if (res != E_ABORT)
1379 errorInfo.Message = "Scanning error";
1380 return res;
1381 }
1382
1383 RINOK(callback->FinishScanning(dirItems.Stat))
1384
1385 // 22.00: we don't need parent folder, if absolute path mode
1386 if (options.PathMode != NWildcard::k_AbsPath)
1387 if (censor.Pairs.Size() == 1)
1388 {
1389 NFind::CFileInfo fi;
1390 FString prefix = us2fs(censor.Pairs[0].Prefix);
1391 prefix.Add_Dot();
1392 // UString prefix = censor.Pairs[0].Prefix;
1393 /*
1394 if (prefix.Back() == WCHAR_PATH_SEPARATOR)
1395 {
1396 prefix.DeleteBack();
1397 }
1398 */
1399 if (fi.Find(prefix))
1400 if (fi.IsDir())
1401 {
1402 parentDirItem.Copy_From_FileInfoBase(fi);
1403 parentDirItem_Ptr = &parentDirItem;
1404
1405 int secureIndex = -1;
1406 #if defined(_WIN32) && !defined(UNDER_CE)
1407 if (options.NtSecurity.Val)
1408 dirItems.AddSecurityItem(prefix, secureIndex);
1409 #endif
1410 parentDirItem.SecureIndex = secureIndex;
1411 }
1412 }
1413 }
1414 }
1415
1416 FString tempDirPrefix;
1417 bool usesTempDir = false;
1418
1419 #ifdef _WIN32
1420 CTempDir tempDirectory;
1421 if (options.EMailMode && options.EMailRemoveAfter)
1422 {
1423 tempDirectory.Create(kTempFolderPrefix);
1424 tempDirPrefix = tempDirectory.GetPath();
1425 NormalizeDirPathPrefix(tempDirPrefix);
1426 usesTempDir = true;
1427 }
1428 #endif
1429
1430 CTempFiles tempFiles;
1431
1432 bool createTempFile = false;
1433
1434 if (!options.StdOutMode && options.UpdateArchiveItself)
1435 {
1436 CArchivePath &ap = options.Commands[0].ArchivePath;
1437 ap = options.ArchivePath;
1438 // if ((archive != 0 && !usesTempDir) || !options.WorkingDir.IsEmpty())
1439 if ((thereIsInArchive || !options.WorkingDir.IsEmpty()) && !usesTempDir && options.VolumesSizes.Size() == 0)
1440 {
1441 createTempFile = true;
1442 ap.Temp = true;
1443 if (!options.WorkingDir.IsEmpty())
1444 ap.TempPrefix = options.WorkingDir;
1445 else
1446 ap.TempPrefix = us2fs(ap.Prefix);
1447 NormalizeDirPathPrefix(ap.TempPrefix);
1448 }
1449 }
1450
1451 unsigned ci;
1452
1453
1454 // self including protection
1455 if (options.DeleteAfterCompressing)
1456 {
1457 for (ci = 0; ci < options.Commands.Size(); ci++)
1458 {
1459 CArchivePath &ap = options.Commands[ci].ArchivePath;
1460 const FString path = us2fs(ap.GetFinalPath());
1461 // maybe we must compare absolute paths path here
1462 FOR_VECTOR (i, dirItems.Items)
1463 {
1464 const FString phyPath = dirItems.GetPhyPath(i);
1465 if (phyPath == path)
1466 {
1467 UString s;
1468 s = "It is not allowed to include archive to itself";
1469 s.Add_LF();
1470 s += fs2us(path);
1471 throw s;
1472 }
1473 }
1474 }
1475 }
1476
1477
1478 for (ci = 0; ci < options.Commands.Size(); ci++)
1479 {
1480 CArchivePath &ap = options.Commands[ci].ArchivePath;
1481 if (usesTempDir)
1482 {
1483 // Check it
1484 ap.Prefix = fs2us(tempDirPrefix);
1485 // ap.Temp = true;
1486 // ap.TempPrefix = tempDirPrefix;
1487 }
1488 if (!options.StdOutMode &&
1489 (ci > 0 || !createTempFile))
1490 {
1491 const FString path = us2fs(ap.GetFinalPath());
1492 if (NFind::DoesFileOrDirExist(path))
1493 {
1494 errorInfo.SystemError = ERROR_FILE_EXISTS;
1495 errorInfo.Message = "The file already exists";
1496 errorInfo.FileNames.Add(path);
1497 return errorInfo.Get_HRESULT_Error();
1498 }
1499 }
1500 }
1501
1502 CObjectVector<CArcItem> arcItems;
1503 if (thereIsInArchive)
1504 {
1505 RINOK(EnumerateInArchiveItems(
1506 // options.StoreAltStreams,
1507 censor, arcLink.Arcs.Back(), arcItems))
1508 }
1509
1510 /*
1511 FStringVector processedFilePaths;
1512 FStringVector *processedFilePaths_Ptr = NULL;
1513 if (options.DeleteAfterCompressing)
1514 processedFilePaths_Ptr = &processedFilePaths;
1515 */
1516
1517 CByteBuffer processedItems;
1518 if (options.DeleteAfterCompressing)
1519 {
1520 const unsigned num = dirItems.Items.Size();
1521 processedItems.Alloc(num);
1522 for (unsigned i = 0; i < num; i++)
1523 processedItems[i] = 0;
1524 }
1525
1526 CMultiOutStream_Bunch multiStreams;
1527
1528 /*
1529 #ifndef Z7_NO_CRYPTO
1530 if (arcLink.PasswordWasAsked)
1531 {
1532 // We set password, if open have requested password
1533 RINOK(callback->SetPassword(arcLink.Password));
1534 }
1535 #endif
1536 */
1537
1538 for (ci = 0; ci < options.Commands.Size(); ci++)
1539 {
1540 const CArc *arc = thereIsInArchive ? arcLink.GetArc() : NULL;
1541 CUpdateArchiveCommand &command = options.Commands[ci];
1542 UString name;
1543 bool isUpdating;
1544
1545 if (options.StdOutMode)
1546 {
1547 name = "stdout";
1548 isUpdating = thereIsInArchive;
1549 }
1550 else
1551 {
1552 name = command.ArchivePath.GetFinalPath();
1553 isUpdating = (ci == 0 && options.UpdateArchiveItself && thereIsInArchive);
1554 }
1555
1556 RINOK(callback->StartArchive(name, isUpdating))
1557
1558 CFinishArchiveStat st;
1559
1560 RINOK(Compress(options,
1561 isUpdating,
1562 codecs,
1563 command.ActionSet,
1564 arc,
1565 command.ArchivePath,
1566 arcItems,
1567 options.DeleteAfterCompressing ? (Byte *)processedItems : NULL,
1568
1569 dirItems,
1570 parentDirItem_Ptr,
1571
1572 tempFiles,
1573 multiStreams,
1574 errorInfo, callback, st))
1575
1576 RINOK(callback->FinishArchive(st))
1577 }
1578
1579
1580 if (thereIsInArchive)
1581 {
1582 RINOK(arcLink.Close())
1583 arcLink.Release();
1584 }
1585
1586 multiStreams.DisableDeletion();
1587 RINOK(multiStreams.Destruct())
1588
1589 tempFiles.Paths.Clear();
1590 if (createTempFile)
1591 {
1592 try
1593 {
1594 CArchivePath &ap = options.Commands[0].ArchivePath;
1595 const FString &tempPath = ap.GetTempPath();
1596
1597 // DWORD attrib = 0;
1598 if (thereIsInArchive)
1599 {
1600 // attrib = NFind::GetFileAttrib(us2fs(arcPath));
1601 if (!DeleteFileAlways(us2fs(arcPath)))
1602 return errorInfo.SetFromLastError("cannot delete the file", us2fs(arcPath));
1603 }
1604
1605 if (!MyMoveFile(tempPath, us2fs(arcPath)))
1606 {
1607 errorInfo.SetFromLastError("cannot move the file", tempPath);
1608 errorInfo.FileNames.Add(us2fs(arcPath));
1609 return errorInfo.Get_HRESULT_Error();
1610 }
1611
1612 /*
1613 if (attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_READONLY))
1614 {
1615 DWORD attrib2 = NFind::GetFileAttrib(us2fs(arcPath));
1616 if (attrib2 != INVALID_FILE_ATTRIBUTES)
1617 NDir::SetFileAttrib(us2fs(arcPath), attrib2 | FILE_ATTRIBUTE_READONLY);
1618 }
1619 */
1620 }
1621 catch(...)
1622 {
1623 throw;
1624 }
1625 }
1626
1627
1628 #if defined(_WIN32) && !defined(UNDER_CE)
1629
1630 if (options.EMailMode)
1631 {
1632 NDLL::CLibrary mapiLib;
1633 if (!mapiLib.Load(FTEXT("Mapi32.dll")))
1634 {
1635 errorInfo.SetFromLastError("cannot load Mapi32.dll");
1636 return errorInfo.Get_HRESULT_Error();
1637 }
1638
1639 FStringVector fullPaths;
1640 unsigned i;
1641
1642 for (i = 0; i < options.Commands.Size(); i++)
1643 {
1644 CArchivePath &ap = options.Commands[i].ArchivePath;
1645 const FString finalPath = us2fs(ap.GetFinalPath());
1646 FString arcPath2;
1647 if (!MyGetFullPathName(finalPath, arcPath2))
1648 return errorInfo.SetFromLastError("GetFullPathName error", finalPath);
1649 fullPaths.Add(arcPath2);
1650 }
1651
1652 /*
1653 LPMAPISENDDOCUMENTS fnSend = (LPMAPISENDDOCUMENTS)mapiLib.GetProc("MAPISendDocuments");
1654 if (fnSend == 0)
1655 {
1656 errorInfo.SetFromLastError)("7-Zip cannot find MAPISendDocuments function");
1657 return errorInfo.Get_HRESULT_Error();
1658 }
1659 */
1660 const
1661 Z7_WIN_LPMAPISENDMAILW sendMailW = Z7_GET_PROC_ADDRESS(
1662 Z7_WIN_LPMAPISENDMAILW, mapiLib.Get_HMODULE(),
1663 "MAPISendMailW");
1664 if (sendMailW)
1665 {
1666
1667 CCurrentDirRestorer curDirRestorer;
1668
1669 UStringVector paths;
1670 UStringVector names;
1671
1672 for (i = 0; i < fullPaths.Size(); i++)
1673 {
1674 const UString arcPath2 = fs2us(fullPaths[i]);
1675 const UString fileName = ExtractFileNameFromPath(arcPath2);
1676 paths.Add(arcPath2);
1677 names.Add(fileName);
1678 // Warning!!! MAPISendDocuments function changes Current directory
1679 // fnSend(0, ";", (LPSTR)(LPCSTR)path, (LPSTR)(LPCSTR)name, 0);
1680 }
1681
1682 CRecordVector<Z7_WIN_MapiFileDescW> files;
1683 files.ClearAndSetSize(paths.Size());
1684
1685 for (i = 0; i < paths.Size(); i++)
1686 {
1687 Z7_WIN_MapiFileDescW &f = files[i];
1688 memset(&f, 0, sizeof(f));
1689 f.nPosition = 0xFFFFFFFF;
1690 f.lpszPathName = paths[i].Ptr_non_const();
1691 f.lpszFileName = names[i].Ptr_non_const();
1692 }
1693
1694 {
1695 Z7_WIN_MapiMessageW m;
1696 memset(&m, 0, sizeof(m));
1697 m.nFileCount = files.Size();
1698 m.lpFiles = &files.Front();
1699
1700 const UString addr (options.EMailAddress);
1701 Z7_WIN_MapiRecipDescW rec;
1702 if (!addr.IsEmpty())
1703 {
1704 memset(&rec, 0, sizeof(rec));
1705 rec.ulRecipClass = MAPI_TO;
1706 rec.lpszAddress = addr.Ptr_non_const();
1707 m.nRecipCount = 1;
1708 m.lpRecips = &rec;
1709 }
1710
1711 sendMailW((LHANDLE)0, 0, &m, MAPI_DIALOG, 0);
1712 }
1713 }
1714 else
1715 {
1716 const
1717 LPMAPISENDMAIL sendMail = Z7_GET_PROC_ADDRESS(
1718 LPMAPISENDMAIL, mapiLib.Get_HMODULE(),
1719 "MAPISendMail");
1720 if (!sendMail)
1721 {
1722 errorInfo.SetFromLastError("7-Zip cannot find MAPISendMail function");
1723 return errorInfo.Get_HRESULT_Error();
1724 }
1725
1726 CCurrentDirRestorer curDirRestorer;
1727
1728 AStringVector paths;
1729 AStringVector names;
1730
1731 for (i = 0; i < fullPaths.Size(); i++)
1732 {
1733 const UString arcPath2 = fs2us(fullPaths[i]);
1734 const UString fileName = ExtractFileNameFromPath(arcPath2);
1735 paths.Add(GetAnsiString(arcPath2));
1736 names.Add(GetAnsiString(fileName));
1737 // const AString path (GetAnsiString(arcPath2));
1738 // const AString name (GetAnsiString(fileName));
1739 // Warning!!! MAPISendDocuments function changes Current directory
1740 // fnSend(0, ";", (LPSTR)(LPCSTR)path, (LPSTR)(LPCSTR)name, 0);
1741 }
1742
1743 CRecordVector<MapiFileDesc> files;
1744 files.ClearAndSetSize(paths.Size());
1745
1746 for (i = 0; i < paths.Size(); i++)
1747 {
1748 MapiFileDesc &f = files[i];
1749 memset(&f, 0, sizeof(f));
1750 f.nPosition = 0xFFFFFFFF;
1751 f.lpszPathName = paths[i].Ptr_non_const();
1752 f.lpszFileName = names[i].Ptr_non_const();
1753 }
1754
1755 {
1756 MapiMessage m;
1757 memset(&m, 0, sizeof(m));
1758 m.nFileCount = files.Size();
1759 m.lpFiles = &files.Front();
1760
1761 const AString addr (GetAnsiString(options.EMailAddress));
1762 MapiRecipDesc rec;
1763 if (!addr.IsEmpty())
1764 {
1765 memset(&rec, 0, sizeof(rec));
1766 rec.ulRecipClass = MAPI_TO;
1767 rec.lpszAddress = addr.Ptr_non_const();
1768 m.nRecipCount = 1;
1769 m.lpRecips = &rec;
1770 }
1771
1772 sendMail((LHANDLE)0, 0, &m, MAPI_DIALOG, 0);
1773 }
1774 }
1775 }
1776
1777 #endif
1778
1779 if (options.DeleteAfterCompressing)
1780 {
1781 CRecordVector<CDirPathSortPair> pairs;
1782 FStringVector foldersNames;
1783
1784 unsigned i;
1785
1786 for (i = 0; i < dirItems.Items.Size(); i++)
1787 {
1788 const CDirItem &dirItem = dirItems.Items[i];
1789 const FString phyPath = dirItems.GetPhyPath(i);
1790 if (dirItem.IsDir())
1791 {
1792 CDirPathSortPair pair;
1793 pair.Index = i;
1794 pair.SetNumSlashes(phyPath);
1795 pairs.Add(pair);
1796 }
1797 else
1798 {
1799 // 21.04: we have set processedItems[*] before for all required items
1800 if (processedItems[i] != 0
1801 // || dirItem.Size == 0
1802 // || dirItem.AreReparseData()
1803 )
1804 {
1805 NFind::CFileInfo fileInfo;
1806 /* if (!SymLinks), we follow link here, similar to (dirItem) filling */
1807 if (fileInfo.Find(phyPath, !options.SymLinks.Val))
1808 {
1809 bool is_SameSize = false;
1810 if (options.SymLinks.Val && dirItem.AreReparseData())
1811 {
1812 /* (dirItem.Size = dirItem.ReparseData.Size()) was set before.
1813 So we don't compare sizes for that case here */
1814 is_SameSize = fileInfo.IsOsSymLink();
1815 }
1816 else
1817 is_SameSize = (fileInfo.Size == dirItem.Size);
1818
1819 if (is_SameSize
1820 && Compare_FiTime(&fileInfo.MTime, &dirItem.MTime) == 0
1821 && Compare_FiTime(&fileInfo.CTime, &dirItem.CTime) == 0)
1822 {
1823 RINOK(callback->DeletingAfterArchiving(phyPath, false))
1824 DeleteFileAlways(phyPath);
1825 }
1826 }
1827 }
1828 else
1829 {
1830 // file was skipped by some reason. We can throw error for debug:
1831 /*
1832 errorInfo.SystemError = 0;
1833 errorInfo.Message = "file was not processed";
1834 errorInfo.FileNames.Add(phyPath);
1835 return E_FAIL;
1836 */
1837 }
1838 }
1839 }
1840
1841 pairs.Sort2();
1842
1843 for (i = 0; i < pairs.Size(); i++)
1844 {
1845 const FString phyPath = dirItems.GetPhyPath(pairs[i].Index);
1846 if (NFind::DoesDirExist(phyPath))
1847 {
1848 RINOK(callback->DeletingAfterArchiving(phyPath, true))
1849 RemoveDir(phyPath);
1850 }
1851 }
1852
1853 RINOK(callback->FinishDeletingAfterArchiving())
1854 }
1855
1856 return S_OK;
1857 }
1858