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