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