windows-nt/Source/XPSP1/NT/inetsrv/iis/utils/cfgmnt/verengine.cpp
2020-09-26 16:20:57 +08:00

698 lines
17 KiB
C++

// VerEngine.cpp: implementation of the CVerEngine class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "VerEngine.h"
#include "ssauterr.h"
#include "Error.h"
#include <COMDEF.h>
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CVerEngine::CVerEngine()
{
}
CVerEngine::~CVerEngine()
{
}
HRESULT CVerEngine::NewInit(LPCTSTR szVSSRootPrj)
{
// save the root prj
m_szVSSRootPrj = szVSSRootPrj;
HRESULT hr = E_FAIL;
// check if we already have a db instance
if(!m_pIDB)
{
// create db instance
hr = CoCreateInstance(CLSID_VSSDatabase,
NULL,
CLSCTX_INPROC_SERVER,
IID_IVSSDatabase,
(void**)&m_pIDB);
if(FAILED(hr))
return hr;
}
// Open the database
hr = m_pIDB->Open(m_bstrSrcSafeIni,m_bstrUsername,m_bstrPassword);
if(FAILED(hr))
return hr;
return hr;
}
HRESULT CVerEngine::ShutDown()
{
// release of interface ptr here and not during destructor,
// since CVerEngine could live on stack in the same frame that CoUninitialize() is called,
// i.e. CoUninitialize() would be called before the destructor calls release and gets
// an Access Violation.
m_pIDB.Release();
return S_OK;
}
HRESULT CVerEngine::AddPrj(LPCTSTR szBasePrj,LPCTSTR szRelSpec)
{
_ASSERT(szBasePrj && szRelSpec);
HRESULT hr = S_OK;
CComPtr<IVSSItem> pIItem;
wstring szPrj(szBasePrj);
MakePrjSpec(szPrj,szRelSpec);
// see if the item exists
CError::Trace(szPrj.c_str()); CError::Trace(" Add ");
hr = GetPrjEx(szPrj.c_str(),&pIItem,true);
if( SUCCEEDED(hr) )
{
if(hr == S_FALSE)
CError::Trace("created ");
}
else
FAIL_RTN1(hr,"\nGetPrjEx");
CError::Trace("\n");
return hr;
}
HRESULT CVerEngine::RenamePrj(LPCTSTR szBasePrj,LPCTSTR szRelSpec,LPCTSTR szRelSpecOld)
{
_ASSERTE(szBasePrj && szRelSpec && szRelSpecOld);
HRESULT hr;
CComPtr<IVSSItem> pIItem;
wstring szItem(szBasePrj);
MakePrjSpec(szItem,szRelSpecOld);
// see if the item exists
CError::Trace(szRelSpecOld); CError::Trace(" Rename to "); CError::Trace(szRelSpec);
hr = GetPrjEx(szItem.c_str(),&pIItem,true);
if(SUCCEEDED(hr))
{
wstring szFileName(szRelSpec);
int iFileNameIndex = szFileName.find_last_of(L"\\/");
if(iFileNameIndex == wstring::npos)
iFileNameIndex = 0;
else iFileNameIndex++;
hr = pIItem->put_Name(_bstr_t(szFileName.substr(iFileNameIndex).c_str()));
IF_FAIL_RTN1(hr,"\nput_Name");
}
else
FAIL_RTN1(hr,"\nGetPrjEx");
CError::Trace("\n");
return hr;
}
HRESULT CVerEngine::Rename(LPCTSTR szBasePrj,LPCTSTR szDir,LPCTSTR szRelSpec,LPCTSTR szRelSpecOld)
{
_ASSERTE(szBasePrj && szRelSpec && szRelSpecOld);
HRESULT hr;
CComPtr<IVSSItem> pIItem;
wstring szOldItem(szBasePrj);
MakePrjSpec(szOldItem,szRelSpecOld);
// see if the item exists
CError::Trace(szRelSpecOld); CError::Trace(" Rename to "); CError::Trace(szRelSpec);
hr = GetItemEx(szOldItem.c_str(),&pIItem,true);
if(SUCCEEDED(hr))
{
if(hr == S_FALSE)
{
CError::Trace(" created ");
// file was created, therefore let's checkin the old version
_ASSERTE(szDir);
wstring szFileSpec(szDir);
szFileSpec.append(L"\\").append(szRelSpec);
hr = Sync2(szBasePrj,szRelSpecOld,szFileSpec.c_str());
IF_FAIL_RTN1(hr,"\nSync");
}
wstring szItem(szRelSpec);
int iFileNameIndex = szItem.find_last_of(L"\\/");
if(iFileNameIndex == wstring::npos)
iFileNameIndex = 0;
else
iFileNameIndex++;
CComBSTR bstrFileName(szItem.substr(iFileNameIndex).c_str());
hr = pIItem->put_Name(bstrFileName);
IF_FAIL_RTN1(hr,"\nput_Name");
}
else
FAIL_RTN1(hr,"\nGetItemEx");
CError::Trace("\n");
return hr;
}
HRESULT CVerEngine::Sync2(LPCTSTR szPrj,LPCTSTR szFileName,LPCTSTR szFileSpec)
{
// return Sync(szPrj,NULL,szFileName,szFileSpec);
// @todo: handle errors
HRESULT hr;
CComPtr<IVSSItem> pIItem;
wstring szItem(szPrj);
MakePrjSpec(szItem,szFileName);
// complete file/prj specs
wstring szFSpec;
szFSpec = szFileSpec;
// see if the item exists
CError::Trace(szItem.c_str()); CError::Trace(" Sync ");
hr = GetItemEx(szItem.c_str(),&pIItem,true);
if(SUCCEEDED(hr))
{
hr = CheckIn(pIItem,szFSpec.c_str());
if(hr == ESS_FILE_SHARE)
{
// File %s is already open, meaning is held open by other process
// Let's hope they close the file and we can try to add it agian,
// so let's ignore it for now
CError::Trace("not checked in(isopen)\n");
return S_FALSE;
}
else
IF_FAIL_RTN1(hr,"\nCheckin");
CError::Trace("synced ");
}
else
FAIL_RTN1(hr,"\nget_VSSItem");
CError::Trace("\n");
return hr;
}
HRESULT CVerEngine::Sync(LPCTSTR szBasePrj,LPCTSTR szDir,LPCTSTR szRelSpec,LPCTSTR szFileSpec)
{
// @todo: handle errors
_ASSERT(m_pIDB && szBasePrj && szRelSpec);
_ASSERTE(szDir||szFileSpec);
HRESULT hr;
CComPtr<IVSSItem> pIItem;
wstring szItem(szBasePrj);
MakePrjSpec(szItem,szRelSpec);
// complete file/prj specs
wstring szFSpec;
if(szDir)
{
szFSpec = szDir;
szFSpec.append(L"\\").append(szRelSpec);
}
else
{
_ASSERTE(szFileSpec);
szFSpec = szFileSpec;
}
// see if the item exists
CError::Trace(szRelSpec); CError::Trace(" Sync ");
hr = GetItemEx(szItem.c_str(),&pIItem,false);
if(SUCCEEDED(hr))
{
hr = CheckIn(pIItem,szFSpec.c_str());
if(hr == ESS_FILE_SHARE)
{
// File %s is already open, meaning is held open by other process
// Let's hope they close the file and we can try to add it agian,
// so let's ignore it for now
CError::Trace("not checked in(isopen)\n");
return S_FALSE;
}
else
IF_FAIL_RTN1(hr,"\nCheckin");
CError::Trace("synced ");
}
else if(hr == ESS_VS_NOT_FOUND)
{
hr = Add(szItem.c_str(),szFSpec.c_str());
if(hr == ESS_FILE_SHARE)
{
// File %s is already open, meaning is held open by other process
// Let's hope they close the file and we can try to add it agian,
// so let's ignore it for now
CError::Trace("not added(isopen)\n");
return S_FALSE;
}
else
IF_FAIL_RTN1(hr,"\nAdd");
CError::Trace("added ");
}
else
FAIL_RTN1(hr,"\nget_VSSItem");
CError::Trace("\n");
return hr;
}
HRESULT CVerEngine::Delete(LPCTSTR szBasePrj,LPCTSTR szRelSpec)
{
_ASSERT(m_pIDB && szBasePrj && szRelSpec);
HRESULT hr = S_OK;
CComPtr<IVSSItem> pIItem;
wstring szItem(szBasePrj);
MakePrjSpec(szItem,szRelSpec);
// see if the item exists
CError::Trace(szItem.c_str()); CError::Trace(" Delete ");
hr = GetItemEx(szItem.c_str(),&pIItem,false);
if( SUCCEEDED(hr) )
{
CError::Trace("exists ");
// delete the file
hr = pIItem->put_Deleted(true);
IF_FAIL_RTN1(hr,"\nput_Delete");
CError::Trace("deleted ");
}
else if( hr == ESS_VS_NOT_FOUND )
{
CError::Trace("not-exist ");
// This is bad. The file should have been in version control.
// We can't add the file and delete it from VSS since the file
// might no longer exist. We could create an empty dummy file,
// but that's more confusing than helpfull.
// Let's just log this error
// @todo: log condition that file doesn't exist in VSS
hr = S_OK;
}
else
// This is really bad. There is some other error. Maybe we should try and
// shutdown the srcsafe db and start it up again (this is slooowww!!!)
// or maybe just write the failure to the log
FAIL_RTN1(hr,"\nGetItemEx");
CError::Trace("\n");
return hr;
}
void CVerEngine::MakePrjSpec(wstring &szDest,LPCTSTR szSource)
{
// szDest = m_szVSSRootPrj + [/]
if(m_szVSSRootPrj[m_szVSSRootPrj.length()-1] != L'/' && szDest[0] != L'/')
szDest.insert(0,L"/");
szDest.insert(0,m_szVSSRootPrj.c_str());
// szDest = szDest + [/] + szSource
if(szDest[szDest.length()-1] != L'/' && szSource[0] != L'/')
szDest.append(L"/");
szDest.append(szSource);
// convert all backslashes with slashes
int pos = 0;
while((pos = szDest.find(L'\\',pos)) != wstring::npos)
{
szDest[pos] = L'/';
pos++;
}
}
HRESULT CVerEngine::Add(LPCTSTR szItem,LPCTSTR szFileSpec)
{
_ASSERTE(szItem && szFileSpec);
HRESULT hr = S_OK;
CComPtr<IVSSItem> pIPrj;
CComPtr<IVSSItem> pIItem;
// get prj
wstring szTmp = szItem;
int iFileNameIndex = szTmp.find_last_of(L"/");
if(iFileNameIndex == wstring::npos)
return E_FAIL;
hr = GetPrjEx(szTmp.substr(0,iFileNameIndex).c_str(),&pIPrj,true);
IF_FAIL_RTN1(hr,"GetPrjEx");
CComBSTR bstrFileSpec(szFileSpec);
hr = pIPrj->Add(bstrFileSpec,NULL,VSSFLAG_USERRONO|VSSFLAG_GETNO,&pIItem); // VSSFLAG_KEEPYES
if(hr == 0x80040000) // @todo tmp fix, since pIPrj->Add has a bug when called with VSSFLAG_KEEPYES
hr = S_OK;
IF_FAIL_RTN1(hr,"Add");
return hr;
}
HRESULT CVerEngine::GetLocalWritable(LPCTSTR szFileSpec,LPCTSTR szBasePrj,LPCTSTR szRelSpec)
{
_ASSERTE(m_pIDB && szFileSpec && szBasePrj && szRelSpec);
HRESULT hr = S_OK;
CComPtr<IVSSItem> pIItem;
wstring szItem(szBasePrj);
MakePrjSpec(szItem,szRelSpec);
// see if the item exists
CError::Trace(szBasePrj); CError::Trace(L"/"); CError::Trace(szRelSpec); CError::Trace(" Get ");
hr = GetItemEx(szItem.c_str(),&pIItem,false);
if(SUCCEEDED(hr))
{
CError::Trace("exists ");
// checkout file
CComBSTR bstrFileSpec(szFileSpec);
hr = pIItem->Get(&bstrFileSpec,VSSFLAG_REPREPLACE|VSSFLAG_USERRONO);
IF_FAIL_RTN1(hr,"\nGet");
CError::Trace("gotten ");
}
else if(hr == ESS_VS_NOT_FOUND)
{
HANDLE hFile = NULL;
hFile = CreateFile(szFileSpec,
GENERIC_READ|GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_TEMPORARY|FILE_FLAG_SEQUENTIAL_SCAN,
NULL);
if(hFile == INVALID_HANDLE_VALUE)
{
hFile = NULL;
hr = GetLastError();
FAIL_RTN1(hr,"\nCreateFile");
}
CloseHandle(hFile);
hFile = NULL;
hr = S_OK;
}
CError::Trace("\n");
return hr;
}
HRESULT CVerEngine::CheckOut(LPCTSTR szFileSpec,LPCTSTR szBasePrj,LPCTSTR szRelSpec)
{
_ASSERTE(m_pIDB && szFileSpec && szBasePrj && szRelSpec);
HRESULT hr = S_OK;
CComPtr<IVSSItem> pIItem;
wstring szItem(szBasePrj);
MakePrjSpec(szItem,szRelSpec);
// see if the item exists
CError::Trace(szBasePrj); CError::Trace(L"/"); CError::Trace(szRelSpec); CError::Trace(" Checkout ");
hr = GetItemEx(szItem.c_str(),&pIItem,true);
if( SUCCEEDED(hr) )
{
CError::Trace("exists ");
// checkout file
hr = CheckOutLocal(pIItem,szFileSpec);
IF_FAIL_RTN1(hr,"\nCheckout");
CError::Trace("gotten ");
}
else
FAIL_RTN1(hr,"\nGetItemEx");
CError::Trace("\n");
return hr;
}
HRESULT CVerEngine::CheckOutNoGet(IVSSItem *pIItem)
{
_ASSERTE(pIItem);
HRESULT hr = S_OK;
long iStatus = 0;
// is files checked out?
hr = pIItem->get_IsCheckedOut(&iStatus);
IF_FAIL_RTN1(hr,"\nget_IsCheckOut");
// check it out to me
if(iStatus != VSSFILE_CHECKEDOUT_ME)
{
hr = pIItem->Checkout(NULL,NULL,VSSFLAG_GETNO);
IF_FAIL_RTN1(hr,"\nCheckout");
}
return hr;
}
HRESULT CVerEngine::CheckIn(IVSSItem *pIItem,LPCTSTR szFileSpec)
{
_ASSERTE(pIItem && szFileSpec);
HRESULT hr = S_OK;
hr = CheckOutNoGet(pIItem);
if(FAILED(hr))
return hr;
// checkin
hr = pIItem->Checkin(NULL,_bstr_t(szFileSpec),VSSFLAG_KEEPYES);
return hr;
}
HRESULT CVerEngine::CheckOutGet(IVSSItem *pIItem)
{
_ASSERTE(pIItem);
HRESULT hr = S_OK;
long iStatus = 0;
// is files checked out?
hr = pIItem->get_IsCheckedOut(&iStatus);
if(FAILED(hr))
return hr;
// check it out to me
if(iStatus != VSSFILE_CHECKEDOUT_ME)
hr = pIItem->Checkout(NULL,NULL,0);
return hr;
}
HRESULT CVerEngine::CheckOutLocal(IVSSItem *pIItem,LPCTSTR szFileSpec)
{
_ASSERTE(pIItem);
HRESULT hr = S_OK;
long iStatus = 0;
// is files checked out?
hr = pIItem->get_IsCheckedOut(&iStatus);
if(FAILED(hr))
return hr;
// check it out to me
if(iStatus != VSSFILE_CHECKEDOUT_ME)
{
hr = pIItem->Checkout(NULL,_bstr_t(szFileSpec),0);
}
else
{
CComBSTR bstrFileSpec(szFileSpec);
hr = pIItem->Get(&bstrFileSpec,0);
}
return hr;
}
HRESULT CVerEngine::GetPrjEx(LPCTSTR szPrj,IVSSItem **hIPrj,bool bCreate)
{
_ASSERTE(hIPrj && szPrj);
HRESULT hr = S_OK;
*hIPrj = NULL;
_bstr_t bstrPrj(szPrj);
hr = m_pIDB->get_VSSItem(bstrPrj,false,hIPrj);
if( hr == ESS_VS_NOT_FOUND
&& bCreate )
{
// does it exist as delete
hr = m_pIDB->get_VSSItem(bstrPrj,true,hIPrj);
if(SUCCEEDED(hr))
{
hr = (*hIPrj)->put_Deleted(false); // make sure it's not deleted
}
else if(hr == ESS_VS_NOT_FOUND)
{
// find the top-most prj that exists
CComPtr<IVSSItem> pItmp;
wstring sztmp = szPrj;
int iPos = wstring::npos;
while( hr == ESS_VS_NOT_FOUND )
{
iPos = sztmp.find_last_of(L"/");
if(iPos == wstring::npos)
return E_FAIL;
sztmp = sztmp.substr(0,iPos).c_str();
if(sztmp.size() == 1) // if we reached $/
sztmp = L"$/"; // we need to have the / in $/
hr = m_pIDB->get_VSSItem(_bstr_t(sztmp.c_str()),false,&pItmp);
}
IF_FAIL_RTN1(hr,"get_VSSItem");
// add recursivly the remaining subprojects
CComPtr<IVSSItem> pItmp2;
int iPos2 = 0;
sztmp = szPrj;
_bstr_t bstrSubPrj;
while( iPos2 != wstring::npos )
{
++iPos;
iPos2 = sztmp.find_first_of(L"/",iPos);
if(iPos2 == wstring::npos)
bstrSubPrj = sztmp.substr(iPos,sztmp.length()-iPos).c_str();
else
bstrSubPrj = sztmp.substr(iPos,iPos2-iPos).c_str();
hr = pItmp->NewSubproject(bstrSubPrj,NULL,&pItmp2);
IF_FAIL_RTN1(hr,"NewSubproject");
iPos = iPos2;
pItmp.Release();
pItmp = pItmp2;
pItmp2.Release();
}
*hIPrj = pItmp;
(*hIPrj)->AddRef();
pItmp.Release();
hr = S_FALSE; // signal that we created it
}
}
IF_FAIL_RTN1(hr,"get_VSSItem");
return hr;
}
HRESULT CVerEngine::GetItemEx(LPCTSTR szItem,IVSSItem **hIItem,bool bCreate)
{
_ASSERTE(hIItem && szItem);
HRESULT hr = S_OK;
*hIItem = NULL;
_bstr_t bstrItem(szItem);
hr = m_pIDB->get_VSSItem(bstrItem,false,hIItem);
if( hr == ESS_VS_NOT_FOUND
&& bCreate )
{
// does it exist as delete
hr = m_pIDB->get_VSSItem(bstrItem,true,hIItem);
if(SUCCEEDED(hr))
{
hr = (*hIItem)->put_Deleted(false); // make sure it's not deleted
IF_FAIL_RTN1(hr,"put_Deleted");
hr = S_FALSE;
}
else if(hr == ESS_VS_NOT_FOUND)
{
CComPtr<IVSSItem> pIPrj;
// get prj
wstring szItem = szItem;
int iFileNameIndex = szItem.find_last_of(L"/");
if(iFileNameIndex == wstring::npos)
return E_FAIL;
hr = GetPrjEx(_bstr_t(szItem.substr(0,iFileNameIndex).c_str()),&pIPrj,bCreate);
IF_FAIL_RTN1(hr,"GetPrjEx");
// add the file to the prj
HANDLE hFile = NULL;
TCHAR szTmpSpec[MAX_PATH];
BOOL b = FALSE;
CComBSTR bstrFileSpec;
// create an empty file szFileName in tmp dir
GetTempPath(MAX_PATH,szTmpSpec);
GetTempFileName(szTmpSpec,L"",0,szTmpSpec); // creates tmp file
b = DeleteFile(szTmpSpec); // delete tmp file since we want tmp dir
b = CreateDirectory(szTmpSpec,NULL); // create tmp dir
bstrFileSpec = szTmpSpec;
bstrFileSpec.Append(L"\\");
bstrFileSpec.Append(szItem.substr(iFileNameIndex+1).c_str());
hFile = CreateFile(bstrFileSpec, // create file in tmp dir
GENERIC_READ|GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_TEMPORARY,
NULL);
CloseHandle(hFile);
// add this file
hr = pIPrj->Add(bstrFileSpec,NULL,VSSFLAG_KEEPYES,hIItem);
b = DeleteFile(bstrFileSpec);
b = RemoveDirectory(szTmpSpec);
hr = S_FALSE;
}
}
else if(hr == ESS_VS_NOT_FOUND)
return hr;
IF_FAIL_RTN1(hr,"get_VSSItem");
return hr;
}
void CVerEngine::EliminateCommon(list<wstring> &ListOne, list<wstring> &ListTwo)
{
int sizeOne = ListOne.size();
int sizeTwo = ListTwo.size();
if(sizeOne == 0 || sizeTwo == 0)
return;
list<wstring> &List1 = ListTwo;
list<wstring> &List2 = ListOne;
if(sizeOne >= sizeTwo)
{
List1 = ListOne;
List2 = ListTwo;
}
list<wstring>::iterator i;
list<wstring>::iterator j;
for(i = List1.begin(); i != List1.end(); ++i)
{
for(j = List2.begin(); j != List2.end(); ++j)
{
if((*i).compare(*j) == 0)
{
List1.erase(i);
List2.erase(j);
break;
}
}
}
}
HRESULT CVerEngine::SyncPrj(LPCTSTR szBasePrj,LPCTSTR szDir)
{
bool result = true;
typedef list<wstring> wstringlist;
wstringlist FileList;
wstringlist DirList;
WIN32_FIND_DATA finddata;
HANDLE hFind = FindFirstFile( wstring(szDir).append(L"\\*.*").c_str(), &finddata);
if(hFind == INVALID_HANDLE_VALUE && GetLastError() != ERROR_NO_MORE_FILES)
return GetLastError();
do
{
if(finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
DirList.insert(DirList.end(),finddata.cFileName);
else
FileList.insert(FileList.end(),finddata.cFileName);
}
while(FindNextFile(hFind,&finddata));
FindClose(hFind);
hFind = 0;
HRESULT hr;
wstringlist::iterator i;
for(i = FileList.begin(); i != FileList.end(); ++i)
{
hr = Sync(szBasePrj,
szDir,
(*i).c_str());
IF_FAIL_RTN1(hr,"Sync");
}
for(i = DirList.begin(); i != DirList.end(); ++i)
{
}
return S_OK;
}