00001
00002
00003
00004
00005 #include "StdAfx.h"
00006
00007 #include "ArchiveExtractCallback.h"
00008
00009 #include "Common/Wildcard.h"
00010 #include "Common/StringConvert.h"
00011
00012 #include "Windows/FileDir.h"
00013 #include "Windows/FileFind.h"
00014 #include "Windows/Time.h"
00015 #include "Windows/Defs.h"
00016 #include "Windows/PropVariant.h"
00017
00018 #include "Windows/PropVariantConversions.h"
00019
00020 #include "../../Common/FilePathAutoRename.h"
00021
00022 #include "../Common/ExtractingFilePath.h"
00023 #include "OpenArchive.h"
00024
00025 using namespace NWindows;
00026
00027 static const wchar_t *kCantAutoRename = L"ERROR: Can not create file with auto name";
00028 static const wchar_t *kCantRenameFile = L"ERROR: Can not rename existing file ";
00029 static const wchar_t *kCantDeleteOutputFile = L"ERROR: Can not delete output file ";
00030
00031 void CArchiveExtractCallback::Init(
00032 IInArchive *archiveHandler,
00033 IFolderArchiveExtractCallback *extractCallback2,
00034 bool stdOutMode,
00035 const UString &directoryPath,
00036 NExtract::NPathMode::EEnum pathMode,
00037 NExtract::NOverwriteMode::EEnum overwriteMode,
00038 const UStringVector &removePathParts,
00039 const UString &itemDefaultName,
00040 const FILETIME &utcLastWriteTimeDefault,
00041 UInt32 attributesDefault)
00042 {
00043 _stdOutMode = stdOutMode;
00044 _numErrors = 0;
00045 _extractCallback2 = extractCallback2;
00046 _itemDefaultName = itemDefaultName;
00047 _utcLastWriteTimeDefault = utcLastWriteTimeDefault;
00048 _attributesDefault = attributesDefault;
00049 _removePathParts = removePathParts;
00050 _pathMode = pathMode;
00051 _overwriteMode = overwriteMode;
00052 _archiveHandler = archiveHandler;
00053 _directoryPath = directoryPath;
00054 NFile::NName::NormalizeDirPathPrefix(_directoryPath);
00055 }
00056
00057 STDMETHODIMP CArchiveExtractCallback::SetTotal(UInt64 size)
00058 {
00059 return _extractCallback2->SetTotal(size);
00060 }
00061
00062 STDMETHODIMP CArchiveExtractCallback::SetCompleted(const UInt64 *completeValue)
00063 {
00064 return _extractCallback2->SetCompleted(completeValue);
00065 }
00066
00067 void CArchiveExtractCallback::CreateComplexDirectory(const UStringVector &dirPathParts)
00068 {
00069 UString fullPath = _directoryPath;
00070 for(int i = 0; i < dirPathParts.Size(); i++)
00071 {
00072 fullPath += dirPathParts[i];
00073 NFile::NDirectory::MyCreateDirectory(fullPath);
00074 fullPath += wchar_t(NFile::NName::kDirDelimiter);
00075 }
00076 }
00077
00078 static UString MakePathNameFromParts(const UStringVector &parts)
00079 {
00080 UString result;
00081 for(int i = 0; i < parts.Size(); i++)
00082 {
00083 if(i != 0)
00084 result += wchar_t(NFile::NName::kDirDelimiter);
00085 result += parts[i];
00086 }
00087 return result;
00088 }
00089
00090 STDMETHODIMP CArchiveExtractCallback::GetStream(UInt32 index,
00091 ISequentialOutStream **outStream, Int32 askExtractMode)
00092 {
00093 *outStream = 0;
00094 _outFileStream.Release();
00095 NCOM::CPropVariant propVariant;
00096 RINOK(_archiveHandler->GetProperty(index, kpidPath, &propVariant));
00097
00098 UString fullPath;
00099 if(propVariant.vt == VT_EMPTY)
00100 fullPath = _itemDefaultName;
00101 else
00102 {
00103 if(propVariant.vt != VT_BSTR)
00104 return E_FAIL;
00105 fullPath = propVariant.bstrVal;
00106 }
00107
00108
00109 _filePath = fullPath;
00110 _isSplit = false;
00111
00112 RINOK(_archiveHandler->GetProperty(index, kpidPosition, &propVariant));
00113 if (propVariant.vt != VT_EMPTY)
00114 {
00115 if (propVariant.vt != VT_UI8)
00116 return E_FAIL;
00117 _position = propVariant.uhVal.QuadPart;
00118 _isSplit = true;
00119 }
00120
00121 if(askExtractMode == NArchive::NExtract::NAskMode::kExtract)
00122 {
00123 if (_stdOutMode)
00124 {
00125 CMyComPtr<ISequentialOutStream> outStreamLoc = new CStdOutFileStream;
00126 *outStream = outStreamLoc.Detach();
00127 return S_OK;
00128 }
00129
00130 RINOK(_archiveHandler->GetProperty(index, kpidAttributes, &propVariant));
00131 if (propVariant.vt == VT_EMPTY)
00132 {
00133 _processedFileInfo.Attributes = _attributesDefault;
00134 _processedFileInfo.AttributesAreDefined = false;
00135 }
00136 else
00137 {
00138 if (propVariant.vt != VT_UI4)
00139 throw "incorrect item";
00140 _processedFileInfo.Attributes = propVariant.ulVal;
00141 _processedFileInfo.AttributesAreDefined = true;
00142 }
00143
00144 RINOK(IsArchiveItemFolder(_archiveHandler, index, _processedFileInfo.IsDirectory));
00145
00146 RINOK(_archiveHandler->GetProperty(index, kpidLastWriteTime, &propVariant));
00147 switch(propVariant.vt)
00148 {
00149 case VT_EMPTY:
00150 _processedFileInfo.UTCLastWriteTime = _utcLastWriteTimeDefault;
00151 break;
00152 case VT_FILETIME:
00153 _processedFileInfo.UTCLastWriteTime = propVariant.filetime;
00154 break;
00155 default:
00156 return E_FAIL;
00157 }
00158
00159 RINOK(_archiveHandler->GetProperty(index, kpidSize, &propVariant));
00160 bool newFileSizeDefined = (propVariant.vt != VT_EMPTY);
00161 UInt64 newFileSize;
00162 if (newFileSizeDefined)
00163 newFileSize = ConvertPropVariantToUInt64(propVariant);
00164
00165 bool isAnti = false;
00166 {
00167 NCOM::CPropVariant propVariantTemp;
00168 RINOK(_archiveHandler->GetProperty(index, kpidIsAnti,
00169 &propVariantTemp));
00170 if (propVariantTemp.vt == VT_BOOL)
00171 isAnti = VARIANT_BOOLToBool(propVariantTemp.boolVal);
00172 }
00173
00174 UStringVector pathParts;
00175 SplitPathToParts(fullPath, pathParts);
00176
00177 if(pathParts.IsEmpty())
00178 return E_FAIL;
00179 UString processedPath;
00180 switch(_pathMode)
00181 {
00182 case NExtract::NPathMode::kFullPathnames:
00183 {
00184 processedPath = fullPath;
00185 break;
00186 }
00187 case NExtract::NPathMode::kCurrentPathnames:
00188 {
00189
00190 int numRemovePathParts = _removePathParts.Size();
00191 if(pathParts.Size() <= numRemovePathParts)
00192 return E_FAIL;
00193 for(int i = 0; i < numRemovePathParts; i++)
00194 if(_removePathParts[i].CompareNoCase(pathParts[i]) != 0)
00195 return E_FAIL;
00196 pathParts.Delete(0, numRemovePathParts);
00197 processedPath = MakePathNameFromParts(pathParts);
00198 break;
00199 }
00200 case NExtract::NPathMode::kNoPathnames:
00201 {
00202 processedPath = pathParts.Back();
00203 pathParts.Delete(0, pathParts.Size() - 1);
00204 break;
00205 }
00206 }
00207 processedPath = GetCorrectPath(processedPath);
00208 if(!_processedFileInfo.IsDirectory)
00209 pathParts.DeleteBack();
00210
00211 MakeCorrectPath(pathParts);
00212
00213 if (!isAnti)
00214 if (!pathParts.IsEmpty())
00215 CreateComplexDirectory(pathParts);
00216
00217 UString fullProcessedPath = _directoryPath + processedPath;
00218
00219 if(_processedFileInfo.IsDirectory)
00220 {
00221 _diskFilePath = fullProcessedPath;
00222 if (isAnti)
00223 NFile::NDirectory::MyRemoveDirectory(_diskFilePath);
00224 return S_OK;
00225 }
00226
00227 if (!_isSplit)
00228 {
00229 NFile::NFind::CFileInfoW fileInfo;
00230 if(NFile::NFind::FindFile(fullProcessedPath, fileInfo))
00231 {
00232 switch(_overwriteMode)
00233 {
00234 case NExtract::NOverwriteMode::kSkipExisting:
00235 return S_OK;
00236 case NExtract::NOverwriteMode::kAskBefore:
00237 {
00238 Int32 overwiteResult;
00239 RINOK(_extractCallback2->AskOverwrite(
00240 fullProcessedPath, &fileInfo.LastWriteTime, &fileInfo.Size,
00241 fullPath, &_processedFileInfo.UTCLastWriteTime, newFileSizeDefined?
00242 &newFileSize : NULL, &overwiteResult))
00243
00244 switch(overwiteResult)
00245 {
00246 case NOverwriteAnswer::kCancel:
00247 return E_ABORT;
00248 case NOverwriteAnswer::kNo:
00249 return S_OK;
00250 case NOverwriteAnswer::kNoToAll:
00251 _overwriteMode = NExtract::NOverwriteMode::kSkipExisting;
00252 return S_OK;
00253 case NOverwriteAnswer::kYesToAll:
00254 _overwriteMode = NExtract::NOverwriteMode::kWithoutPrompt;
00255 break;
00256 case NOverwriteAnswer::kYes:
00257 break;
00258 case NOverwriteAnswer::kAutoRename:
00259 _overwriteMode = NExtract::NOverwriteMode::kAutoRename;
00260 break;
00261 default:
00262 throw 20413;
00263 }
00264 }
00265 }
00266 if (_overwriteMode == NExtract::NOverwriteMode::kAutoRename)
00267 {
00268 if (!AutoRenamePath(fullProcessedPath))
00269 {
00270 UString message = UString(kCantAutoRename) +
00271 fullProcessedPath;
00272 RINOK(_extractCallback2->MessageError(message));
00273 return E_ABORT;
00274 }
00275 }
00276 else if (_overwriteMode == NExtract::NOverwriteMode::kAutoRenameExisting)
00277 {
00278 UString existPath = fullProcessedPath;
00279 if (!AutoRenamePath(existPath))
00280 {
00281 UString message = kCantAutoRename + fullProcessedPath;
00282 RINOK(_extractCallback2->MessageError(message));
00283 return E_ABORT;
00284 }
00285 if(!NFile::NDirectory::MyMoveFile(fullProcessedPath, existPath))
00286 {
00287 UString message = UString(kCantRenameFile) + fullProcessedPath;
00288 RINOK(_extractCallback2->MessageError(message));
00289 return E_ABORT;
00290 }
00291 }
00292 else
00293 if (!NFile::NDirectory::DeleteFileAlways(fullProcessedPath))
00294 {
00295 UString message = UString(kCantDeleteOutputFile) +
00296 fullProcessedPath;
00297 RINOK(_extractCallback2->MessageError(message));
00298 return E_ABORT;
00299 }
00300 }
00301 }
00302 if (!isAnti)
00303 {
00304 _outFileStreamSpec = new COutFileStream;
00305 CMyComPtr<ISequentialOutStream> outStreamLoc(_outFileStreamSpec);
00306 if (!_outFileStreamSpec->File.Open(fullProcessedPath,
00307 _isSplit ? OPEN_ALWAYS: CREATE_ALWAYS))
00308 {
00309
00310 {
00311 UString message = L"can not open output file " + fullProcessedPath;
00312 RINOK(_extractCallback2->MessageError(message));
00313 return S_OK;
00314 }
00315 }
00316 if (_isSplit)
00317 {
00318 RINOK(_outFileStreamSpec->Seek(_position, STREAM_SEEK_SET, NULL));
00319 }
00320 _outFileStream = outStreamLoc;
00321 *outStream = outStreamLoc.Detach();
00322 }
00323 _diskFilePath = fullProcessedPath;
00324 }
00325 else
00326 {
00327 *outStream = NULL;
00328 }
00329 return S_OK;
00330 }
00331
00332 STDMETHODIMP CArchiveExtractCallback::PrepareOperation(Int32 askExtractMode)
00333 {
00334 _extractMode = false;
00335 switch (askExtractMode)
00336 {
00337 case NArchive::NExtract::NAskMode::kExtract:
00338 _extractMode = true;
00339 };
00340 return _extractCallback2->PrepareOperation(_filePath, askExtractMode, _isSplit ? &_position: 0);
00341 }
00342
00343 STDMETHODIMP CArchiveExtractCallback::SetOperationResult(Int32 operationResult)
00344 {
00345 switch(operationResult)
00346 {
00347 case NArchive::NExtract::NOperationResult::kOK:
00348 case NArchive::NExtract::NOperationResult::kUnSupportedMethod:
00349 case NArchive::NExtract::NOperationResult::kCRCError:
00350 case NArchive::NExtract::NOperationResult::kDataError:
00351 break;
00352 default:
00353 _outFileStream.Release();
00354 return E_FAIL;
00355 }
00356 if(_outFileStream != NULL)
00357 _outFileStreamSpec->File.SetLastWriteTime(&_processedFileInfo.UTCLastWriteTime);
00358 _outFileStream.Release();
00359 if (_extractMode && _processedFileInfo.AttributesAreDefined)
00360 NFile::NDirectory::MySetFileAttributes(_diskFilePath, _processedFileInfo.Attributes);
00361 RINOK(_extractCallback2->SetOperationResult(operationResult));
00362 return S_OK;
00363 }
00364
00365 STDMETHODIMP CArchiveExtractCallback::CryptoGetTextPassword(BSTR *password)
00366 {
00367 if (!_cryptoGetTextPassword)
00368 {
00369 RINOK(_extractCallback2.QueryInterface(IID_ICryptoGetTextPassword,
00370 &_cryptoGetTextPassword));
00371 }
00372 return _cryptoGetTextPassword->CryptoGetTextPassword(password);
00373 }
00374