Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1093 lines
41 KiB

//---------------------------------------------------------------------------
// UPDTUPN.cpp
//
// Comment: This is a COM object extension for the MCS DCTAccountReplicator.
// This object implements the IExtendAccountMigration interface.
// The process method on this object updates the userPrincipalName
// property on the user object.
//
// (c) Copyright 1995-1998, Mission Critical Software, Inc., All Rights Reserved
//
// Proprietary and confidential to Mission Critical Software, Inc.
//---------------------------------------------------------------------------
#include "stdafx.h"
#include "ARExt.h"
#include "ARExt_i.c"
#include "UPNUpdt.h"
#include "ErrDCT.hpp"
#include "Names.hpp"
#include "resstr.h"
#include <GetDcName.h>
#include <Array.h>
#include "AdsiHelpers.h"
//#import "\bin\NetEnum.tlb" no_namespace
#import "NetEnum.tlb" no_namespace
#include "UpdtUPN.h"
TErrorDct err;
/////////////////////////////////////////////////////////////////////////////
// CUpdtUPN
StringLoader gString;
#define SEQUENCE_UPPER_BOUND 999
//---------------------------------------------------------------------------
// Get and set methods for the properties.
//---------------------------------------------------------------------------
STDMETHODIMP CUpdtUPN::get_sName(BSTR *pVal)
{
*pVal = m_sName;
return S_OK;
}
STDMETHODIMP CUpdtUPN::put_sName(BSTR newVal)
{
m_sName = newVal;
return S_OK;
}
STDMETHODIMP CUpdtUPN::get_sDesc(BSTR *pVal)
{
*pVal = m_sDesc;
return S_OK;
}
STDMETHODIMP CUpdtUPN::put_sDesc(BSTR newVal)
{
m_sDesc = newVal;
return S_OK;
}
//---------------------------------------------------------------------------
// ProcessObject : This method doesn't do anything.
//---------------------------------------------------------------------------
STDMETHODIMP CUpdtUPN::PreProcessObject(
IUnknown *pSource, //in- Pointer to the source AD object
IUnknown *pTarget, //in- Pointer to the target AD object
IUnknown *pMainSettings, //in- Varset filled with the settings supplied by user
IUnknown **ppPropsToSet, //in,out - Varset filled with Prop-Value pairs that will be set
// once all extension objects are executed.
EAMAccountStats* pStats
)
{
IVarSetPtr pVs = pMainSettings;
_variant_t var;
_bstr_t sTemp;
_bstr_t sUPN;
_bstr_t sPref;
_bstr_t sSuff;
IADs * pAds = NULL;
IADs * pAdsSource = NULL;
HRESULT hr;
c_array<WCHAR> sTempUPN(7000);
long ub, lb;
_bstr_t sFull;
_variant_t HUGEP * pDt;
_bstr_t sAdsPath;
_variant_t varDN;
_bstr_t sIntraforest;
_bstr_t sDomainDNS;
_bstr_t sTargetOU;
WCHAR fileName[MAX_PATH];
bool bReplace = false;
tstring sSAMName;
tstring sUPNName;
_bstr_t sOldUPN;
bool bConflicted = false;
SUPNStruc UPNStruc;
// We need to process the user accounts only
sTemp = pVs->get(GET_BSTR(DCTVS_CopiedAccount_Type));
if (!sTemp.length())
return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
if (_wcsicmp((WCHAR*)sTemp,L"user") && _wcsicmp((WCHAR*)sTemp,L"inetOrgPerson"))
return S_OK;
//store the name of this user in the UPN list
sSAMName = _bstr_t(pVs->get(GET_BSTR(DCTVS_CopiedAccount_SourceSam)));
//get the target domain DNS name
sDomainDNS = pVs->get(GET_BSTR(DCTVS_Options_TargetDomainDns));
//get the target OU path
sTargetOU = pVs->get(GET_BSTR(DCTVS_Options_OuPath));
//if not retrieved yet, get the default UPN suffix for this domain
if (m_sUPNSuffix.length() == 0)
{
//if failed, use the domain's DNS name
if (!GetDefaultUPNSuffix(sDomainDNS, sTargetOU))
m_sUPNSuffix = sDomainDNS;
}
// Get the Error log filename from the Varset
wcscpy(fileName, (WCHAR*)(pVs->get(GET_BSTR(DCTVS_Options_Logfile)).bstrVal));
// Open the error log
err.LogOpen(fileName, 1);
sPref = pVs->get(GET_BSTR(DCTVS_Options_Prefix));
sSuff = pVs->get(GET_BSTR(DCTVS_Options_Suffix));
sIntraforest = pVs->get(GET_BSTR(DCTVS_Options_IsIntraforest));
sTemp = pVs->get(GET_BSTR(DCTVS_AccountOptions_ReplaceExistingAccounts));
if (!UStrICmp(sTemp,GET_STRING(IDS_YES)))
bReplace = true;
sAdsPath = L"";
if ( pSource )
{
// Get the UPN from the source domain
hr = pSource->QueryInterface(IID_IADs, (void**) &pAdsSource);
}
if ( pAdsSource )
{
if ( SUCCEEDED(hr) )
{
hr = pAdsSource->GetEx(L"userPrincipalName", &var);
if (SUCCEEDED(hr) )
{
SAFEARRAY * pArray = V_ARRAY(&var);
hr = SafeArrayGetLBound(pArray, 1, &lb);
hr = SafeArrayGetUBound(pArray, 1, &ub);
hr = SafeArrayAccessData(pArray, (void HUGEP **) &pDt);
if ( SUCCEEDED(hr) )
{
// translate all the UPNs to the target domain.
for ( long x = lb; x <= ub; x++)
{
wcsncpy(sTempUPN, (WCHAR*) pDt[x].bstrVal, 5000);
sTempUPN[4999] = 0;
//Get the stuff before the LAST @ sign.
WCHAR * ndx = NULL;
WCHAR * tempNdx = sTempUPN;
do
{
tempNdx = wcschr(tempNdx + 1, L'@');
if ( tempNdx )
ndx = tempNdx;
} while (tempNdx);
if (ndx) *ndx = L'\0';
if ( sPref.length() )
sFull = sPref + _bstr_t(sTempUPN);
else if ( sSuff.length() )
sFull = _bstr_t(sTempUPN) + sSuff;
else
sFull = sTempUPN;
sTemp = sFull;
sUPN = sTemp + _bstr_t(L"@");
sUPN = sUPN + m_sUPNSuffix;
//store UPN name as it enters
sOldUPN = sUPN;
sUPNName = sUPN;
//
// If able to verify that UPN is unique or a unique UPN
// was generated then sUPN will contain the unique UPN
// otherwise sUPN will be an empty string.
//
GetUniqueUPN(sUPN, pVs, false, sAdsPath);
if (sUPN.length() > 0)
{
//see if the two UPN's differ. If they do, then we had a conflict
if (_wcsicmp((WCHAR*)sOldUPN, sUPN) != 0)
{
sUPNName = sUPN;
hr = ERROR_OBJECT_ALREADY_EXISTS;
bConflicted = true;
}
}
else
{
//
// Unable to verify if UPN was unique therefore set UPN to be an
// empty string which will cause the UPN attribute not to be set
// in the process object method. Also must increment the error count.
//
sUPNName = sUPN;
if (pStats != NULL)
{
pStats->errors.users++;
}
}
pDt[x] = _variant_t(sUPN);
}
SafeArrayUnaccessData(pArray);
}
}
else
{
sTemp = pVs->get(GET_BSTR(DCTVS_CopiedAccount_TargetSam));
sUPN = sTemp + _bstr_t(L"@");
sUPN = sUPN + m_sUPNSuffix;
//store UPN name as it enters
sOldUPN = sUPN;
sUPNName = sUPN;
//
// If able to verify that UPN is unique or a unique UPN
// was generated then sUPN will contain the unique UPN
// otherwise sUPN will be an empty string.
//
GetUniqueUPN(sUPN, pVs, false, sAdsPath);
if (sUPN.length() > 0)
{
//see if the two UPN's differ. If they do, then we had a conflict
if (_wcsicmp((WCHAR*)sOldUPN, sUPN) != 0)
{
sUPNName = sUPN;
hr = ERROR_OBJECT_ALREADY_EXISTS;
bConflicted = true;
}
}
else
{
//
// Unable to verify if UPN was unique therefore set UPN to be an
// empty string which will cause the UPN attribute not to be set
// in the process object method. Also must increment the error count.
//
sUPNName = sUPN;
if (pStats != NULL)
{
pStats->errors.users++;
}
}
}
}
}
else
{
sTemp = pVs->get(GET_BSTR(DCTVS_CopiedAccount_TargetSam));
sUPN = sTemp + _bstr_t(L"@");
sUPN = sUPN + m_sUPNSuffix;
//store UPN name as it enters
sOldUPN = sUPN;
sUPNName = sUPN;
//
// If able to verify that UPN is unique or a unique UPN
// was generated then sUPN will contain the unique UPN
// otherwise sUPN will be an empty string.
//
GetUniqueUPN(sUPN, pVs, false, sAdsPath);
if (sUPN.length() > 0)
{
//see if the two UPN's differ. If they do, then we had a conflict
if (_wcsicmp((WCHAR*)sOldUPN, sUPN) != 0)
{
sUPNName = sUPN;
hr = ERROR_OBJECT_ALREADY_EXISTS;
bConflicted = true;
}
}
else
{
//
// Unable to verify if UPN was unique therefore set UPN to be an
// empty string which will cause the UPN attribute not to be set
// in the process object method. Also must increment the error count.
//
sUPNName = sUPN;
if (pStats != NULL)
{
pStats->errors.users++;
}
}
}
if ( pAds ) pAds->Release();
if (pAdsSource) pAdsSource->Release();
UPNStruc.sName = sUPNName;
UPNStruc.sOldName = sOldUPN;
UPNStruc.bConflicted = bConflicted;
//insert UPN into Map
mUPNMap.insert(CUPNMap::value_type(sSAMName, UPNStruc));
return hr;
}
//---------------------------------------------------------------------------
// ProcessObject : This method updates the UPN property of the object. It
// first sees if a E-Mail is specified then it will set UPN
// to that otherwise it builds it from SAMAccountName and the
// Domain name
//---------------------------------------------------------------------------
STDMETHODIMP CUpdtUPN::ProcessObject(
IUnknown *pSource, //in- Pointer to the source AD object
IUnknown *pTarget, //in- Pointer to the target AD object
IUnknown *pMainSettings, //in- Varset filled with the settings supplied by user
IUnknown **ppPropsToSet, //in,out - Varset filled with Prop-Value pairs that will be set
// once all extension objects are executed.
EAMAccountStats* pStats
)
{
IVarSetPtr pVs = pMainSettings;
_bstr_t sTemp;
IADs * pAds = NULL;
_variant_t var;
HRESULT hr;
WCHAR fileName[MAX_PATH];
CUPNMap::iterator itUPNMap;
tstring sSam;
SUPNStruc UPNStruc;
bool bReplace = false;
_bstr_t sOldUPNSuffix;
// We need to process the user accounts only
sTemp = pVs->get(GET_BSTR(DCTVS_CopiedAccount_Type));
if ( _wcsicmp((WCHAR*)sTemp,L"user") && _wcsicmp((WCHAR*)sTemp,L"inetOrgPerson") ) return S_OK;
sTemp = pVs->get(GET_BSTR(DCTVS_AccountOptions_ReplaceExistingAccounts));
if (!UStrICmp(sTemp,GET_STRING(IDS_YES)))
bReplace = true;
//get the target SAM name
sSam = _bstr_t(pVs->get(GET_BSTR(DCTVS_CopiedAccount_SourceSam)));
// Get the Error log filename from the Varset
wcscpy(fileName, (WCHAR*)(pVs->get(GET_BSTR(DCTVS_Options_Logfile)).bstrVal));
// Open the error log
err.LogOpen(fileName, 1);
// And only need to process the accounts copied to Win2k domain.
if ( pTarget )
{
//Get the IADs pointer to manipulate properties
hr = pTarget->QueryInterface(IID_IADs, (void**) &pAds);
if (SUCCEEDED(hr))
{
//get the UPN name for this user from the list
itUPNMap = mUPNMap.find(sSam);
if (itUPNMap != mUPNMap.end())
UPNStruc = itUPNMap->second;
if (!UPNStruc.sName.empty())
{
bool bSame = false;
//if replace mode, don't set UPN if same object we are replacing and
//if not the same object, get it's current UPN Suffix
if (bReplace)
{
hr = pAds->Get(L"userPrincipalName", &var);
if (SUCCEEDED(hr))
{
//if replacing the object whose UPN conflicted, don't change it
if (!UPNStruc.sOldName.compare(var.bstrVal))
bSame = true;
else //else, get the object's current UPN suffix for re-use
sOldUPNSuffix = GetUPNSuffix(var.bstrVal);
}
}
if (!bSame)
{
var = UPNStruc.sName.c_str();
//if replacing an existing object, use it's old UPN suffix
if ((bReplace) && (sOldUPNSuffix.length() != 0))
{
//change the suffix on the old name, since it may longer conflict
_bstr_t sUPN = ChangeUPNSuffix(UPNStruc.sOldName.c_str(), sOldUPNSuffix);
//if changed, make sure we don't still have a UPN conflict and save the
//new UPN for setting
if (sUPN.length() != 0)
{
_bstr_t sTempUPN = sUPN;
//get unigue UPN on target, now that we could conflict
GetUniqueUPN(sUPN, pVs, true, _bstr_t(L""));
if (sUPN.length() > 0)
{
//if changed, set conflicted flag and names for error message
if (sUPN != sTempUPN)
{
UPNStruc.sName = sUPN;
UPNStruc.sOldName = sTempUPN;
UPNStruc.bConflicted = true;
}
else
UPNStruc.bConflicted = false;
}
var = sUPN;
}
}
//
// If unable to determine if UPN is unique then GetUniqueUPN will
// set the UPN to an empty string. If this is the case then don't
// set the UPN attribute.
//
if ((V_VT(&var) == VT_BSTR) && (SysStringLen(V_BSTR(&var)) > 0))
{
hr = pAds->Put(L"userPrincipalName", var);
if (SUCCEEDED(hr))
{
hr = pAds->SetInfo();
if (SUCCEEDED(hr))
{
// If we changed the UPN Name due to conflict, we need to log a
//message indicating the fact that we changed it.
if (UPNStruc.bConflicted)
err.MsgWrite(1, DCT_MSG_CREATE_FAILED_UPN_CONF_SS,
UPNStruc.sOldName.c_str(), UPNStruc.sName.c_str());
}
}
}
else
{
if (pStats != NULL)
{
pStats->errors.users++;
}
}
}
}
}
}
if ( pAds ) pAds->Release();
return hr;
}
//---------------------------------------------------------------------------
// ProcessUndo : We are not going to undo this.
//---------------------------------------------------------------------------
STDMETHODIMP CUpdtUPN::ProcessUndo(
IUnknown *pSource, //in- Pointer to the source AD object
IUnknown *pTarget, //in- Pointer to the target AD object
IUnknown *pMainSettings, //in- Varset filled with the settings supplied by user
IUnknown **ppPropsToSet, //in,out - Varset filled with Prop-Value pairs that will be set
// once all extension objects are executed.
EAMAccountStats* pStats
)
{
IVarSetPtr pVs = pMainSettings;
_bstr_t sTemp, sSUPN;
IADs * pAds = NULL;
_variant_t var;
HRESULT hr = S_OK;
_bstr_t sAdsPath = L"";
_bstr_t sTempUPN;
// We need to process the user accounts only
sTemp = pVs->get(GET_BSTR(DCTVS_CopiedAccount_Type));
if ( _wcsicmp((WCHAR*)sTemp,L"user") || _wcsicmp((WCHAR*)sTemp,L"inetOrgPerson") ) return S_OK;
// get the original source account's UPN
sSUPN = pVs->get(GET_BSTR(DCTVS_CopiedAccount_SourceUPN));
if (sSUPN.length())
{
sTempUPN = sSUPN;
GetUniqueUPN(sTempUPN, pVs, true, sAdsPath);
int len;
WCHAR * ndx, * tempNdx = (WCHAR*)sTempUPN;
do
{
tempNdx = wcschr(tempNdx + 1, L'@');
if ( tempNdx )
ndx = tempNdx;
} while (tempNdx);
if (ndx) len = ndx - sTempUPN;
if (_wcsnicmp(sTempUPN, sSUPN, len) != 0)
return S_OK;
// And only need to process the accounts copied to Win2k domain.
if ( pTarget )
{
//Get the IADs pointer to manipulate properties
hr = pTarget->QueryInterface(IID_IADs, (void**) &pAds);
if ( SUCCEEDED(hr) )
{
var = sSUPN;
hr = pAds->Put(L"userPrincipalName", var);
if (SUCCEEDED(hr))
{
hr = pAds->SetInfo();
}
}
}
if ( pAds ) pAds->Release();
}
return hr;
}
//---------------------------------------------------------------------------
// GetUniqueUPN : This function checks if the UPN is unique if not then
// appends a number starting with 0 and retries till a unique
// UPN is found.
//---------------------------------------------------------------------------
void CUpdtUPN::GetUniqueUPN(_bstr_t &sUPN, IVarSetPtr pVs, bool bUsingSamName, _bstr_t sAdsPath)
{
// Here are the steps that we follow to get the unique UPN name
// 1. Check if the current name is unique. If it is then return that.
// 2. Append collision prefix and suffix if the sam account name has changed due to pref/suffix.
// 3. Add a numeric suffix to the UPN and repeat till a unique UPN is found.
c_array<WCHAR> sTempUPN(5000);
c_array<WCHAR> sPath(5000);
HRESULT hr = E_FAIL;
LPWSTR pCols[] = { L"distinguishedName", L"sAMAccountName" };
BSTR * pData = NULL;
SAFEARRAY * pSaCols = NULL;
SAFEARRAYBOUND bd = { 2, 0 };
_bstr_t sSrcDomain = pVs->get(GET_BSTR(DCTVS_Options_SourceDomainDns));
_bstr_t sTgtDomain = pVs->get(GET_BSTR(DCTVS_Options_TargetDomainDns));
INetObjEnumeratorPtr pQuery(__uuidof(NetObjEnumerator));
IEnumVARIANTPtr pEnum;
DWORD fetched = 0;
_variant_t var;
bool bCollPrefSufProcessed = false;
_bstr_t sSourceSam = pVs->get(GET_BSTR(DCTVS_CopiedAccount_SourceSam));
_bstr_t sTargetSam = pVs->get(GET_BSTR(DCTVS_CopiedAccount_TargetSam));
_bstr_t sPrefix = pVs->get(GET_BSTR(DCTVS_AccountOptions_Prefix));
_bstr_t sSuffix = pVs->get(GET_BSTR(DCTVS_AccountOptions_Suffix));
_bstr_t sPref = pVs->get(GET_BSTR(DCTVS_Options_Prefix));
_bstr_t sSuff = pVs->get(GET_BSTR(DCTVS_Options_Suffix));
int offset = 0;
c_array<WCHAR> sTemp(5000);
SAFEARRAY * psaPath = NULL;
_bstr_t strDn;
_bstr_t strSam;
VARIANT * pVar;
bool bReplace = false;
WCHAR sTempSAM[MAX_PATH];
_bstr_t sNewSAM;
_bstr_t sUPNSuffix;
_bstr_t sUPNPrefix;
_bstr_t sReplace = pVs->get(GET_BSTR(DCTVS_AccountOptions_ReplaceExistingAccounts));
if (!UStrICmp(sReplace,GET_STRING(IDS_YES)))
bReplace = true;
wcscpy(sTempSAM, (WCHAR*)sSourceSam);
StripSamName(sTempSAM);
if ( sPref.length() )
sNewSAM = sPref + _bstr_t(sTempSAM);
else if ( sSuff.length() )
sNewSAM = _bstr_t(sTempSAM) + sSuff;
else
sNewSAM = sTempSAM;
wcscpy(sTempUPN, (WCHAR*) sUPN);
//Get the stuff before the LAST @ sign.
WCHAR * ndx = NULL;
WCHAR * tempNdx = sTempUPN;
do
{
tempNdx = wcschr(tempNdx + 1, L'@');
if ( tempNdx )
ndx = tempNdx;
} while (tempNdx);
//
// If UPN prefix and suffix terminate prefix portion
// otherwise return empty UPN as an internal error
// has occurred so do not generate UPN for this user.
//
if (ndx)
{
*ndx = L'\0';
}
else
{
err.SysMsgWrite(ErrE, E_FAIL, DCT_MSG_UNABLE_TO_GENERATE_UNIQUE_UPN_S, (PCWSTR)sUPN);
sUPN = L"";
return;
}
sUPNSuffix = ndx+1;
sUPNPrefix = sTempUPN;
//
// User principal names (UPN) must be unique across the forest therefore
// the name of a global catalog server in the target forest must be
// obtained so that the entire forest may be queried for user principal names.
//
// If unable to obtain name of a global catalog server then log error message
// and set UPN to an empty string which will cause the UPN attribute not to
// be set.
//
_bstr_t strGlobalCatalogServer;
DWORD dwError = GetGlobalCatalogServer5(sTgtDomain, strGlobalCatalogServer);
if ((dwError == ERROR_SUCCESS) && (strGlobalCatalogServer.length() > 0))
{
wsprintf(sPath, L"GC://%s", (PCWSTR)strGlobalCatalogServer);
}
else
{
err.SysMsgWrite(ErrE, HRESULT_FROM_WIN32(dwError), DCT_MSG_UNABLE_TO_QUERY_UPN_IN_GLOBAL_CATALOG_SERVER_S, (PCWSTR)sUPN);
sUPN = L"";
return;
}
// setup the columns that we want the query to return to us.
pSaCols = SafeArrayCreate(VT_BSTR, 1, &bd);
if (pSaCols)
{
hr = SafeArrayAccessData(pSaCols, (void HUGEP **) &pData);
if ( SUCCEEDED(hr) )
{
pData[0] = SysAllocString(pCols[0]);
pData[1] = SysAllocString(pCols[1]);
if (!pData[0] || !pData[1])
{
SafeArrayUnaccessData(pSaCols);
sUPN = L"";
return;
}
}
hr = SafeArrayUnaccessData(pSaCols);
}
if ( SUCCEEDED(hr) )
{
// First we need to set up a query to find the UPN
wcscpy(sTempUPN, (WCHAR*)sUPN);
do
{
_bstr_t sQuery = L"(userPrincipalName=";
sQuery += GetEscapedFilterValue(sTempUPN).c_str();
sQuery += L")";
hr = pQuery->raw_SetQuery(sPath, sTgtDomain, sQuery, ADS_SCOPE_SUBTREE, FALSE);
if ( SUCCEEDED(hr) )
hr = pQuery->raw_SetColumns(pSaCols);
if ( SUCCEEDED(hr) )
hr = pQuery->raw_Execute(&pEnum);
if ( SUCCEEDED(hr) )
{
hr = pEnum->Next(1, &var, &fetched);
while ( hr == S_OK )
{
if ( var.vt & VT_ARRAY )
{
psaPath = var.parray;
hr = SafeArrayAccessData(psaPath, (void HUGEP**) &pVar);
if ( SUCCEEDED(hr) )
{
//
// Retrieve distinguishedName and sAMAccountName attributes.
//
if (V_VT(&pVar[0]) == VT_BSTR)
{
strDn = V_BSTR(&pVar[0]);
}
if (V_VT(&pVar[1]) == VT_BSTR)
{
strSam = V_BSTR(&pVar[1]);
}
}
SafeArrayUnaccessData(psaPath);
bool bContinue = false;
//
// If unable to query attributes generate error message and set UPN to empty string.
//
if (!strDn || !strSam)
{
err.SysMsgWrite(ErrE, E_FAIL, DCT_MSG_UNABLE_TO_QUERY_UPN_IN_GLOBAL_CATALOG_SERVER_S, (PCWSTR)sUPN);
wcscpy(sTempUPN, L"");
hr = S_FALSE;
}
else
{
//
// If the object found is the source domain object continue.
// This will be the case during intra-forest migrations.
//
if (sSrcDomain.length() && (_wcsicmp((wchar_t*)strSam, (wchar_t*)sSourceSam) == 0))
{
_bstr_t strDomain = GetDomainDNSFromPath(strDn);
if (_wcsicmp((wchar_t*)strDomain, (wchar_t*)sSrcDomain) == 0)
{
bContinue = true;
}
}
//
// If the object found is the target domain object to be replaced continue.
//
if (!bContinue && bReplace && (_wcsicmp((wchar_t*)strSam, (wchar_t*)sNewSAM) == 0))
{
_bstr_t strDomain = GetDomainDNSFromPath(strDn);
if (_wcsicmp((wchar_t*)strDomain, (wchar_t*)sTgtDomain) == 0)
{
bContinue = true;
}
}
}
// If the account found is the same as the account being processed then we
// need to see if any other accounts have this UPN. if they do then we need
// to change it other wise we do not need to process this any further.
if (bContinue)
{
var.Clear();
hr = pEnum->Next(1, &var, &fetched);
}
else
{
break;
}
}
}
if ( hr == S_OK )
{
// If we are here that means we have a collision So we need to update the UPN and try again
// See if we have processed the Prefix/Suffix
if ( !bCollPrefSufProcessed )
{
// See if we renamed the samAccountName with the prefix/suffix. If we are already using
// sam name then there is no need to add the prefix/suffix.
if ( !bUsingSamName && RenamedWithPrefixSuffix(sSourceSam, sTargetSam, sPrefix, sSuffix))
{
// Since we renamed the sam names we can rename the UPN
if ( sPrefix.length() )
wsprintf(sTempUPN, L"%s%s", (WCHAR*)sPrefix, (WCHAR*)sUPNPrefix);
if ( sSuffix.length() )
wsprintf(sTempUPN, L"%s%s",(WCHAR*)sUPNPrefix, (WCHAR*)sSuffix);
sUPNPrefix = sTempUPN; // we want to apply the prefix/suffix in any case.
}
else
{
// just add a number to the end of the name.
wsprintf(sTempUPN, L"%s%d", (WCHAR*)sUPNPrefix, offset);
offset++;
}
bCollPrefSufProcessed = true;
}
else
{
//
// Attempt sequence numbers only to some reasonable upper limit.
//
if (offset <= SEQUENCE_UPPER_BOUND)
{
// we went through prefix/suffix and still found a collision so we need to go by the count now.
wsprintf(sTempUPN, L"%s%d", (WCHAR*)sUPNPrefix, offset);
offset++;
}
else
{
//
// If unable to find a unique UPN then must return empty UPN.
//
err.MsgWrite(ErrE, DCT_MSG_UNABLE_TO_GENERATE_UNIQUE_UPN_S, (PCWSTR)sUPN);
wcscpy(sTempUPN, L"");
break;
}
}
if (wcslen(sTempUPN) > 0)
{
wcscpy(sTemp, sTempUPN);
wsprintf(sTempUPN, L"%s@%s", (WCHAR*)sTemp, (WCHAR*)sUPNSuffix);
}
}
var.Clear();
}
else
{
err.SysMsgWrite(ErrE, hr, DCT_MSG_UNABLE_TO_QUERY_UPN_IN_GLOBAL_CATALOG_SERVER_S, (PCWSTR)sUPN);
}
} while ( hr == S_OK );
SafeArrayDestroy(pSaCols);
}
if (FAILED(hr))
{
sUPN = L"";
}
else
{
sUPN = sTempUPN;
}
}
//---------------------------------------------------------------------------
// RenamedWithPrefixSuffix : Checks to see if the Target sam name
// was renamed with a prefix/suffix.
//---------------------------------------------------------------------------
bool CUpdtUPN::RenamedWithPrefixSuffix(_bstr_t sSourceSam, _bstr_t sTargetSam, _bstr_t sPrefix, _bstr_t sSuffix)
{
bool retVal = false;
if ( sSourceSam != sTargetSam )
{
if ( sPrefix.length() )
{
if ( !wcsncmp((WCHAR*) sTargetSam, (WCHAR*) sPrefix, sPrefix.length()) )
retVal = true;
}
if ( sSuffix.length() )
{
if ( !wcscmp((WCHAR*) sTargetSam + (sTargetSam.length() - sSuffix.length()), (WCHAR*) sSuffix ) )
retVal = true;
}
}
return retVal;
}
/*********************************************************************
* *
* Written by: Paul Thompson *
* Date: 24 MAR 2001 *
* *
* This function is responsible for retrieving the default UPN *
* suffix to be used in making UPN names. The suffix will be stored *
* in a class member variable. *
* First, using the given target OU path, see if the target OU *
* has any UPN suffixes defined for it. If so, return store the *
* last one enumerated. Otherwise, see if any UPN suffixes have *
* been defined on the configuration's partition. If so, store the *
* last one enumerated. If no success yet, use the forest root's *
* domain DNS name. *
* *
*********************************************************************/
//BEGIN GetDefaultUPNSuffix
bool CUpdtUPN::GetDefaultUPNSuffix(_bstr_t sDomainDNS, _bstr_t sTargetOU)
{
/* local variables */
IADs * pDSE = NULL;
IADs * pCont = NULL;
WCHAR sRoot[1000];
HRESULT hr = S_OK;
_variant_t var;
_variant_t HUGEP * pVar;
int nLast;
/* function body */
//check incoming parameters
if ((sDomainDNS.length() == 0) || (sTargetOU.length() == 0))
return false;
/* first see if the target OU has UPN suffixes defined */
//get a pointer to the target OU
hr = ADsGetObject(sTargetOU,IID_IADs,(void**)&pCont);
if ( SUCCEEDED(hr) )
{
//get any UPN suffixes defined
hr = pCont->Get( L"uPNSuffixes", &var);
if ( SUCCEEDED(hr) )
{
//if one, store it and return
if ( var.vt == VT_BSTR )
{
m_sUPNSuffix = var.bstrVal; //store the suffix
pCont->Release();
return true;
}
//else if nore than one, get the first one, store it, and return
else if ( var.vt & VT_ARRAY )
{
SAFEARRAY * multiVals = var.parray;
SafeArrayAccessData(multiVals, (void HUGEP **) &pVar);
nLast = multiVals->rgsabound->cElements - 1;
m_sUPNSuffix = _bstr_t(V_BSTR(&pVar[nLast]));
SafeArrayUnaccessData(multiVals);
pCont->Release();
return true;
}
}//end if suffixes defined on the partition
pCont->Release();
pCont = NULL;
}//if got partition
/* next try the UPN suffixes on the partition container or the root
domain's DNS name */
//get the root DSE container
_snwprintf(sRoot, sizeof(sRoot) / sizeof(sRoot[0]), L"LDAP://%s/RootDSE", (WCHAR*)sDomainDNS);
sRoot[sizeof(sRoot) / sizeof(sRoot[0]) - 1] = L'\0';
hr = ADsGetObject(sRoot,IID_IADs,(void**)&pDSE);
if ( SUCCEEDED(hr) )
{
//get the suffixes listed on the configuration partition
hr = pDSE->Get(L"configurationNamingContext",&var);
if ( SUCCEEDED(hr) )
{
swprintf(sRoot,L"LDAP://%ls/CN=Partitions,%ls", (WCHAR*)sDomainDNS, var.bstrVal);
hr = ADsGetObject(sRoot,IID_IADs,(void**)&pCont);
if ( SUCCEEDED(hr) )
{
//get any UPN suffixes defined
hr = pCont->Get( L"uPNSuffixes", &var);
if ( SUCCEEDED(hr) )
{
//if one, store it and return
if ( var.vt == VT_BSTR )
{
m_sUPNSuffix = var.bstrVal; //store the suffix
pDSE->Release();
pCont->Release();
return true;
}
//else if nore than one, get the first one, store it, and return
else if ( var.vt & VT_ARRAY )
{
SAFEARRAY * multiVals = var.parray;
SafeArrayAccessData(multiVals, (void HUGEP **) &pVar);
nLast = multiVals->rgsabound->cElements - 1;
m_sUPNSuffix = _bstr_t(V_BSTR(&pVar[nLast]));
SafeArrayUnaccessData(multiVals);
pDSE->Release();
pCont->Release();
return true;
}
}//end if suffixes defined on the partition
pCont->Release();
pCont = NULL;
}//if got partition
}//if got config naming context
//since no UPN suffixes defined on the partition, try the root domain's
//DNS name
hr = pDSE->Get(L"RootDomainNamingContext",&var);
if ( SUCCEEDED(hr) )
{
//convert the DN of the root domain to a DNS name, store it, and return
m_sUPNSuffix = GetDomainDNSFromPath(_bstr_t(var.bstrVal));
pDSE->Release();
return true;
}
pDSE->Release();
pDSE = NULL;
}//if got rootDSE
return false;
}
//END GetDefaultUPNSuffix
/*********************************************************************
* *
* Written by: Paul Thompson *
* Date: 26 MAR 2001 *
* *
* This function is responsible for extracting the UPN Suffix *
* from a given UPN name and returning that suffix. *
* *
*********************************************************************/
//BEGIN GetUPNSuffix
_bstr_t CUpdtUPN::GetUPNSuffix(_bstr_t sUPNName)
{
/* local variables */
_bstr_t sUPNSuffix = L"";
WCHAR * pTemp;
/* function body */
//check incoming parameters
if (sUPNName.length() == 0)
return sUPNSuffix;
//find the last '@'
pTemp = wcsrchr((WCHAR*)sUPNName, L'@');
//if found, copy the suffix to the return variable
if (pTemp)
sUPNSuffix = pTemp+1;
return sUPNSuffix;
}
//END GetUPNSuffix
/*********************************************************************
* *
* Written by: Paul Thompson *
* Date: 26 MAR 2001 *
* *
* This function is responsible for replacing the UPN Suffix *
* on a given UPN name with the given suffix and returning the new *
* UPN name. *
* *
*********************************************************************/
//BEGIN ChangeUPNSuffix
_bstr_t CUpdtUPN::ChangeUPNSuffix(_bstr_t sUPNName, _bstr_t sNewSuffix)
{
/* local variables */
_bstr_t sNewUPN = L"";
WCHAR * pTemp;
/* function body */
//check incoming parameters
if (sUPNName.length() == 0)
return sNewUPN;
//create a temporary buffer to hold the UPN Name
WCHAR* sUPN = new WCHAR[sUPNName.length() + 1];
if (!sUPN)
return sNewUPN;
//copy the UPN to this buffer
wcscpy(sUPN, sUPNName);
//find the last '@'
pTemp = wcsrchr(sUPN, L'@');
//if found, make the new UPN with the old Prefix and given suffix
if (pTemp)
{
//end the string after the '@'
*(pTemp+1) = L'\0';
//copy the Prefix plus the new Suffix to the new UPN name
sNewUPN = sUPN + sNewSuffix;
}
//delete the prefix string
delete [] sUPN;
return sNewUPN;
}
//END ChangeUPNSuffix