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
1049 #ifdef _WIN32
1050 void ConvertToLongNames(NWildcard::CCensor &censor);
1051 #endif
1052
UpdateArchive(CCodecs * codecs,const CObjectVector<COpenType> & types,const UString & cmdArcPath2,NWildcard::CCensor & censor,CUpdateOptions & options,CUpdateErrorInfo & errorInfo,IOpenCallbackUI * openCallback,IUpdateCallbackUI2 * callback,bool needSetPath)1053 HRESULT UpdateArchive(
1054 CCodecs *codecs,
1055 const CObjectVector<COpenType> &types,
1056 const UString &cmdArcPath2,
1057 NWildcard::CCensor &censor,
1058 CUpdateOptions &options,
1059 CUpdateErrorInfo &errorInfo,
1060 IOpenCallbackUI *openCallback,
1061 IUpdateCallbackUI2 *callback,
1062 bool needSetPath)
1063 {
1064 if (options.StdOutMode && options.EMailMode)
1065 return E_FAIL;
1066
1067 if (types.Size() > 1)
1068 return E_NOTIMPL;
1069
1070 bool renameMode = !options.RenamePairs.IsEmpty();
1071 if (renameMode)
1072 {
1073 if (options.Commands.Size() != 1)
1074 return E_FAIL;
1075 }
1076
1077 if (options.DeleteAfterCompressing)
1078 {
1079 if (options.Commands.Size() != 1)
1080 return E_NOTIMPL;
1081 const CActionSet &as = options.Commands[0].ActionSet;
1082 for (int i = 2; i < NPairState::kNumValues; i++)
1083 if (as.StateActions[i] != NPairAction::kCompress)
1084 return E_NOTIMPL;
1085 }
1086
1087 censor.AddPathsToCensor(options.PathMode);
1088 #ifdef _WIN32
1089 ConvertToLongNames(censor);
1090 #endif
1091 censor.ExtendExclude();
1092
1093
1094 if (options.VolumesSizes.Size() > 0 && (options.EMailMode /* || options.SfxMode */))
1095 return E_NOTIMPL;
1096
1097 if (options.SfxMode)
1098 {
1099 CProperty property;
1100 property.Name = "rsfx";
1101 options.MethodMode.Properties.Add(property);
1102 if (options.SfxModule.IsEmpty())
1103 {
1104 errorInfo.Message = "SFX file is not specified";
1105 return E_FAIL;
1106 }
1107 bool found = false;
1108 if (options.SfxModule.Find(FCHAR_PATH_SEPARATOR) < 0)
1109 {
1110 const FString fullName = NDLL::GetModuleDirPrefix() + options.SfxModule;
1111 if (NFind::DoesFileExist(fullName))
1112 {
1113 options.SfxModule = fullName;
1114 found = true;
1115 }
1116 }
1117 if (!found)
1118 {
1119 if (!NFind::DoesFileExist(options.SfxModule))
1120 return errorInfo.SetFromLastError("cannot find specified SFX module", options.SfxModule);
1121 }
1122 }
1123
1124 CArchiveLink arcLink;
1125
1126
1127 if (needSetPath)
1128 {
1129 if (!options.InitFormatIndex(codecs, types, cmdArcPath2) ||
1130 !options.SetArcPath(codecs, cmdArcPath2))
1131 return E_NOTIMPL;
1132 }
1133
1134 UString arcPath = options.ArchivePath.GetFinalPath();
1135
1136 if (!options.VolumesSizes.IsEmpty())
1137 {
1138 arcPath = options.ArchivePath.GetFinalVolPath();
1139 arcPath += '.';
1140 arcPath += "001";
1141 }
1142
1143 if (cmdArcPath2.IsEmpty())
1144 {
1145 if (options.MethodMode.Type.FormatIndex < 0)
1146 throw "type of archive is not specified";
1147 }
1148 else
1149 {
1150 NFind::CFileInfo fi;
1151 if (!fi.Find(us2fs(arcPath)))
1152 {
1153 if (renameMode)
1154 throw "can't find archive";;
1155 if (options.MethodMode.Type.FormatIndex < 0)
1156 {
1157 if (!options.SetArcPath(codecs, cmdArcPath2))
1158 return E_NOTIMPL;
1159 }
1160 }
1161 else
1162 {
1163 if (fi.IsDir())
1164 throw "there is no such archive";
1165 if (fi.IsDevice)
1166 return E_NOTIMPL;
1167
1168 if (!options.StdOutMode && options.UpdateArchiveItself)
1169 if (fi.IsReadOnly())
1170 {
1171 errorInfo.SystemError = ERROR_ACCESS_DENIED;
1172 errorInfo.Message = "The file is read-only";
1173 errorInfo.FileNames.Add(arcPath);
1174 return errorInfo.Get_HRESULT_Error();
1175 }
1176
1177 if (options.VolumesSizes.Size() > 0)
1178 {
1179 errorInfo.FileNames.Add(us2fs(arcPath));
1180 errorInfo.SystemError = (DWORD)E_NOTIMPL;
1181 errorInfo.Message = kUpdateIsNotSupoorted_MultiVol;
1182 return E_NOTIMPL;
1183 }
1184 CObjectVector<COpenType> types2;
1185 // change it.
1186 if (options.MethodMode.Type_Defined)
1187 types2.Add(options.MethodMode.Type);
1188 // We need to set Properties to open archive only in some cases (WIM archives).
1189
1190 CIntVector excl;
1191 COpenOptions op;
1192 #ifndef _SFX
1193 op.props = &options.MethodMode.Properties;
1194 #endif
1195 op.codecs = codecs;
1196 op.types = &types2;
1197 op.excludedFormats = !
1198 op.stdInMode = false;
1199 op.stream = NULL;
1200 op.filePath = arcPath;
1201
1202 RINOK(callback->StartOpenArchive(arcPath));
1203
1204 HRESULT result = arcLink.Open_Strict(op, openCallback);
1205
1206 if (result == E_ABORT)
1207 return result;
1208
1209 HRESULT res2 = callback->OpenResult(codecs, arcLink, arcPath, result);
1210 /*
1211 if (result == S_FALSE)
1212 return E_FAIL;
1213 */
1214 RINOK(res2);
1215 RINOK(result);
1216
1217 if (arcLink.VolumePaths.Size() > 1)
1218 {
1219 errorInfo.SystemError = (DWORD)E_NOTIMPL;
1220 errorInfo.Message = kUpdateIsNotSupoorted_MultiVol;
1221 return E_NOTIMPL;
1222 }
1223
1224 CArc &arc = arcLink.Arcs.Back();
1225 arc.MTimeDefined = !fi.IsDevice;
1226 arc.MTime = fi.MTime;
1227
1228 if (arc.ErrorInfo.ThereIsTail)
1229 {
1230 errorInfo.SystemError = (DWORD)E_NOTIMPL;
1231 errorInfo.Message = "There is some data block after the end of the archive";
1232 return E_NOTIMPL;
1233 }
1234 if (options.MethodMode.Type.FormatIndex < 0)
1235 {
1236 options.MethodMode.Type.FormatIndex = arcLink.GetArc()->FormatIndex;
1237 if (!options.SetArcPath(codecs, cmdArcPath2))
1238 return E_NOTIMPL;
1239 }
1240 }
1241 }
1242
1243 if (options.MethodMode.Type.FormatIndex < 0)
1244 {
1245 options.MethodMode.Type.FormatIndex = codecs->FindFormatForArchiveType((UString)kDefaultArcType);
1246 if (options.MethodMode.Type.FormatIndex < 0)
1247 return E_NOTIMPL;
1248 }
1249
1250 bool thereIsInArchive = arcLink.IsOpen;
1251 if (!thereIsInArchive && renameMode)
1252 return E_FAIL;
1253
1254 CDirItems dirItems;
1255 dirItems.Callback = callback;
1256
1257 CDirItem parentDirItem;
1258 CDirItem *parentDirItem_Ptr = NULL;
1259
1260 /*
1261 FStringVector requestedPaths;
1262 FStringVector *requestedPaths_Ptr = NULL;
1263 if (options.DeleteAfterCompressing)
1264 requestedPaths_Ptr = &requestedPaths;
1265 */
1266
1267 if (options.StdInMode)
1268 {
1269 CDirItem di;
1270 di.Name = options.StdInFileName;
1271 di.Size = (UInt64)(Int64)-1;
1272 di.Attrib = 0;
1273 NTime::GetCurUtcFileTime(di.MTime);
1274 di.CTime = di.ATime = di.MTime;
1275 dirItems.Items.Add(di);
1276 }
1277 else
1278 {
1279 bool needScanning = false;
1280
1281 if (!renameMode)
1282 FOR_VECTOR (i, options.Commands)
1283 if (options.Commands[i].ActionSet.NeedScanning())
1284 needScanning = true;
1285
1286 if (needScanning)
1287 {
1288 RINOK(callback->StartScanning());
1289
1290 dirItems.SymLinks = options.SymLinks.Val;
1291
1292 #if defined(_WIN32) && !defined(UNDER_CE)
1293 dirItems.ReadSecure = options.NtSecurity.Val;
1294 #endif
1295
1296 dirItems.ScanAltStreams = options.AltStreams.Val;
1297
1298 HRESULT res = EnumerateItems(censor,
1299 options.PathMode,
1300 options.AddPathPrefix,
1301 dirItems);
1302
1303 if (res != S_OK)
1304 {
1305 if (res != E_ABORT)
1306 errorInfo.Message = "Scanning error";
1307 return res;
1308 }
1309
1310 RINOK(callback->FinishScanning(dirItems.Stat));
1311
1312 if (censor.Pairs.Size() == 1)
1313 {
1314 NFind::CFileInfo fi;
1315 FString prefix = us2fs(censor.Pairs[0].Prefix);
1316 prefix += '.';
1317 // UString prefix = censor.Pairs[0].Prefix;
1318 /*
1319 if (prefix.Back() == WCHAR_PATH_SEPARATOR)
1320 {
1321 prefix.DeleteBack();
1322 }
1323 */
1324 if (fi.Find(prefix))
1325 if (fi.IsDir())
1326 {
1327 parentDirItem.Size = fi.Size;
1328 parentDirItem.CTime = fi.CTime;
1329 parentDirItem.ATime = fi.ATime;
1330 parentDirItem.MTime = fi.MTime;
1331 parentDirItem.Attrib = fi.Attrib;
1332 parentDirItem_Ptr = &parentDirItem;
1333
1334 int secureIndex = -1;
1335 #if defined(_WIN32) && !defined(UNDER_CE)
1336 if (options.NtSecurity.Val)
1337 dirItems.AddSecurityItem(prefix, secureIndex);
1338 #endif
1339 parentDirItem.SecureIndex = secureIndex;
1340
1341 parentDirItem_Ptr = &parentDirItem;
1342 }
1343 }
1344 }
1345 }
1346
1347 FString tempDirPrefix;
1348 bool usesTempDir = false;
1349
1350 #ifdef _WIN32
1351 CTempDir tempDirectory;
1352 if (options.EMailMode && options.EMailRemoveAfter)
1353 {
1354 tempDirectory.Create(kTempFolderPrefix);
1355 tempDirPrefix = tempDirectory.GetPath();
1356 NormalizeDirPathPrefix(tempDirPrefix);
1357 usesTempDir = true;
1358 }
1359 #endif
1360
1361 CTempFiles tempFiles;
1362
1363 bool createTempFile = false;
1364
1365 if (!options.StdOutMode && options.UpdateArchiveItself)
1366 {
1367 CArchivePath &ap = options.Commands[0].ArchivePath;
1368 ap = options.ArchivePath;
1369 // if ((archive != 0 && !usesTempDir) || !options.WorkingDir.IsEmpty())
1370 if ((thereIsInArchive || !options.WorkingDir.IsEmpty()) && !usesTempDir && options.VolumesSizes.Size() == 0)
1371 {
1372 createTempFile = true;
1373 ap.Temp = true;
1374 if (!options.WorkingDir.IsEmpty())
1375 ap.TempPrefix = options.WorkingDir;
1376 else
1377 ap.TempPrefix = us2fs(ap.Prefix);
1378 NormalizeDirPathPrefix(ap.TempPrefix);
1379 }
1380 }
1381
1382 unsigned ci;
1383
1384 for (ci = 0; ci < options.Commands.Size(); ci++)
1385 {
1386 CArchivePath &ap = options.Commands[ci].ArchivePath;
1387 if (usesTempDir)
1388 {
1389 // Check it
1390 ap.Prefix = fs2us(tempDirPrefix);
1391 // ap.Temp = true;
1392 // ap.TempPrefix = tempDirPrefix;
1393 }
1394 if (!options.StdOutMode &&
1395 (ci > 0 || !createTempFile))
1396 {
1397 const FString path = us2fs(ap.GetFinalPath());
1398 if (NFind::DoesFileOrDirExist(path))
1399 {
1400 errorInfo.SystemError = ERROR_FILE_EXISTS;
1401 errorInfo.Message = "The file already exists";
1402 errorInfo.FileNames.Add(path);
1403 return errorInfo.Get_HRESULT_Error();
1404 }
1405 }
1406 }
1407
1408 CObjectVector<CArcItem> arcItems;
1409 if (thereIsInArchive)
1410 {
1411 RINOK(EnumerateInArchiveItems(
1412 // options.StoreAltStreams,
1413 censor, arcLink.Arcs.Back(), arcItems));
1414 }
1415
1416 /*
1417 FStringVector processedFilePaths;
1418 FStringVector *processedFilePaths_Ptr = NULL;
1419 if (options.DeleteAfterCompressing)
1420 processedFilePaths_Ptr = &processedFilePaths;
1421 */
1422
1423 CByteBuffer processedItems;
1424 if (options.DeleteAfterCompressing)
1425 {
1426 unsigned num = dirItems.Items.Size();
1427 processedItems.Alloc(num);
1428 for (unsigned i = 0; i < num; i++)
1429 processedItems[i] = 0;
1430 }
1431
1432 /*
1433 #ifndef _NO_CRYPTO
1434 if (arcLink.PasswordWasAsked)
1435 {
1436 // We set password, if open have requested password
1437 RINOK(callback->SetPassword(arcLink.Password));
1438 }
1439 #endif
1440 */
1441
1442 for (ci = 0; ci < options.Commands.Size(); ci++)
1443 {
1444 const CArc *arc = thereIsInArchive ? arcLink.GetArc() : NULL;
1445 CUpdateArchiveCommand &command = options.Commands[ci];
1446 UString name;
1447 bool isUpdating;
1448
1449 if (options.StdOutMode)
1450 {
1451 name = "stdout";
1452 isUpdating = thereIsInArchive;
1453 }
1454 else
1455 {
1456 name = command.ArchivePath.GetFinalPath();
1457 isUpdating = (ci == 0 && options.UpdateArchiveItself && thereIsInArchive);
1458 }
1459
1460 RINOK(callback->StartArchive(name, isUpdating))
1461
1462 CFinishArchiveStat st;
1463
1464 RINOK(Compress(options,
1465 isUpdating,
1466 codecs,
1467 command.ActionSet,
1468 arc,
1469 command.ArchivePath,
1470 arcItems,
1471 options.DeleteAfterCompressing ? (Byte *)processedItems : NULL,
1472
1473 dirItems,
1474 parentDirItem_Ptr,
1475
1476 tempFiles,
1477 errorInfo, callback, st));
1478
1479 RINOK(callback->FinishArchive(st));
1480 }
1481
1482
1483 if (thereIsInArchive)
1484 {
1485 RINOK(arcLink.Close());
1486 arcLink.Release();
1487 }
1488
1489 tempFiles.Paths.Clear();
1490 if (createTempFile)
1491 {
1492 try
1493 {
1494 CArchivePath &ap = options.Commands[0].ArchivePath;
1495 const FString &tempPath = ap.GetTempPath();
1496
1497 // DWORD attrib = 0;
1498 if (thereIsInArchive)
1499 {
1500 // attrib = NFind::GetFileAttrib(us2fs(arcPath));
1501 if (!DeleteFileAlways(us2fs(arcPath)))
1502 return errorInfo.SetFromLastError("cannot delete the file", us2fs(arcPath));
1503 }
1504
1505 if (!MyMoveFile(tempPath, us2fs(arcPath)))
1506 {
1507 errorInfo.SetFromLastError("cannot move the file", tempPath);
1508 errorInfo.FileNames.Add(us2fs(arcPath));
1509 return errorInfo.Get_HRESULT_Error();
1510 }
1511
1512 /*
1513 if (attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_READONLY))
1514 {
1515 DWORD attrib2 = NFind::GetFileAttrib(us2fs(arcPath));
1516 if (attrib2 != INVALID_FILE_ATTRIBUTES)
1517 NDir::SetFileAttrib(us2fs(arcPath), attrib2 | FILE_ATTRIBUTE_READONLY);
1518 }
1519 */
1520 }
1521 catch(...)
1522 {
1523 throw;
1524 }
1525 }
1526
1527
1528 #if defined(_WIN32) && !defined(UNDER_CE)
1529
1530 if (options.EMailMode)
1531 {
1532 NDLL::CLibrary mapiLib;
1533 if (!mapiLib.Load(FTEXT("Mapi32.dll")))
1534 {
1535 errorInfo.SetFromLastError("cannot load Mapi32.dll");
1536 return errorInfo.Get_HRESULT_Error();
1537 }
1538
1539 /*
1540 LPMAPISENDDOCUMENTS fnSend = (LPMAPISENDDOCUMENTS)mapiLib.GetProc("MAPISendDocuments");
1541 if (fnSend == 0)
1542 {
1543 errorInfo.SetFromLastError)("7-Zip cannot find MAPISendDocuments function");
1544 return errorInfo.Get_HRESULT_Error();
1545 }
1546 */
1547
1548 LPMAPISENDMAIL sendMail = (LPMAPISENDMAIL)mapiLib.GetProc("MAPISendMail");
1549 if (sendMail == 0)
1550 {
1551 errorInfo.SetFromLastError("7-Zip cannot find MAPISendMail function");
1552 return errorInfo.Get_HRESULT_Error();;
1553 }
1554
1555 FStringVector fullPaths;
1556 unsigned i;
1557
1558 for (i = 0; i < options.Commands.Size(); i++)
1559 {
1560 CArchivePath &ap = options.Commands[i].ArchivePath;
1561 FString finalPath = us2fs(ap.GetFinalPath());
1562 FString arcPath2;
1563 if (!MyGetFullPathName(finalPath, arcPath2))
1564 return errorInfo.SetFromLastError("GetFullPathName error", finalPath);
1565 fullPaths.Add(arcPath2);
1566 }
1567
1568 CCurrentDirRestorer curDirRestorer;
1569
1570 for (i = 0; i < fullPaths.Size(); i++)
1571 {
1572 const UString arcPath2 = fs2us(fullPaths[i]);
1573 const UString fileName = ExtractFileNameFromPath(arcPath2);
1574 const AString path (GetAnsiString(arcPath2));
1575 const AString name (GetAnsiString(fileName));
1576 // Warning!!! MAPISendDocuments function changes Current directory
1577 // fnSend(0, ";", (LPSTR)(LPCSTR)path, (LPSTR)(LPCSTR)name, 0);
1578
1579 MapiFileDesc f;
1580 memset(&f, 0, sizeof(f));
1581 f.nPosition = 0xFFFFFFFF;
1582 f.lpszPathName = (char *)(const char *)path;
1583 f.lpszFileName = (char *)(const char *)name;
1584
1585 MapiMessage m;
1586 memset(&m, 0, sizeof(m));
1587 m.nFileCount = 1;
1588 m.lpFiles = &f;
1589
1590 const AString addr (GetAnsiString(options.EMailAddress));
1591 MapiRecipDesc rec;
1592 if (!addr.IsEmpty())
1593 {
1594 memset(&rec, 0, sizeof(rec));
1595 rec.ulRecipClass = MAPI_TO;
1596 rec.lpszAddress = (char *)(const char *)addr;
1597 m.nRecipCount = 1;
1598 m.lpRecips = &rec;
1599 }
1600
1601 sendMail((LHANDLE)0, 0, &m, MAPI_DIALOG, 0);
1602 }
1603 }
1604
1605 #endif
1606
1607 if (options.DeleteAfterCompressing)
1608 {
1609 CRecordVector<CDirPathSortPair> pairs;
1610 FStringVector foldersNames;
1611
1612 unsigned i;
1613
1614 for (i = 0; i < dirItems.Items.Size(); i++)
1615 {
1616 const CDirItem &dirItem = dirItems.Items[i];
1617 const FString phyPath = dirItems.GetPhyPath(i);
1618 if (dirItem.IsDir())
1619 {
1620 CDirPathSortPair pair;
1621 pair.Index = i;
1622 pair.SetNumSlashes(phyPath);
1623 pairs.Add(pair);
1624 }
1625 else
1626 {
1627 if (processedItems[i] != 0 || dirItem.Size == 0)
1628 {
1629 NFind::CFileInfo fileInfo;
1630 if (fileInfo.Find(phyPath))
1631 {
1632 // maybe we must exclude also files with archive name: "a a.7z * -sdel"
1633 if (fileInfo.Size == dirItem.Size
1634 && CompareFileTime(&fileInfo.MTime, &dirItem.MTime) == 0
1635 && CompareFileTime(&fileInfo.CTime, &dirItem.CTime) == 0)
1636 {
1637 RINOK(callback->DeletingAfterArchiving(phyPath, false));
1638 DeleteFileAlways(phyPath);
1639 }
1640 }
1641 }
1642 else
1643 {
1644 // file was skipped
1645 /*
1646 errorInfo.SystemError = 0;
1647 errorInfo.Message = "file was not processed";
1648 errorInfo.FileName = phyPath;
1649 return E_FAIL;
1650 */
1651 }
1652 }
1653 }
1654
1655 pairs.Sort2();
1656
1657 for (i = 0; i < pairs.Size(); i++)
1658 {
1659 const FString phyPath = dirItems.GetPhyPath(pairs[i].Index);
1660 if (NFind::DoesDirExist(phyPath))
1661 {
1662 RINOK(callback->DeletingAfterArchiving(phyPath, true));
1663 RemoveDir(phyPath);
1664 }
1665 }
1666
1667 RINOK(callback->FinishDeletingAfterArchiving());
1668 }
1669
1670 return S_OK;
1671 }
1672