1 // ArchiveExtractCallback.cpp |
|
2 |
|
3 #include "StdAfx.h" |
|
4 |
|
5 #include "ArchiveExtractCallback.h" |
|
6 |
|
7 #include "Common/Wildcard.h" |
|
8 #include "Common/StringConvert.h" |
|
9 #include "Common/ComTry.h" |
|
10 |
|
11 #include "Windows/FileDir.h" |
|
12 #include "Windows/FileFind.h" |
|
13 #include "Windows/Time.h" |
|
14 #include "Windows/Defs.h" |
|
15 #include "Windows/PropVariant.h" |
|
16 |
|
17 #include "Windows/PropVariantConversions.h" |
|
18 |
|
19 #include "../../Common/FilePathAutoRename.h" |
|
20 |
|
21 #include "../Common/ExtractingFilePath.h" |
|
22 #include "OpenArchive.h" |
|
23 |
|
24 using namespace NWindows; |
|
25 |
|
26 static const wchar_t *kCantAutoRename = L"ERROR: Can not create file with auto name"; |
|
27 static const wchar_t *kCantRenameFile = L"ERROR: Can not rename existing file "; |
|
28 static const wchar_t *kCantDeleteOutputFile = L"ERROR: Can not delete output file "; |
|
29 |
|
30 |
|
31 void CArchiveExtractCallback::Init( |
|
32 IInArchive *archiveHandler, |
|
33 IFolderArchiveExtractCallback *extractCallback2, |
|
34 bool stdOutMode, |
|
35 const UString &directoryPath, |
|
36 const UStringVector &removePathParts, |
|
37 const UString &itemDefaultName, |
|
38 const FILETIME &utcLastWriteTimeDefault, |
|
39 UInt32 attributesDefault, |
|
40 UInt64 packSize) |
|
41 { |
|
42 _stdOutMode = stdOutMode; |
|
43 _numErrors = 0; |
|
44 _unpTotal = 1; |
|
45 _packTotal = packSize; |
|
46 |
|
47 _extractCallback2 = extractCallback2; |
|
48 _compressProgress.Release(); |
|
49 _extractCallback2.QueryInterface(IID_ICompressProgressInfo, &_compressProgress); |
|
50 |
|
51 LocalProgressSpec->Init(extractCallback2, true); |
|
52 LocalProgressSpec->SendProgress = false; |
|
53 |
|
54 _itemDefaultName = itemDefaultName; |
|
55 _utcLastWriteTimeDefault = utcLastWriteTimeDefault; |
|
56 _attributesDefault = attributesDefault; |
|
57 _removePathParts = removePathParts; |
|
58 _archiveHandler = archiveHandler; |
|
59 _directoryPath = directoryPath; |
|
60 NFile::NName::NormalizeDirPathPrefix(_directoryPath); |
|
61 } |
|
62 |
|
63 STDMETHODIMP CArchiveExtractCallback::SetTotal(UInt64 size) |
|
64 { |
|
65 COM_TRY_BEGIN |
|
66 _unpTotal = size; |
|
67 if (!_multiArchives && _extractCallback2) |
|
68 return _extractCallback2->SetTotal(size); |
|
69 return S_OK; |
|
70 COM_TRY_END |
|
71 } |
|
72 |
|
73 static void NormalizeVals(UInt64 &v1, UInt64 &v2) |
|
74 { |
|
75 const UInt64 kMax = (UInt64)1 << 31; |
|
76 while (v1 > kMax) |
|
77 { |
|
78 v1 >>= 1; |
|
79 v2 >>= 1; |
|
80 } |
|
81 } |
|
82 |
|
83 static UInt64 MyMultDiv64(UInt64 unpCur, UInt64 unpTotal, UInt64 packTotal) |
|
84 { |
|
85 NormalizeVals(packTotal, unpTotal); |
|
86 NormalizeVals(unpCur, unpTotal); |
|
87 if (unpTotal == 0) |
|
88 unpTotal = 1; |
|
89 return unpCur * packTotal / unpTotal; |
|
90 } |
|
91 |
|
92 STDMETHODIMP CArchiveExtractCallback::SetCompleted(const UInt64 *completeValue) |
|
93 { |
|
94 COM_TRY_BEGIN |
|
95 if (!_extractCallback2) |
|
96 return S_OK; |
|
97 |
|
98 if (_multiArchives) |
|
99 { |
|
100 if (completeValue != NULL) |
|
101 { |
|
102 UInt64 packCur = LocalProgressSpec->InSize + MyMultDiv64(*completeValue, _unpTotal, _packTotal); |
|
103 return _extractCallback2->SetCompleted(&packCur); |
|
104 } |
|
105 } |
|
106 return _extractCallback2->SetCompleted(completeValue); |
|
107 COM_TRY_END |
|
108 } |
|
109 |
|
110 STDMETHODIMP CArchiveExtractCallback::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize) |
|
111 { |
|
112 COM_TRY_BEGIN |
|
113 return _localProgress->SetRatioInfo(inSize, outSize); |
|
114 COM_TRY_END |
|
115 } |
|
116 |
|
117 void CArchiveExtractCallback::CreateComplexDirectory(const UStringVector &dirPathParts, UString &fullPath) |
|
118 { |
|
119 fullPath = _directoryPath; |
|
120 for(int i = 0; i < dirPathParts.Size(); i++) |
|
121 { |
|
122 if (i > 0) |
|
123 fullPath += wchar_t(NFile::NName::kDirDelimiter); |
|
124 fullPath += dirPathParts[i]; |
|
125 NFile::NDirectory::MyCreateDirectory(fullPath); |
|
126 } |
|
127 } |
|
128 |
|
129 static UString MakePathNameFromParts(const UStringVector &parts) |
|
130 { |
|
131 UString result; |
|
132 for(int i = 0; i < parts.Size(); i++) |
|
133 { |
|
134 if(i != 0) |
|
135 result += wchar_t(NFile::NName::kDirDelimiter); |
|
136 result += parts[i]; |
|
137 } |
|
138 return result; |
|
139 } |
|
140 |
|
141 |
|
142 HRESULT CArchiveExtractCallback::GetTime(int index, PROPID propID, FILETIME &filetime, bool &filetimeIsDefined) |
|
143 { |
|
144 filetimeIsDefined = false; |
|
145 NCOM::CPropVariant prop; |
|
146 RINOK(_archiveHandler->GetProperty(index, propID, &prop)); |
|
147 if (prop.vt == VT_FILETIME) |
|
148 { |
|
149 filetime = prop.filetime; |
|
150 filetimeIsDefined = (filetime.dwHighDateTime != 0 || filetime.dwLowDateTime != 0); |
|
151 } |
|
152 else if (prop.vt != VT_EMPTY) |
|
153 return E_FAIL; |
|
154 return S_OK; |
|
155 } |
|
156 |
|
157 STDMETHODIMP CArchiveExtractCallback::GetStream(UInt32 index, ISequentialOutStream **outStream, Int32 askExtractMode) |
|
158 { |
|
159 COM_TRY_BEGIN |
|
160 *outStream = 0; |
|
161 _outFileStream.Release(); |
|
162 |
|
163 _encrypted = false; |
|
164 _isSplit = false; |
|
165 _curSize = 0; |
|
166 |
|
167 UString fullPath; |
|
168 |
|
169 RINOK(GetArchiveItemPath(_archiveHandler, index, _itemDefaultName, fullPath)); |
|
170 RINOK(IsArchiveItemFolder(_archiveHandler, index, _processedFileInfo.IsDirectory)); |
|
171 |
|
172 _filePath = fullPath; |
|
173 |
|
174 { |
|
175 NCOM::CPropVariant prop; |
|
176 RINOK(_archiveHandler->GetProperty(index, kpidPosition, &prop)); |
|
177 if (prop.vt != VT_EMPTY) |
|
178 { |
|
179 if (prop.vt != VT_UI8) |
|
180 return E_FAIL; |
|
181 _position = prop.uhVal.QuadPart; |
|
182 _isSplit = true; |
|
183 } |
|
184 } |
|
185 |
|
186 RINOK(IsArchiveItemProp(_archiveHandler, index, kpidEncrypted, _encrypted)); |
|
187 |
|
188 bool newFileSizeDefined; |
|
189 UInt64 newFileSize; |
|
190 { |
|
191 NCOM::CPropVariant prop; |
|
192 RINOK(_archiveHandler->GetProperty(index, kpidSize, &prop)); |
|
193 newFileSizeDefined = (prop.vt != VT_EMPTY); |
|
194 if (newFileSizeDefined) |
|
195 { |
|
196 newFileSize = ConvertPropVariantToUInt64(prop); |
|
197 _curSize = newFileSize; |
|
198 } |
|
199 } |
|
200 |
|
201 if(askExtractMode == NArchive::NExtract::NAskMode::kExtract) |
|
202 { |
|
203 if (_stdOutMode) |
|
204 { |
|
205 CMyComPtr<ISequentialOutStream> outStreamLoc = new CStdOutFileStream; |
|
206 *outStream = outStreamLoc.Detach(); |
|
207 return S_OK; |
|
208 } |
|
209 |
|
210 { |
|
211 NCOM::CPropVariant prop; |
|
212 RINOK(_archiveHandler->GetProperty(index, kpidAttributes, &prop)); |
|
213 if (prop.vt == VT_EMPTY) |
|
214 { |
|
215 _processedFileInfo.Attributes = _attributesDefault; |
|
216 _processedFileInfo.AttributesAreDefined = false; |
|
217 } |
|
218 else |
|
219 { |
|
220 if (prop.vt != VT_UI4) |
|
221 return E_FAIL; |
|
222 _processedFileInfo.Attributes = prop.ulVal; |
|
223 _processedFileInfo.AttributesAreDefined = true; |
|
224 } |
|
225 } |
|
226 |
|
227 RINOK(GetTime(index, kpidCreationTime, _processedFileInfo.CreationTime, |
|
228 _processedFileInfo.IsCreationTimeDefined)); |
|
229 RINOK(GetTime(index, kpidLastWriteTime, _processedFileInfo.LastWriteTime, |
|
230 _processedFileInfo.IsLastWriteTimeDefined)); |
|
231 RINOK(GetTime(index, kpidLastAccessTime, _processedFileInfo.LastAccessTime, |
|
232 _processedFileInfo.IsLastAccessTimeDefined)); |
|
233 |
|
234 bool isAnti = false; |
|
235 RINOK(IsArchiveItemProp(_archiveHandler, index, kpidIsAnti, isAnti)); |
|
236 |
|
237 UStringVector pathParts; |
|
238 SplitPathToParts(fullPath, pathParts); |
|
239 |
|
240 if(pathParts.IsEmpty()) |
|
241 return E_FAIL; |
|
242 int numRemovePathParts = 0; |
|
243 switch(_pathMode) |
|
244 { |
|
245 case NExtract::NPathMode::kFullPathnames: |
|
246 break; |
|
247 case NExtract::NPathMode::kCurrentPathnames: |
|
248 { |
|
249 numRemovePathParts = _removePathParts.Size(); |
|
250 if (pathParts.Size() <= numRemovePathParts) |
|
251 return E_FAIL; |
|
252 for (int i = 0; i < numRemovePathParts; i++) |
|
253 if (_removePathParts[i].CompareNoCase(pathParts[i]) != 0) |
|
254 return E_FAIL; |
|
255 break; |
|
256 } |
|
257 case NExtract::NPathMode::kNoPathnames: |
|
258 { |
|
259 numRemovePathParts = pathParts.Size() - 1; |
|
260 break; |
|
261 } |
|
262 } |
|
263 pathParts.Delete(0, numRemovePathParts); |
|
264 MakeCorrectPath(pathParts); |
|
265 UString processedPath = MakePathNameFromParts(pathParts); |
|
266 if (!isAnti) |
|
267 { |
|
268 if (!_processedFileInfo.IsDirectory) |
|
269 { |
|
270 if (!pathParts.IsEmpty()) |
|
271 pathParts.DeleteBack(); |
|
272 } |
|
273 |
|
274 if (!pathParts.IsEmpty()) |
|
275 { |
|
276 UString fullPathNew; |
|
277 CreateComplexDirectory(pathParts, fullPathNew); |
|
278 if (_processedFileInfo.IsDirectory) |
|
279 NFile::NDirectory::SetDirTime(fullPathNew, |
|
280 (WriteCreated && _processedFileInfo.IsCreationTimeDefined) ? &_processedFileInfo.CreationTime : NULL, |
|
281 (WriteAccessed && _processedFileInfo.IsLastAccessTimeDefined) ? &_processedFileInfo.LastAccessTime : NULL, |
|
282 (WriteModified && _processedFileInfo.IsLastWriteTimeDefined) ? &_processedFileInfo.LastWriteTime : &_utcLastWriteTimeDefault); |
|
283 } |
|
284 } |
|
285 |
|
286 |
|
287 UString fullProcessedPath = _directoryPath + processedPath; |
|
288 |
|
289 if(_processedFileInfo.IsDirectory) |
|
290 { |
|
291 _diskFilePath = fullProcessedPath; |
|
292 if (isAnti) |
|
293 NFile::NDirectory::MyRemoveDirectory(_diskFilePath); |
|
294 return S_OK; |
|
295 } |
|
296 |
|
297 if (!_isSplit) |
|
298 { |
|
299 NFile::NFind::CFileInfoW fileInfo; |
|
300 if(NFile::NFind::FindFile(fullProcessedPath, fileInfo)) |
|
301 { |
|
302 switch(_overwriteMode) |
|
303 { |
|
304 case NExtract::NOverwriteMode::kSkipExisting: |
|
305 return S_OK; |
|
306 case NExtract::NOverwriteMode::kAskBefore: |
|
307 { |
|
308 Int32 overwiteResult; |
|
309 RINOK(_extractCallback2->AskOverwrite( |
|
310 fullProcessedPath, &fileInfo.LastWriteTime, &fileInfo.Size, fullPath, |
|
311 _processedFileInfo.IsLastWriteTimeDefined ? &_processedFileInfo.LastWriteTime : NULL, |
|
312 newFileSizeDefined ? &newFileSize : NULL, |
|
313 &overwiteResult)) |
|
314 |
|
315 switch(overwiteResult) |
|
316 { |
|
317 case NOverwriteAnswer::kCancel: |
|
318 return E_ABORT; |
|
319 case NOverwriteAnswer::kNo: |
|
320 return S_OK; |
|
321 case NOverwriteAnswer::kNoToAll: |
|
322 _overwriteMode = NExtract::NOverwriteMode::kSkipExisting; |
|
323 return S_OK; |
|
324 case NOverwriteAnswer::kYesToAll: |
|
325 _overwriteMode = NExtract::NOverwriteMode::kWithoutPrompt; |
|
326 break; |
|
327 case NOverwriteAnswer::kYes: |
|
328 break; |
|
329 case NOverwriteAnswer::kAutoRename: |
|
330 _overwriteMode = NExtract::NOverwriteMode::kAutoRename; |
|
331 break; |
|
332 default: |
|
333 return E_FAIL; |
|
334 } |
|
335 } |
|
336 } |
|
337 if (_overwriteMode == NExtract::NOverwriteMode::kAutoRename) |
|
338 { |
|
339 if (!AutoRenamePath(fullProcessedPath)) |
|
340 { |
|
341 UString message = UString(kCantAutoRename) + fullProcessedPath; |
|
342 RINOK(_extractCallback2->MessageError(message)); |
|
343 return E_FAIL; |
|
344 } |
|
345 } |
|
346 else if (_overwriteMode == NExtract::NOverwriteMode::kAutoRenameExisting) |
|
347 { |
|
348 UString existPath = fullProcessedPath; |
|
349 if (!AutoRenamePath(existPath)) |
|
350 { |
|
351 UString message = kCantAutoRename + fullProcessedPath; |
|
352 RINOK(_extractCallback2->MessageError(message)); |
|
353 return E_FAIL; |
|
354 } |
|
355 if(!NFile::NDirectory::MyMoveFile(fullProcessedPath, existPath)) |
|
356 { |
|
357 UString message = UString(kCantRenameFile) + fullProcessedPath; |
|
358 RINOK(_extractCallback2->MessageError(message)); |
|
359 return E_FAIL; |
|
360 } |
|
361 } |
|
362 else |
|
363 if (!NFile::NDirectory::DeleteFileAlways(fullProcessedPath)) |
|
364 { |
|
365 UString message = UString(kCantDeleteOutputFile) + fullProcessedPath; |
|
366 RINOK(_extractCallback2->MessageError(message)); |
|
367 return S_OK; |
|
368 // return E_FAIL; |
|
369 } |
|
370 } |
|
371 } |
|
372 if (!isAnti) |
|
373 { |
|
374 _outFileStreamSpec = new COutFileStream; |
|
375 CMyComPtr<ISequentialOutStream> outStreamLoc(_outFileStreamSpec); |
|
376 if (!_outFileStreamSpec->Open(fullProcessedPath, _isSplit ? OPEN_ALWAYS: CREATE_ALWAYS)) |
|
377 { |
|
378 // if (::GetLastError() != ERROR_FILE_EXISTS || !isSplit) |
|
379 { |
|
380 UString message = L"can not open output file " + fullProcessedPath; |
|
381 RINOK(_extractCallback2->MessageError(message)); |
|
382 return S_OK; |
|
383 } |
|
384 } |
|
385 if (_isSplit) |
|
386 { |
|
387 RINOK(_outFileStreamSpec->Seek(_position, STREAM_SEEK_SET, NULL)); |
|
388 } |
|
389 _outFileStream = outStreamLoc; |
|
390 *outStream = outStreamLoc.Detach(); |
|
391 } |
|
392 _diskFilePath = fullProcessedPath; |
|
393 } |
|
394 else |
|
395 { |
|
396 *outStream = NULL; |
|
397 } |
|
398 return S_OK; |
|
399 COM_TRY_END |
|
400 } |
|
401 |
|
402 STDMETHODIMP CArchiveExtractCallback::PrepareOperation(Int32 askExtractMode) |
|
403 { |
|
404 COM_TRY_BEGIN |
|
405 _extractMode = false; |
|
406 switch (askExtractMode) |
|
407 { |
|
408 case NArchive::NExtract::NAskMode::kExtract: |
|
409 _extractMode = true; |
|
410 }; |
|
411 return _extractCallback2->PrepareOperation(_filePath, _processedFileInfo.IsDirectory, |
|
412 askExtractMode, _isSplit ? &_position: 0); |
|
413 COM_TRY_END |
|
414 } |
|
415 |
|
416 STDMETHODIMP CArchiveExtractCallback::SetOperationResult(Int32 operationResult) |
|
417 { |
|
418 COM_TRY_BEGIN |
|
419 switch(operationResult) |
|
420 { |
|
421 case NArchive::NExtract::NOperationResult::kOK: |
|
422 case NArchive::NExtract::NOperationResult::kUnSupportedMethod: |
|
423 case NArchive::NExtract::NOperationResult::kCRCError: |
|
424 case NArchive::NExtract::NOperationResult::kDataError: |
|
425 break; |
|
426 default: |
|
427 _outFileStream.Release(); |
|
428 return E_FAIL; |
|
429 } |
|
430 if (_outFileStream != NULL) |
|
431 { |
|
432 _outFileStreamSpec->SetTime( |
|
433 (WriteCreated && _processedFileInfo.IsCreationTimeDefined) ? &_processedFileInfo.CreationTime : NULL, |
|
434 (WriteAccessed && _processedFileInfo.IsLastAccessTimeDefined) ? &_processedFileInfo.LastAccessTime : NULL, |
|
435 (WriteModified && _processedFileInfo.IsLastWriteTimeDefined) ? &_processedFileInfo.LastWriteTime : &_utcLastWriteTimeDefault); |
|
436 _curSize = _outFileStreamSpec->ProcessedSize; |
|
437 RINOK(_outFileStreamSpec->Close()); |
|
438 _outFileStream.Release(); |
|
439 } |
|
440 UnpackSize += _curSize; |
|
441 if (_processedFileInfo.IsDirectory) |
|
442 NumFolders++; |
|
443 else |
|
444 NumFiles++; |
|
445 |
|
446 if (_extractMode && _processedFileInfo.AttributesAreDefined) |
|
447 NFile::NDirectory::MySetFileAttributes(_diskFilePath, _processedFileInfo.Attributes); |
|
448 RINOK(_extractCallback2->SetOperationResult(operationResult, _encrypted)); |
|
449 return S_OK; |
|
450 COM_TRY_END |
|
451 } |
|
452 |
|
453 /* |
|
454 STDMETHODIMP CArchiveExtractCallback::GetInStream( |
|
455 const wchar_t *name, ISequentialInStream **inStream) |
|
456 { |
|
457 COM_TRY_BEGIN |
|
458 CInFileStream *inFile = new CInFileStream; |
|
459 CMyComPtr<ISequentialInStream> inStreamTemp = inFile; |
|
460 if (!inFile->Open(_srcDirectoryPrefix + name)) |
|
461 return ::GetLastError(); |
|
462 *inStream = inStreamTemp.Detach(); |
|
463 return S_OK; |
|
464 COM_TRY_END |
|
465 } |
|
466 */ |
|
467 |
|
468 STDMETHODIMP CArchiveExtractCallback::CryptoGetTextPassword(BSTR *password) |
|
469 { |
|
470 COM_TRY_BEGIN |
|
471 if (!_cryptoGetTextPassword) |
|
472 { |
|
473 RINOK(_extractCallback2.QueryInterface(IID_ICryptoGetTextPassword, |
|
474 &_cryptoGetTextPassword)); |
|
475 } |
|
476 return _cryptoGetTextPassword->CryptoGetTextPassword(password); |
|
477 COM_TRY_END |
|
478 } |
|
479 |
|