OLD | NEW |
| (Empty) |
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 n
ame"; | |
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 fil
e "; | |
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 &utcMTimeDefault, | |
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 _utcMTimeDefault = utcMTimeDefault; | |
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 U
Int64 *outSize) | |
111 { | |
112 COM_TRY_BEGIN | |
113 return _localProgress->SetRatioInfo(inSize, outSize); | |
114 COM_TRY_END | |
115 } | |
116 | |
117 void CArchiveExtractCallback::CreateComplexDirectory(const UStringVector &dirPat
hParts, 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 &fil
etime, 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, ISequentialOutStre
am **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.IsDir)); | |
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, kpidAttrib, &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, kpidCTime, _processedFileInfo.CTime, _processedFileInfo
.CTimeDefined)); | |
228 RINOK(GetTime(index, kpidATime, _processedFileInfo.ATime, _processedFileInfo
.ATimeDefined)); | |
229 RINOK(GetTime(index, kpidMTime, _processedFileInfo.MTime, _processedFileInfo
.MTimeDefined)); | |
230 | |
231 bool isAnti = false; | |
232 RINOK(IsArchiveItemProp(_archiveHandler, index, kpidIsAnti, isAnti)); | |
233 | |
234 UStringVector pathParts; | |
235 SplitPathToParts(fullPath, pathParts); | |
236 | |
237 if(pathParts.IsEmpty()) | |
238 return E_FAIL; | |
239 int numRemovePathParts = 0; | |
240 switch(_pathMode) | |
241 { | |
242 case NExtract::NPathMode::kFullPathnames: | |
243 break; | |
244 case NExtract::NPathMode::kCurrentPathnames: | |
245 { | |
246 numRemovePathParts = _removePathParts.Size(); | |
247 if (pathParts.Size() <= numRemovePathParts) | |
248 return E_FAIL; | |
249 for (int i = 0; i < numRemovePathParts; i++) | |
250 if (_removePathParts[i].CompareNoCase(pathParts[i]) != 0) | |
251 return E_FAIL; | |
252 break; | |
253 } | |
254 case NExtract::NPathMode::kNoPathnames: | |
255 { | |
256 numRemovePathParts = pathParts.Size() - 1; | |
257 break; | |
258 } | |
259 } | |
260 pathParts.Delete(0, numRemovePathParts); | |
261 MakeCorrectPath(pathParts); | |
262 UString processedPath = MakePathNameFromParts(pathParts); | |
263 if (!isAnti) | |
264 { | |
265 if (!_processedFileInfo.IsDir) | |
266 { | |
267 if (!pathParts.IsEmpty()) | |
268 pathParts.DeleteBack(); | |
269 } | |
270 | |
271 if (!pathParts.IsEmpty()) | |
272 { | |
273 UString fullPathNew; | |
274 CreateComplexDirectory(pathParts, fullPathNew); | |
275 if (_processedFileInfo.IsDir) | |
276 NFile::NDirectory::SetDirTime(fullPathNew, | |
277 (WriteCTime && _processedFileInfo.CTimeDefined) ? &_processedFileInf
o.CTime : NULL, | |
278 (WriteATime && _processedFileInfo.ATimeDefined) ? &_processedFileInf
o.ATime : NULL, | |
279 (WriteMTime && _processedFileInfo.MTimeDefined) ? &_processedFileInf
o.MTime : &_utcMTimeDefault); | |
280 } | |
281 } | |
282 | |
283 | |
284 UString fullProcessedPath = _directoryPath + processedPath; | |
285 | |
286 if(_processedFileInfo.IsDir) | |
287 { | |
288 _diskFilePath = fullProcessedPath; | |
289 if (isAnti) | |
290 NFile::NDirectory::MyRemoveDirectory(_diskFilePath); | |
291 return S_OK; | |
292 } | |
293 | |
294 if (!_isSplit) | |
295 { | |
296 NFile::NFind::CFileInfoW fileInfo; | |
297 if(NFile::NFind::FindFile(fullProcessedPath, fileInfo)) | |
298 { | |
299 switch(_overwriteMode) | |
300 { | |
301 case NExtract::NOverwriteMode::kSkipExisting: | |
302 return S_OK; | |
303 case NExtract::NOverwriteMode::kAskBefore: | |
304 { | |
305 Int32 overwiteResult; | |
306 RINOK(_extractCallback2->AskOverwrite( | |
307 fullProcessedPath, &fileInfo.MTime, &fileInfo.Size, fullPath, | |
308 _processedFileInfo.MTimeDefined ? &_processedFileInfo.MTime : NULL
, | |
309 newFileSizeDefined ? &newFileSize : NULL, | |
310 &overwiteResult)) | |
311 | |
312 switch(overwiteResult) | |
313 { | |
314 case NOverwriteAnswer::kCancel: | |
315 return E_ABORT; | |
316 case NOverwriteAnswer::kNo: | |
317 return S_OK; | |
318 case NOverwriteAnswer::kNoToAll: | |
319 _overwriteMode = NExtract::NOverwriteMode::kSkipExisting; | |
320 return S_OK; | |
321 case NOverwriteAnswer::kYesToAll: | |
322 _overwriteMode = NExtract::NOverwriteMode::kWithoutPrompt; | |
323 break; | |
324 case NOverwriteAnswer::kYes: | |
325 break; | |
326 case NOverwriteAnswer::kAutoRename: | |
327 _overwriteMode = NExtract::NOverwriteMode::kAutoRename; | |
328 break; | |
329 default: | |
330 return E_FAIL; | |
331 } | |
332 } | |
333 } | |
334 if (_overwriteMode == NExtract::NOverwriteMode::kAutoRename) | |
335 { | |
336 if (!AutoRenamePath(fullProcessedPath)) | |
337 { | |
338 UString message = UString(kCantAutoRename) + fullProcessedPath; | |
339 RINOK(_extractCallback2->MessageError(message)); | |
340 return E_FAIL; | |
341 } | |
342 } | |
343 else if (_overwriteMode == NExtract::NOverwriteMode::kAutoRenameExisting) | |
344 { | |
345 UString existPath = fullProcessedPath; | |
346 if (!AutoRenamePath(existPath)) | |
347 { | |
348 UString message = kCantAutoRename + fullProcessedPath; | |
349 RINOK(_extractCallback2->MessageError(message)); | |
350 return E_FAIL; | |
351 } | |
352 if(!NFile::NDirectory::MyMoveFile(fullProcessedPath, existPath)) | |
353 { | |
354 UString message = UString(kCantRenameFile) + fullProcessedPath; | |
355 RINOK(_extractCallback2->MessageError(message)); | |
356 return E_FAIL; | |
357 } | |
358 } | |
359 else | |
360 if (!NFile::NDirectory::DeleteFileAlways(fullProcessedPath)) | |
361 { | |
362 UString message = UString(kCantDeleteOutputFile) + fullProcessedPath; | |
363 RINOK(_extractCallback2->MessageError(message)); | |
364 return S_OK; | |
365 // return E_FAIL; | |
366 } | |
367 } | |
368 } | |
369 if (!isAnti) | |
370 { | |
371 _outFileStreamSpec = new COutFileStream; | |
372 CMyComPtr<ISequentialOutStream> outStreamLoc(_outFileStreamSpec); | |
373 if (!_outFileStreamSpec->Open(fullProcessedPath, _isSplit ? OPEN_ALWAYS: C
REATE_ALWAYS)) | |
374 { | |
375 // if (::GetLastError() != ERROR_FILE_EXISTS || !isSplit) | |
376 { | |
377 UString message = L"can not open output file " + fullProcessedPath; | |
378 RINOK(_extractCallback2->MessageError(message)); | |
379 return S_OK; | |
380 } | |
381 } | |
382 if (_isSplit) | |
383 { | |
384 RINOK(_outFileStreamSpec->Seek(_position, STREAM_SEEK_SET, NULL)); | |
385 } | |
386 _outFileStream = outStreamLoc; | |
387 *outStream = outStreamLoc.Detach(); | |
388 } | |
389 _diskFilePath = fullProcessedPath; | |
390 } | |
391 else | |
392 { | |
393 *outStream = NULL; | |
394 } | |
395 return S_OK; | |
396 COM_TRY_END | |
397 } | |
398 | |
399 STDMETHODIMP CArchiveExtractCallback::PrepareOperation(Int32 askExtractMode) | |
400 { | |
401 COM_TRY_BEGIN | |
402 _extractMode = false; | |
403 switch (askExtractMode) | |
404 { | |
405 case NArchive::NExtract::NAskMode::kExtract: | |
406 _extractMode = true; | |
407 }; | |
408 return _extractCallback2->PrepareOperation(_filePath, _processedFileInfo.IsDir
, | |
409 askExtractMode, _isSplit ? &_position: 0); | |
410 COM_TRY_END | |
411 } | |
412 | |
413 STDMETHODIMP CArchiveExtractCallback::SetOperationResult(Int32 operationResult) | |
414 { | |
415 COM_TRY_BEGIN | |
416 switch(operationResult) | |
417 { | |
418 case NArchive::NExtract::NOperationResult::kOK: | |
419 case NArchive::NExtract::NOperationResult::kUnSupportedMethod: | |
420 case NArchive::NExtract::NOperationResult::kCRCError: | |
421 case NArchive::NExtract::NOperationResult::kDataError: | |
422 break; | |
423 default: | |
424 _outFileStream.Release(); | |
425 return E_FAIL; | |
426 } | |
427 if (_outFileStream != NULL) | |
428 { | |
429 _outFileStreamSpec->SetTime( | |
430 (WriteCTime && _processedFileInfo.CTimeDefined) ? &_processedFileInfo.CT
ime : NULL, | |
431 (WriteATime && _processedFileInfo.ATimeDefined) ? &_processedFileInfo.AT
ime : NULL, | |
432 (WriteMTime && _processedFileInfo.MTimeDefined) ? &_processedFileInfo.MT
ime : &_utcMTimeDefault); | |
433 _curSize = _outFileStreamSpec->ProcessedSize; | |
434 RINOK(_outFileStreamSpec->Close()); | |
435 _outFileStream.Release(); | |
436 } | |
437 UnpackSize += _curSize; | |
438 if (_processedFileInfo.IsDir) | |
439 NumFolders++; | |
440 else | |
441 NumFiles++; | |
442 | |
443 if (_extractMode && _processedFileInfo.AttributesAreDefined) | |
444 NFile::NDirectory::MySetFileAttributes(_diskFilePath, _processedFileInfo.Att
ributes); | |
445 RINOK(_extractCallback2->SetOperationResult(operationResult, _encrypted)); | |
446 return S_OK; | |
447 COM_TRY_END | |
448 } | |
449 | |
450 /* | |
451 STDMETHODIMP CArchiveExtractCallback::GetInStream( | |
452 const wchar_t *name, ISequentialInStream **inStream) | |
453 { | |
454 COM_TRY_BEGIN | |
455 CInFileStream *inFile = new CInFileStream; | |
456 CMyComPtr<ISequentialInStream> inStreamTemp = inFile; | |
457 if (!inFile->Open(_srcDirectoryPrefix + name)) | |
458 return ::GetLastError(); | |
459 *inStream = inStreamTemp.Detach(); | |
460 return S_OK; | |
461 COM_TRY_END | |
462 } | |
463 */ | |
464 | |
465 STDMETHODIMP CArchiveExtractCallback::CryptoGetTextPassword(BSTR *password) | |
466 { | |
467 COM_TRY_BEGIN | |
468 if (!_cryptoGetTextPassword) | |
469 { | |
470 RINOK(_extractCallback2.QueryInterface(IID_ICryptoGetTextPassword, | |
471 &_cryptoGetTextPassword)); | |
472 } | |
473 return _cryptoGetTextPassword->CryptoGetTextPassword(password); | |
474 COM_TRY_END | |
475 } | |
476 | |
OLD | NEW |