1 // ArchiveFolderOut.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "../../../Common/ComTry.h"
6 
7 #include "../../../Windows/FileDir.h"
8 
9 #include "../../Common/FileStreams.h"
10 #include "../../Common/LimitedStreams.h"
11 
12 #include "../../Compress/CopyCoder.h"
13 
14 #include "../Common/WorkDir.h"
15 
16 #include "Agent.h"
17 
18 using namespace NWindows;
19 using namespace NFile;
20 using namespace NDir;
21 
GetPathParts(UStringVector & pathParts,bool & isAltStreamFolder)22 void CAgentFolder::GetPathParts(UStringVector &pathParts, bool &isAltStreamFolder)
23 {
24   if (_proxy2)
25     _proxy2->GetDirPathParts(_proxyDirIndex, pathParts, isAltStreamFolder);
26   else
27     _proxy->GetDirPathParts(_proxyDirIndex, pathParts);
28 }
29 
DeleteEmptyFolderAndEmptySubFolders(const FString & path)30 static bool DeleteEmptyFolderAndEmptySubFolders(const FString &path)
31 {
32   NFind::CFileInfo fileInfo;
33   FString pathPrefix = path;
34   pathPrefix.Add_PathSepar();
35   {
36     NFind::CEnumerator enumerator;
37     enumerator.SetDirPrefix(pathPrefix);
38     while (enumerator.Next(fileInfo))
39     {
40       if (fileInfo.IsDir())
41         if (!DeleteEmptyFolderAndEmptySubFolders(pathPrefix + fileInfo.Name))
42           return false;
43     }
44   }
45   /*
46   // we don't need clear readonly for folders
47   if (!SetFileAttrib(path, 0))
48     return false;
49   */
50   return RemoveDir(path);
51 }
52 
53 
CommonUpdateOperation(AGENT_OP operation,bool moveMode,const wchar_t * newItemName,const NUpdateArchive::CActionSet * actionSet,const UInt32 * indices,UInt32 numItems,IProgress * progress)54 HRESULT CAgentFolder::CommonUpdateOperation(
55     AGENT_OP operation,
56     bool moveMode,
57     const wchar_t *newItemName,
58     const NUpdateArchive::CActionSet *actionSet,
59     const UInt32 *indices, UInt32 numItems,
60     IProgress *progress)
61 {
62   if (!_agentSpec->CanUpdate())
63     return E_NOTIMPL;
64 
65   CMyComPtr<IFolderArchiveUpdateCallback> updateCallback100;
66   if (progress)
67     progress->QueryInterface(IID_IFolderArchiveUpdateCallback, (void **)&updateCallback100);
68 
69   try
70   {
71 
72   RINOK(_agentSpec->SetFolder(this));
73 
74   // ---------- Save FolderItem ----------
75 
76   UStringVector pathParts;
77   bool isAltStreamFolder = false;
78   GetPathParts(pathParts, isAltStreamFolder);
79 
80   FStringVector requestedPaths;
81   FStringVector processedPaths;
82 
83   CWorkDirTempFile tempFile;
84   RINOK(tempFile.CreateTempFile(us2fs(_agentSpec->_archiveFilePath)));
85   {
86     CMyComPtr<IOutStream> tailStream;
87     const CArc &arc = *_agentSpec->_archiveLink.GetArc();
88 
89     if (arc.ArcStreamOffset == 0)
90       tailStream = tempFile.OutStream;
91     else
92     {
93       if (arc.Offset < 0)
94         return E_NOTIMPL;
95       RINOK(arc.InStream->Seek(0, STREAM_SEEK_SET, NULL));
96       RINOK(NCompress::CopyStream_ExactSize(arc.InStream, tempFile.OutStream, arc.ArcStreamOffset, NULL));
97       CTailOutStream *tailStreamSpec = new CTailOutStream;
98       tailStream = tailStreamSpec;
99       tailStreamSpec->Stream = tempFile.OutStream;
100       tailStreamSpec->Offset = arc.ArcStreamOffset;
101       tailStreamSpec->Init();
102     }
103 
104     HRESULT result;
105 
106     switch (operation)
107     {
108       case AGENT_OP_Delete:
109         result = _agentSpec->DeleteItems(tailStream, indices, numItems, updateCallback100);
110         break;
111       case AGENT_OP_CreateFolder:
112         result = _agentSpec->CreateFolder(tailStream, newItemName, updateCallback100);
113         break;
114       case AGENT_OP_Rename:
115         result = _agentSpec->RenameItem(tailStream, indices, numItems, newItemName, updateCallback100);
116         break;
117       case AGENT_OP_Comment:
118         result = _agentSpec->CommentItem(tailStream, indices, numItems, newItemName, updateCallback100);
119         break;
120       case AGENT_OP_CopyFromFile:
121         result = _agentSpec->UpdateOneFile(tailStream, indices, numItems, newItemName, updateCallback100);
122         break;
123       case AGENT_OP_Uni:
124         {
125           Byte actionSetByte[NUpdateArchive::NPairState::kNumValues];
126           for (int i = 0; i < NUpdateArchive::NPairState::kNumValues; i++)
127             actionSetByte[i] = (Byte)actionSet->StateActions[i];
128           result = _agentSpec->DoOperation2(
129               moveMode ? &requestedPaths : NULL,
130               moveMode ? &processedPaths : NULL,
131               tailStream, actionSetByte, NULL, updateCallback100);
132           break;
133         }
134       default:
135         return E_FAIL;
136     }
137 
138     RINOK(result);
139   }
140 
141   _agentSpec->KeepModeForNextOpen();
142   _agentSpec->Close();
143 
144   // before 9.26: if there was error for MoveToOriginal archive was closed.
145   // now: we reopen archive after close
146 
147   // m_FolderItem = NULL;
148 
149   HRESULT res = tempFile.MoveToOriginal(true);
150 
151   // RINOK(res);
152   if (res == S_OK)
153   {
154     if (moveMode)
155     {
156       unsigned i;
157       for (i = 0; i < processedPaths.Size(); i++)
158       {
159         DeleteFileAlways(processedPaths[i]);
160       }
161       for (i = 0; i < requestedPaths.Size(); i++)
162       {
163         const FString &fs = requestedPaths[i];
164         if (NFind::DoesDirExist(fs))
165           DeleteEmptyFolderAndEmptySubFolders(fs);
166       }
167     }
168   }
169 
170   {
171     CMyComPtr<IArchiveOpenCallback> openCallback;
172     if (updateCallback100)
173       updateCallback100->QueryInterface(IID_IArchiveOpenCallback, (void **)&openCallback);
174     RINOK(_agentSpec->ReOpen(openCallback));
175   }
176 
177   // CAgent::ReOpen() deletes _proxy and _proxy2
178   _items.Clear();
179   _proxy = NULL;
180   _proxy2 = NULL;
181   _proxyDirIndex = k_Proxy_RootDirIndex;
182   _isAltStreamFolder = false;
183 
184 
185   // ---------- Restore FolderItem ----------
186 
187   CMyComPtr<IFolderFolder> archiveFolder;
188   RINOK(_agentSpec->BindToRootFolder(&archiveFolder));
189 
190   // CAgent::BindToRootFolder() changes _proxy and _proxy2
191   _proxy = _agentSpec->_proxy;
192   _proxy2 = _agentSpec->_proxy2;
193 
194   if (_proxy)
195   {
196     FOR_VECTOR (i, pathParts)
197     {
198       int next = _proxy->FindSubDir(_proxyDirIndex, pathParts[i]);
199       if (next < 0)
200         break;
201       _proxyDirIndex = next;
202     }
203   }
204 
205   if (_proxy2)
206   {
207     if (pathParts.IsEmpty() && isAltStreamFolder)
208     {
209       _proxyDirIndex = k_Proxy2_AltRootDirIndex;
210     }
211     else FOR_VECTOR (i, pathParts)
212     {
213       bool dirOnly = (i + 1 < pathParts.Size() || !isAltStreamFolder);
214       int index = _proxy2->FindItem(_proxyDirIndex, pathParts[i], dirOnly);
215       if (index < 0)
216         break;
217 
218       const CProxyFile2 &file = _proxy2->Files[_proxy2->Dirs[_proxyDirIndex].Items[index]];
219 
220       if (dirOnly)
221         _proxyDirIndex = file.DirIndex;
222       else
223       {
224         if (file.AltDirIndex >= 0)
225           _proxyDirIndex = file.AltDirIndex;
226         break;
227       }
228     }
229   }
230 
231   /*
232   if (pathParts.IsEmpty() && isAltStreamFolder)
233   {
234     CMyComPtr<IFolderAltStreams> folderAltStreams;
235     archiveFolder.QueryInterface(IID_IFolderAltStreams, &folderAltStreams);
236     if (folderAltStreams)
237     {
238       CMyComPtr<IFolderFolder> newFolder;
239       folderAltStreams->BindToAltStreams((UInt32)(Int32)-1, &newFolder);
240       if (newFolder)
241         archiveFolder = newFolder;
242     }
243   }
244 
245   FOR_VECTOR (i, pathParts)
246   {
247     CMyComPtr<IFolderFolder> newFolder;
248 
249     if (isAltStreamFolder && i == pathParts.Size() - 1)
250     {
251       CMyComPtr<IFolderAltStreams> folderAltStreams;
252       archiveFolder.QueryInterface(IID_IFolderAltStreams, &folderAltStreams);
253       if (folderAltStreams)
254         folderAltStreams->BindToAltStreams(pathParts[i], &newFolder);
255     }
256     else
257       archiveFolder->BindToFolder(pathParts[i], &newFolder);
258 
259     if (!newFolder)
260       break;
261     archiveFolder = newFolder;
262   }
263 
264   CMyComPtr<IArchiveFolderInternal> archiveFolderInternal;
265   RINOK(archiveFolder.QueryInterface(IID_IArchiveFolderInternal, &archiveFolderInternal));
266   CAgentFolder *agentFolder;
267   RINOK(archiveFolderInternal->GetAgentFolder(&agentFolder));
268   _proxyDirIndex = agentFolder->_proxyDirIndex;
269   // _parentFolder = agentFolder->_parentFolder;
270   */
271 
272   if (_proxy2)
273     _isAltStreamFolder = _proxy2->IsAltDir(_proxyDirIndex);
274 
275   return res;
276 
277   }
278   catch(const UString &s)
279   {
280     if (updateCallback100)
281     {
282       UString s2 ("Error: ");
283       s2 += s;
284       RINOK(updateCallback100->UpdateErrorMessage(s2));
285       return E_FAIL;
286     }
287     throw;
288   }
289 }
290 
291 
292 
CopyFrom(Int32 moveMode,const wchar_t * fromFolderPath,const wchar_t * const * itemsPaths,UInt32 numItems,IProgress * progress)293 STDMETHODIMP CAgentFolder::CopyFrom(Int32 moveMode,
294     const wchar_t *fromFolderPath, // test it
295     const wchar_t * const *itemsPaths,
296     UInt32 numItems,
297     IProgress *progress)
298 {
299   COM_TRY_BEGIN
300   {
301     RINOK(_agentSpec->SetFiles(fromFolderPath, itemsPaths, numItems));
302     return CommonUpdateOperation(AGENT_OP_Uni, (moveMode != 0), NULL,
303         &NUpdateArchive::k_ActionSet_Add,
304         NULL, 0, progress);
305   }
306   COM_TRY_END
307 }
308 
CopyFromFile(UInt32 destIndex,const wchar_t * itemPath,IProgress * progress)309 STDMETHODIMP CAgentFolder::CopyFromFile(UInt32 destIndex, const wchar_t *itemPath, IProgress *progress)
310 {
311   COM_TRY_BEGIN
312   return CommonUpdateOperation(AGENT_OP_CopyFromFile, false, itemPath,
313       &NUpdateArchive::k_ActionSet_Add,
314       &destIndex, 1, progress);
315   COM_TRY_END
316 }
317 
Delete(const UInt32 * indices,UInt32 numItems,IProgress * progress)318 STDMETHODIMP CAgentFolder::Delete(const UInt32 *indices, UInt32 numItems, IProgress *progress)
319 {
320   COM_TRY_BEGIN
321   return CommonUpdateOperation(AGENT_OP_Delete, false, NULL,
322       &NUpdateArchive::k_ActionSet_Delete, indices, numItems, progress);
323   COM_TRY_END
324 }
325 
CreateFolder(const wchar_t * name,IProgress * progress)326 STDMETHODIMP CAgentFolder::CreateFolder(const wchar_t *name, IProgress *progress)
327 {
328   COM_TRY_BEGIN
329 
330   if (_isAltStreamFolder)
331     return E_NOTIMPL;
332 
333   if (_proxy2)
334   {
335     if (_proxy2->IsThere_SubDir(_proxyDirIndex, name))
336       return ERROR_ALREADY_EXISTS;
337   }
338   else
339   {
340     if (_proxy->FindSubDir(_proxyDirIndex, name) >= 0)
341       return ERROR_ALREADY_EXISTS;
342   }
343 
344   return CommonUpdateOperation(AGENT_OP_CreateFolder, false, name, NULL, NULL, 0, progress);
345   COM_TRY_END
346 }
347 
Rename(UInt32 index,const wchar_t * newName,IProgress * progress)348 STDMETHODIMP CAgentFolder::Rename(UInt32 index, const wchar_t *newName, IProgress *progress)
349 {
350   COM_TRY_BEGIN
351   return CommonUpdateOperation(AGENT_OP_Rename, false, newName, NULL,
352       &index, 1, progress);
353   COM_TRY_END
354 }
355 
CreateFile(const wchar_t *,IProgress *)356 STDMETHODIMP CAgentFolder::CreateFile(const wchar_t * /* name */, IProgress * /* progress */)
357 {
358   return E_NOTIMPL;
359 }
360 
SetProperty(UInt32 index,PROPID propID,const PROPVARIANT * value,IProgress * progress)361 STDMETHODIMP CAgentFolder::SetProperty(UInt32 index, PROPID propID,
362     const PROPVARIANT *value, IProgress *progress)
363 {
364   COM_TRY_BEGIN
365   if (propID != kpidComment || value->vt != VT_BSTR)
366     return E_NOTIMPL;
367   if (!_agentSpec || !_agentSpec->GetTypeOfArc(_agentSpec->GetArc()).IsEqualTo_Ascii_NoCase("zip"))
368     return E_NOTIMPL;
369 
370   return CommonUpdateOperation(AGENT_OP_Comment, false, value->bstrVal, NULL,
371       &index, 1, progress);
372   COM_TRY_END
373 }
374