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