// AgentOut.cpp #include "StdAfx.h" #include "Common/IntToString.h" #include "Common/StringConvert.h" #include "Windows/Defs.h" #include "Windows/FileDir.h" #include "Windows/PropVariant.h" #include "Windows/PropVariantConversions.h" #include "Windows/Time.h" #include "../../Compress/Copy/CopyCoder.h" #include "../../Common/FileStreams.h" #include "../Common/UpdatePair.h" #include "../Common/EnumDirItems.h" #include "../Common/HandlerLoader.h" #include "../Common/UpdateCallback.h" #include "../Common/OpenArchive.h" #include "Agent.h" #include "UpdateCallbackAgent.h" using namespace NWindows; using namespace NCOM; static HRESULT CopyBlock(ISequentialInStream *inStream, ISequentialOutStream *outStream) { CMyComPtr copyCoder = new NCompress::CCopyCoder; return copyCoder->Code(inStream, outStream, NULL, NULL, NULL); } STDMETHODIMP CAgent::SetFolder(IFolderFolder *folder) { _archiveNamePrefix.Empty(); if (folder == NULL) { _agentFolder = NULL; return S_OK; } else { CMyComPtr archiveFolder = folder; CMyComPtr archiveFolderInternal; RINOK(archiveFolder.QueryInterface(IID_IArchiveFolderInternal, &archiveFolderInternal)); RINOK(archiveFolderInternal->GetAgentFolder(&_agentFolder)); } UStringVector pathParts; pathParts.Clear(); CMyComPtr folderItem = folder; if (folderItem != NULL) for (;;) { CMyComPtr newFolder; folderItem->BindToParentFolder(&newFolder); if (newFolder == NULL) break; NCOM::CPropVariant prop; if (folderItem->GetFolderProperty(kpidName, &prop) == S_OK) if (prop.vt == VT_BSTR) pathParts.Insert(0, (const wchar_t *)prop.bstrVal); folderItem = newFolder; } for(int i = 0; i < pathParts.Size(); i++) { _archiveNamePrefix += pathParts[i]; _archiveNamePrefix += L'\\'; } return S_OK; } STDMETHODIMP CAgent::SetFiles(const wchar_t *folderPrefix, const wchar_t **names, UInt32 numNames) { _folderPrefix = folderPrefix; _names.Clear(); _names.Reserve(numNames); for (UInt32 i = 0; i < numNames; i++) _names.Add(names[i]); return S_OK; } static HRESULT GetFileTime(CAgent *agent, UInt32 itemIndex, FILETIME &fileTime) { CPropVariant property; RINOK(agent->GetArchive()->GetProperty(itemIndex, kpidMTime, &property)); if (property.vt == VT_FILETIME) fileTime = property.filetime; else if (property.vt == VT_EMPTY) fileTime = agent->DefaultTime; else throw 4190407; return S_OK; } static HRESULT EnumerateArchiveItems(CAgent *agent, const CProxyFolder &item, const UString &prefix, CObjectVector &arcItems) { int i; for (i = 0; i < item.Files.Size(); i++) { const CProxyFile &fileItem = item.Files[i]; CArcItem ai; RINOK(::GetFileTime(agent, fileItem.Index, ai.MTime)); CPropVariant property; agent->GetArchive()->GetProperty(fileItem.Index, kpidSize, &property); ai.SizeDefined = (property.vt != VT_EMPTY); if (ai.SizeDefined) ai.Size = ConvertPropVariantToUInt64(property); ai.IsDir = false; ai.Name = prefix + fileItem.Name; ai.Censored = true; // test it ai.IndexInServer = fileItem.Index; arcItems.Add(ai); } for (i = 0; i < item.Folders.Size(); i++) { const CProxyFolder &dirItem = item.Folders[i]; UString fullName = prefix + dirItem.Name; if(dirItem.IsLeaf) { CArcItem ai; RINOK(::GetFileTime(agent, dirItem.Index, ai.MTime)); ai.IsDir = true; ai.SizeDefined = false; ai.Name = fullName; ai.Censored = true; // test it ai.IndexInServer = dirItem.Index; arcItems.Add(ai); } RINOK(EnumerateArchiveItems(agent, dirItem, fullName + UString(L'\\'), arcItems)); } return S_OK; } STDMETHODIMP CAgent::DoOperation( CCodecs *codecs, int formatIndex, const wchar_t *newArchiveName, const Byte *stateActions, const wchar_t *sfxModule, IFolderArchiveUpdateCallback *updateCallback100) { if (!CanUpdate()) return E_NOTIMPL; NUpdateArchive::CActionSet actionSet; int i; for (i = 0; i < NUpdateArchive::NPairState::kNumValues; i++) actionSet.StateActions[i] = (NUpdateArchive::NPairAction::EEnum)stateActions[i]; CDirItems dirItems; { UString folderPrefix = _folderPrefix; NFile::NName::NormalizeDirPathPrefix(folderPrefix); UStringVector errorPaths; CRecordVector errorCodes; dirItems.EnumerateDirItems2(folderPrefix, _archiveNamePrefix, _names, errorPaths, errorCodes); if (errorCodes.Size() > 0) return errorCodes.Front(); } CMyComPtr outArchive; if (GetArchive()) { RINOK(GetArchive()->QueryInterface(IID_IOutArchive, (void **)&outArchive)); } else { if (formatIndex < 0) return E_FAIL; RINOK(codecs->CreateOutArchive(formatIndex, outArchive)); #ifdef EXTERNAL_CODECS { CMyComPtr setCompressCodecsInfo; outArchive.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo); if (setCompressCodecsInfo) { RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(codecs)); } } #endif } NFileTimeType::EEnum fileTimeType; UInt32 value; RINOK(outArchive->GetFileTimeType(&value)); switch(value) { case NFileTimeType::kWindows: case NFileTimeType::kDOS: case NFileTimeType::kUnix: fileTimeType = NFileTimeType::EEnum(value); break; default: return E_FAIL; } CObjectVector arcItems; if (GetArchive()) { RINOK(ReadItems()); EnumerateArchiveItems(this, _proxyArchive->RootFolder, L"", arcItems); } CRecordVector updatePairs2; { CRecordVector updatePairs; GetUpdatePairInfoList(dirItems, arcItems, fileTimeType, updatePairs); UpdateProduce(updatePairs, actionSet, updatePairs2); } UInt32 numFiles = 0; for (i = 0; i < updatePairs2.Size(); i++) if (updatePairs2[i].NewData) numFiles++; if (updateCallback100) { RINOK(updateCallback100->SetNumFiles(numFiles)); } CUpdateCallbackAgent updateCallbackAgent; updateCallbackAgent.SetCallback(updateCallback100); CArchiveUpdateCallback *updateCallbackSpec = new CArchiveUpdateCallback; CMyComPtr updateCallback(updateCallbackSpec ); updateCallbackSpec->DirItems = &dirItems; updateCallbackSpec->ArcItems = &arcItems; updateCallbackSpec->UpdatePairs = &updatePairs2; updateCallbackSpec->Archive = GetArchive(); updateCallbackSpec->Callback = &updateCallbackAgent; COutFileStream *outStreamSpec = new COutFileStream; CMyComPtr outStream(outStreamSpec); UString archiveName = newArchiveName; { UString resultPath; int pos; if(!NFile::NDirectory::MyGetFullPathName(archiveName, resultPath, pos)) return E_FAIL; NFile::NDirectory::CreateComplexDirectory(resultPath.Left(pos)); } if (!outStreamSpec->Create(archiveName, true)) { // ShowLastErrorMessage(); return E_FAIL; } CMyComPtr setProperties; if (outArchive->QueryInterface(IID_ISetProperties, (void **)&setProperties) == S_OK) { if (m_PropNames.Size() == 0) { RINOK(setProperties->SetProperties(0, 0, 0)); } else { CRecordVector names; for(i = 0; i < m_PropNames.Size(); i++) names.Add((const wchar_t *)m_PropNames[i]); NWindows::NCOM::CPropVariant *propValues = new NWindows::NCOM::CPropVariant[m_PropValues.Size()]; try { for (int i = 0; i < m_PropValues.Size(); i++) propValues[i] = m_PropValues[i]; RINOK(setProperties->SetProperties(&names.Front(), propValues, names.Size())); } catch(...) { delete []propValues; return E_FAIL; } delete []propValues; } } m_PropNames.Clear(); m_PropValues.Clear(); if (sfxModule != NULL) { CInFileStream *sfxStreamSpec = new CInFileStream; CMyComPtr sfxStream(sfxStreamSpec); if (!sfxStreamSpec->Open(sfxModule)) return E_FAIL; // throw "Can't open sfx module"; RINOK(CopyBlock(sfxStream, outStream)); } RINOK(outArchive->UpdateItems(outStream, updatePairs2.Size(),updateCallback)); return outStreamSpec->Close(); } STDMETHODIMP CAgent::DoOperation2( const wchar_t *newArchiveName, const Byte *stateActions, const wchar_t *sfxModule, IFolderArchiveUpdateCallback *updateCallback100) { return DoOperation(_codecs, -1, newArchiveName, stateActions, sfxModule, updateCallback100); } HRESULT CAgent::CommonUpdate( const wchar_t *newArchiveName, int numUpdateItems, IArchiveUpdateCallback *updateCallback) { if (!CanUpdate()) return E_NOTIMPL; CMyComPtr outArchive; RINOK(GetArchive()->QueryInterface(IID_IOutArchive, (void **)&outArchive)); COutFileStream *outStreamSpec = new COutFileStream; CMyComPtr outStream(outStreamSpec); UString archiveName = newArchiveName; { UString resultPath; int pos; if(!NFile::NDirectory::MyGetFullPathName(archiveName, resultPath, pos)) throw 141716; NFile::NDirectory::CreateComplexDirectory(resultPath.Left(pos)); } /* bool isOK = false; for (int i = 0; i < (1 << 16); i++) { resultName = newArchiveName; if (i > 0) { wchar_t s[32]; ConvertUInt64ToString(i, s); resultName += s; } if (outStreamSpec->Open(realPath)) { isOK = true; break; } if (::GetLastError() != ERROR_FILE_EXISTS) return ::GetLastError(); } if (!isOK) return ::GetLastError(); */ if (!outStreamSpec->Create(archiveName, true)) { // ShowLastErrorMessage(); return E_FAIL; } RINOK(outArchive->UpdateItems(outStream, numUpdateItems, updateCallback)); return outStreamSpec->Close(); } STDMETHODIMP CAgent::DeleteItems( const wchar_t *newArchiveName, const UInt32 *indices, UInt32 numItems, IFolderArchiveUpdateCallback *updateCallback100) { if (!CanUpdate()) return E_NOTIMPL; CUpdateCallbackAgent updateCallbackAgent; updateCallbackAgent.SetCallback(updateCallback100); CArchiveUpdateCallback *updateCallbackSpec = new CArchiveUpdateCallback; CMyComPtr updateCallback(updateCallbackSpec); CUIntVector realIndices; _agentFolder->GetRealIndices(indices, numItems, realIndices); CRecordVector updatePairs; int curIndex = 0; UInt32 numItemsInArchive; RINOK(GetArchive()->GetNumberOfItems(&numItemsInArchive)); for (UInt32 i = 0; i < numItemsInArchive; i++) { if (curIndex < realIndices.Size()) if (realIndices[curIndex] == i) { curIndex++; continue; } CUpdatePair2 up2; up2.NewData = up2.NewProps = false; up2.IsAnti = false; // check it. Maybe it can be undefined up2.ArcIndex = i; updatePairs.Add(up2); } updateCallbackSpec->UpdatePairs = &updatePairs; updateCallbackSpec->Archive = GetArchive(); updateCallbackSpec->Callback = &updateCallbackAgent; return CommonUpdate(newArchiveName, updatePairs.Size(), updateCallback); } HRESULT CAgent::CreateFolder( const wchar_t *newArchiveName, const wchar_t *folderName, IFolderArchiveUpdateCallback *updateCallback100) { if (!CanUpdate()) return E_NOTIMPL; CUpdateCallbackAgent updateCallbackAgent; updateCallbackAgent.SetCallback(updateCallback100); CArchiveUpdateCallback *updateCallbackSpec = new CArchiveUpdateCallback; CMyComPtr updateCallback(updateCallbackSpec); CRecordVector updatePairs; UInt32 numItemsInArchive; RINOK(GetArchive()->GetNumberOfItems(&numItemsInArchive)); for (UInt32 i = 0; i < numItemsInArchive; i++) { CUpdatePair2 up2; up2.NewData = up2.NewProps = false; up2.IsAnti = false; // check it. up2.ArcIndex = i; updatePairs.Add(up2); } CUpdatePair2 up2; up2.NewData = up2.NewProps = true; up2.IsAnti = false; up2.DirIndex = 0; updatePairs.Add(up2); updatePairs.ReserveDown(); CDirItems dirItems; CDirItem di; di.Attrib = FILE_ATTRIBUTE_DIRECTORY; di.Size = 0; di.Name = _agentFolder->_proxyFolderItem->GetFullPathPrefix() + folderName; FILETIME ft; NTime::GetCurUtcFileTime(ft); di.CTime = di.ATime = di.MTime = ft; dirItems.Items.Add(di); updateCallbackSpec->Callback = &updateCallbackAgent; updateCallbackSpec->DirItems = &dirItems; updateCallbackSpec->UpdatePairs = &updatePairs; updateCallbackSpec->Archive = GetArchive(); return CommonUpdate(newArchiveName, updatePairs.Size(), updateCallback); } HRESULT CAgent::RenameItem( const wchar_t *newArchiveName, const UInt32 *indices, UInt32 numItems, const wchar_t *newItemName, IFolderArchiveUpdateCallback *updateCallback100) { if (!CanUpdate()) return E_NOTIMPL; if (numItems != 1) return E_INVALIDARG; CUpdateCallbackAgent updateCallbackAgent; updateCallbackAgent.SetCallback(updateCallback100); CArchiveUpdateCallback *updateCallbackSpec = new CArchiveUpdateCallback; CMyComPtr updateCallback(updateCallbackSpec); CUIntVector realIndices; _agentFolder->GetRealIndices(indices, numItems, realIndices); UString fullPrefix = _agentFolder->GetFullPathPrefixPlusPrefix(indices[0]); UString oldItemPath = fullPrefix + _agentFolder->GetName(indices[0]); UString newItemPath = fullPrefix + newItemName; CRecordVector updatePairs; UStringVector newNames; int curIndex = 0; UInt32 numItemsInArchive; RINOK(GetArchive()->GetNumberOfItems(&numItemsInArchive)); for (UInt32 i = 0; i < numItemsInArchive; i++) { if (curIndex < realIndices.Size()) if (realIndices[curIndex] == i) { CUpdatePair2 up2; up2.NewData = false; up2.NewProps = true; RINOK(IsArchiveItemAnti(GetArchive(), i, up2.IsAnti)); up2.ArcIndex = i; UString oldFullPath; RINOK(GetArchiveItemPath(GetArchive(), i, DefaultName, oldFullPath)); if (oldItemPath.CompareNoCase(oldFullPath.Left(oldItemPath.Length())) != 0) return E_INVALIDARG; up2.NewNameIndex = newNames.Add(newItemPath + oldFullPath.Mid(oldItemPath.Length())); updatePairs.Add(up2); curIndex++; continue; } CUpdatePair2 up2; up2.NewData = up2.NewProps = false; up2.IsAnti = false; up2.ArcIndex = i; updatePairs.Add(up2); } updateCallbackSpec->Callback = &updateCallbackAgent; updateCallbackSpec->UpdatePairs = &updatePairs; updateCallbackSpec->NewNames = &newNames; updateCallbackSpec->Archive = GetArchive(); return CommonUpdate(newArchiveName, updatePairs.Size(), updateCallback); } STDMETHODIMP CAgent::SetProperties(const wchar_t **names, const PROPVARIANT *values, INT32 numProperties) { m_PropNames.Clear(); m_PropValues.Clear(); for (int i = 0; i < numProperties; i++) { m_PropNames.Add(names[i]); m_PropValues.Add(values[i]); } return S_OK; }