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