1644 lines
50 KiB
C++
1644 lines
50 KiB
C++
|
#include "stdafx.h"
|
||
|
|
||
|
#include "exchange.hpp"
|
||
|
|
||
|
#include "common.hpp"
|
||
|
#include "err.hpp"
|
||
|
#include "ErrDct.hpp"
|
||
|
#include "UString.hpp"
|
||
|
|
||
|
#include "sidcache.hpp"
|
||
|
#include "sd.hpp"
|
||
|
#include "SecObj.hpp"
|
||
|
#include "MAPIProf.hpp"
|
||
|
#include "exldap.h"
|
||
|
|
||
|
#include "Mcs.h"
|
||
|
|
||
|
extern TErrorDct err;
|
||
|
|
||
|
#define NOT_PT_ERROR(x) ( PROP_TYPE(x.ulPropTag) != PT_ERROR )
|
||
|
|
||
|
#define LDAP_PortNumber_DN_Part L"/cn=Protocols/cn=LDAP"
|
||
|
#define ATT_OBJ_CLASS L"Obj-Class"
|
||
|
#define ATT_DIST_NAME L"Obj-Dist-Name"
|
||
|
#define ATT_LDAP_PORT L"Port-Number"
|
||
|
#define LDAP_USE_SITE_VALUES L"Use-Site-Values"
|
||
|
|
||
|
|
||
|
// Stuff related to dynamic loading of DAPI.DLL
|
||
|
|
||
|
HINSTANCE hDapi = NULL;
|
||
|
LPDAPISTART pDAPIStart = NULL;
|
||
|
LPDAPIEND pDAPIEnd = NULL;
|
||
|
LPDAPIREAD pDAPIRead = NULL;
|
||
|
LPDAPIWRITE pDAPIWrite = NULL;
|
||
|
LPDAPIFREEMEMORY pDAPIFreeMemory = NULL;
|
||
|
LPBATCHEXPORT pBatchExport = NULL;
|
||
|
|
||
|
BOOL LoadDAPI()
|
||
|
{
|
||
|
BOOL success = TRUE;
|
||
|
|
||
|
if ( ! hDapi )
|
||
|
{
|
||
|
success = FALSE;
|
||
|
hDapi = LoadLibrary(_T("DAPI.DLL"));
|
||
|
if ( hDapi )
|
||
|
{
|
||
|
do { // once
|
||
|
pDAPIStart = (LPDAPISTART)GetProcAddress(hDapi,"DAPIStartW@8");
|
||
|
if ( ! pDAPIStart )
|
||
|
{
|
||
|
err.MsgWrite(ErrE,DCT_MSG_DAPI_ENTRY_POINT_NOT_FOUND_S,L"DAPIStart");
|
||
|
break;
|
||
|
}
|
||
|
pDAPIEnd = (LPDAPIEND)GetProcAddress(hDapi,"DAPIEnd@4");
|
||
|
if ( ! pDAPIEnd )
|
||
|
{
|
||
|
err.MsgWrite(ErrE,DCT_MSG_DAPI_ENTRY_POINT_NOT_FOUND_S,L"DAPIEnd");
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
pDAPIRead = (LPDAPIREAD)GetProcAddress(hDapi,"DAPIReadW@24");
|
||
|
if ( ! pDAPIRead )
|
||
|
{
|
||
|
err.MsgWrite(ErrE,DCT_MSG_DAPI_ENTRY_POINT_NOT_FOUND_S,L"DAPIRead");
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
pDAPIWrite = (LPDAPIWRITE)GetProcAddress(hDapi,"DAPIWriteW@28");
|
||
|
if ( ! pDAPIWrite )
|
||
|
{
|
||
|
err.MsgWrite(ErrE,DCT_MSG_DAPI_ENTRY_POINT_NOT_FOUND_S,L"DAPIWrite");
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
pDAPIFreeMemory = (LPDAPIFREEMEMORY)GetProcAddress(hDapi,"DAPIFreeMemory@4");
|
||
|
if ( ! pDAPIFreeMemory )
|
||
|
{
|
||
|
err.MsgWrite(ErrE,DCT_MSG_DAPI_ENTRY_POINT_NOT_FOUND_S,L"DAPIFreeMemory");
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
pBatchExport = (LPBATCHEXPORT)GetProcAddress(hDapi,"BatchExportA@4");
|
||
|
if ( ! pBatchExport )
|
||
|
{
|
||
|
err.MsgWrite(ErrE,DCT_MSG_DAPI_ENTRY_POINT_NOT_FOUND_S,L"BatchExport");
|
||
|
break;
|
||
|
}
|
||
|
success = TRUE;
|
||
|
} while (false);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
err.MsgWrite(ErrE,DCT_MSG_DAPI_LOAD_LIBRARY_FAILED);
|
||
|
}
|
||
|
}
|
||
|
if ( ! success )
|
||
|
{
|
||
|
ReleaseDAPI();
|
||
|
}
|
||
|
return success;
|
||
|
}
|
||
|
|
||
|
void ReleaseDAPI()
|
||
|
{
|
||
|
if ( hDapi )
|
||
|
{
|
||
|
FreeLibrary(hDapi);
|
||
|
hDapi = NULL;
|
||
|
pDAPIStart = NULL;
|
||
|
pDAPIEnd = NULL;
|
||
|
pDAPIRead = NULL;
|
||
|
pDAPIWrite = NULL;
|
||
|
pDAPIFreeMemory = NULL;
|
||
|
pBatchExport = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
TGlobalDirectory::TGlobalDirectory()
|
||
|
{
|
||
|
m_stat = NULL;
|
||
|
m_bUseDefaultMapiProfile = FALSE; // Whether to use MAPI profile listed in Registry.
|
||
|
m_bPromptForMapiProfile = TRUE; // Whether to prompt for MAPI profile.
|
||
|
m_pszMapiProfile = NULL; // MAPI profile to use.
|
||
|
|
||
|
// MAPI
|
||
|
m_bMapiInitialized = FALSE; // TRUE if initialization was successful.
|
||
|
m_pMapiSession = NULL; // MAPI session handle.
|
||
|
|
||
|
m_pAdrBook = NULL; // The master AB.
|
||
|
|
||
|
m_pGlobalList = NULL;
|
||
|
m_pGlobalTable = NULL;
|
||
|
m_pGlobalPropertyTags = NULL;
|
||
|
m_pGlobalRows = NULL;
|
||
|
m_pRootRows = NULL;
|
||
|
m_pContainer = NULL;
|
||
|
m_pContainerTable = NULL;
|
||
|
}
|
||
|
|
||
|
TGlobalDirectory::~TGlobalDirectory()
|
||
|
{
|
||
|
delete [] m_pszMapiProfile;
|
||
|
|
||
|
CloseGlobalList();
|
||
|
EndMapiSession();
|
||
|
}
|
||
|
|
||
|
|
||
|
LPTSTR TGlobalDirectory::GetMapiProfile() const
|
||
|
{
|
||
|
return m_pszMapiProfile;
|
||
|
}
|
||
|
|
||
|
BOOL TGlobalDirectory::DoUseDefaultMapiProfile() const
|
||
|
{
|
||
|
return m_bUseDefaultMapiProfile;
|
||
|
}
|
||
|
|
||
|
BOOL TGlobalDirectory::DoPromptForMapiProfile() const
|
||
|
{
|
||
|
return m_bPromptForMapiProfile;
|
||
|
}
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
// Log a MAPI warning or error.
|
||
|
void
|
||
|
TGlobalDirectory::LogMapiError(
|
||
|
int iSeverity, // in - Severity (i.e. ErrW, ErrE, etc)
|
||
|
LPCTSTR pszEntryPoint, // in - API that generated the error
|
||
|
HRESULT hr // in - error code
|
||
|
)
|
||
|
{
|
||
|
if (hr != 0)
|
||
|
err.MsgWrite(iSeverity,DCT_MSG_GENERIC_HRESULT_SD, pszEntryPoint, hr);
|
||
|
else
|
||
|
err.MsgWrite(iSeverity,DCT_MSG_GENERIC_S, pszEntryPoint);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
TGlobalDirectory::LogDapiError(
|
||
|
int iSeverity, // in - severity of error (i.e. ErrW, ErrE, etc.)
|
||
|
LPCTSTR pszUserMessage, // in - message describing the action that failed
|
||
|
DAPI_EVENT * pResult // in - DAPIEvent error structure returned from Exchange
|
||
|
)
|
||
|
{
|
||
|
WCHAR strMsg[1000];
|
||
|
DWORD dimMsg = DIM(strMsg);
|
||
|
DWORD lenMsg;
|
||
|
DAPI_EVENT * pEvt;
|
||
|
WCHAR msg[2000];
|
||
|
|
||
|
if ( !pResult )
|
||
|
return;
|
||
|
|
||
|
safecopy(msg,pszUserMessage);
|
||
|
|
||
|
for ( pEvt = pResult ; pEvt ; pEvt = pEvt->pNextEvent )
|
||
|
{
|
||
|
strMsg[0] = 0;
|
||
|
lenMsg = FormatMessageW(
|
||
|
FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
||
|
pEvt->hinstDAPI,
|
||
|
pEvt->dwDAPIError,
|
||
|
0,
|
||
|
strMsg,
|
||
|
dimMsg,
|
||
|
(va_list*)pEvt->rgpszSubst
|
||
|
);
|
||
|
UStrCpy(msg+UStrLen(msg),strMsg,DIM(msg)-UStrLen(msg));
|
||
|
}
|
||
|
err.MsgWrite(iSeverity,DCT_MSG_GENERIC_S,&*msg);
|
||
|
}
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
// Start a MAPI session.
|
||
|
// Tries to use the profile specified on the command line.
|
||
|
BOOL TGlobalDirectory::StartMapiSession()
|
||
|
{
|
||
|
//ASSERT(m_bMapiInitialized == FALSE);
|
||
|
MCSASSERT(m_pMapiSession == NULL);
|
||
|
|
||
|
HRESULT hr;
|
||
|
if ( !m_bMapiInitialized )
|
||
|
{
|
||
|
hr = (*pMAPIInitialize)(NULL);
|
||
|
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
LogMapiError(ErrE, TEXT("MAPIInitialize"), hr);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
m_bMapiInitialized = TRUE;
|
||
|
}
|
||
|
FLAGS fLogonOptions = MAPI_NEW_SESSION | MAPI_NO_MAIL;
|
||
|
LPTSTR pszMapiProfile;
|
||
|
|
||
|
if (DoUseDefaultMapiProfile()) {
|
||
|
fLogonOptions |= MAPI_USE_DEFAULT;
|
||
|
pszMapiProfile = NULL;
|
||
|
}
|
||
|
else if (DoPromptForMapiProfile()) {
|
||
|
fLogonOptions |= MAPI_LOGON_UI | MAPI_EXPLICIT_PROFILE;
|
||
|
pszMapiProfile = NULL;
|
||
|
}
|
||
|
else {
|
||
|
fLogonOptions |= MAPI_EXPLICIT_PROFILE ;
|
||
|
pszMapiProfile = GetMapiProfile();
|
||
|
}
|
||
|
|
||
|
hr = (*pMAPILogonEx)(0, pszMapiProfile, NULL, fLogonOptions | MAPI_UNICODE, &m_pMapiSession);
|
||
|
if (FAILED(hr)) {
|
||
|
switch (hr) {
|
||
|
case MAPI_E_USER_CANCEL:
|
||
|
break;
|
||
|
default:
|
||
|
err.SysMsgWrite(ErrE,hr,DCT_MSG_NO_MAPI_SESSION_D,hr);
|
||
|
break;
|
||
|
}
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
m_bLoggedFailedClose = FALSE;
|
||
|
return TRUE;
|
||
|
} /* TGlobalDirectory::StartMapiSession() */
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
// Terminate a MAPI session.
|
||
|
// No-op if there's not one open.
|
||
|
void TGlobalDirectory::EndMapiSession()
|
||
|
{
|
||
|
if (m_pMapiSession) {
|
||
|
HRESULT hr = m_pMapiSession->Logoff(0, 0, 0);
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
m_pMapiSession = NULL;
|
||
|
else if (!m_bLoggedFailedClose) {
|
||
|
err.SysMsgWrite(ErrW,hr,DCT_MSG_MAPI_LOGOFF_FAILED_D,hr);
|
||
|
}
|
||
|
|
||
|
// NOTE: If this fails once, it may fail twice.
|
||
|
// The second failure will come on the dtor.
|
||
|
// This is on purpose. It's a retry. But we report it only once.
|
||
|
}
|
||
|
|
||
|
if (m_bMapiInitialized)
|
||
|
{
|
||
|
(*pMAPIUninitialize)();
|
||
|
m_bMapiInitialized = FALSE;
|
||
|
}
|
||
|
} /* TGlobalDirectory::EndMapiSession() */
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
// Open the Address Book and get an interface to the Global List.
|
||
|
// The Global List contains the Distribution Lists.
|
||
|
BOOL TGlobalDirectory::OpenGlobalList()
|
||
|
{
|
||
|
// Open the Address Book.
|
||
|
if ( ! m_pMapiSession )
|
||
|
return FALSE;
|
||
|
|
||
|
HRESULT hr = m_pMapiSession->OpenAddressBook(0, NULL, AB_NO_DIALOG , &m_pAdrBook);
|
||
|
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
err.SysMsgWrite(ErrE,hr,DCT_MSG_NO_ADDRBOOK_D,hr);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// Get the GAL entry ID.
|
||
|
ULONG cbEntryId;
|
||
|
LPENTRYID pEntryId;
|
||
|
|
||
|
hr = HrFindExchangeGlobalAddressList(m_pAdrBook, &cbEntryId, &pEntryId);
|
||
|
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
err.SysMsgWrite(ErrE,hr,DCT_MSG_FAILED_TO_OPEN_GAL_D,hr);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// Load the GAL from the AB.
|
||
|
ULONG ulObjType;
|
||
|
hr = m_pAdrBook->OpenEntry(cbEntryId, pEntryId, NULL, 0, &ulObjType, (LPUNKNOWN*)&m_pGlobalList);
|
||
|
|
||
|
(*pMAPIFreeBuffer)(pEntryId);
|
||
|
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
err.SysMsgWrite(ErrE,hr,DCT_MSG_RETRIEVE_GAL_FAILED_D,hr);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// Get a list of the contents of the Global List.
|
||
|
hr = m_pGlobalList->GetContentsTable(0, &m_pGlobalTable);
|
||
|
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
err.SysMsgWrite(ErrE,hr,DCT_MSG_RETRIEVE_GAL_FAILED_D,hr);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
} /* TEaSyncCommand::OpenGlobalList() */
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
// Release the interfaces to the Global List and the Address Book.
|
||
|
void TGlobalDirectory::CloseGlobalList()
|
||
|
{
|
||
|
if (m_pGlobalTable != NULL)
|
||
|
{
|
||
|
m_pGlobalTable->Release();
|
||
|
m_pGlobalTable = NULL;
|
||
|
}
|
||
|
|
||
|
if (m_pGlobalList != NULL)
|
||
|
{
|
||
|
m_pGlobalList->Release();
|
||
|
m_pGlobalList = NULL;
|
||
|
}
|
||
|
|
||
|
if (m_pAdrBook != NULL)
|
||
|
{
|
||
|
m_pAdrBook->Release();
|
||
|
m_pAdrBook = NULL;
|
||
|
}
|
||
|
} /* TGlobalDirectory::CloseGlobalList() */
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
TGlobalDirectory::UpdateEntry(
|
||
|
LPMAPIPROP pUserEntry, // in - interface to mail recipient object
|
||
|
ULONG ulType, // in - type of object
|
||
|
SecurityTranslatorArgs * args // in - translation settings
|
||
|
)
|
||
|
{
|
||
|
// Prepare to get the columns that interest us.
|
||
|
// For DCT, we probably only care about ASSOC_NT_ACCOUNT, and possibly PR_EMS_AB_NT_SECURITY_DESCRIPTOR
|
||
|
HRESULT hr;
|
||
|
BOOL anychange = FALSE;
|
||
|
BOOL verbose = args->LogVerbose();
|
||
|
|
||
|
MCSASSERT(pUserEntry);
|
||
|
if ( args->Cache()->IsCancelled() )
|
||
|
{
|
||
|
err.MsgWrite(0,DCT_MSG_OPERATION_ABORTED);
|
||
|
return FALSE;
|
||
|
}
|
||
|
SizedSPropTagArray(5, oPropertiesToGet) =
|
||
|
{
|
||
|
5,
|
||
|
{
|
||
|
PR_DISPLAY_NAME,
|
||
|
PR_EMS_AB_ASSOC_NT_ACCOUNT, // SID
|
||
|
PR_ENTRYID,
|
||
|
PR_EMS_AB_NT_SECURITY_DESCRIPTOR, // SD
|
||
|
PR_DISPLAY_TYPE
|
||
|
}
|
||
|
};
|
||
|
ULONG ulPropsReturned = 0;
|
||
|
LPSPropValue pUserProperties = NULL;
|
||
|
|
||
|
hr = pUserEntry->GetProps((SPropTagArray *)&oPropertiesToGet,
|
||
|
0, &ulPropsReturned, &pUserProperties);
|
||
|
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
err.SysMsgWrite(ErrE,hr,DCT_MSG_GET_SECURITY_FOR_RECIP_FAILED_D,hr);
|
||
|
pUserEntry->Release();
|
||
|
pUserEntry = NULL;
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
if (ulPropsReturned != oPropertiesToGet.cValues)
|
||
|
{
|
||
|
err.MsgWrite(ErrE,DCT_MSG_GET_SECURITY_FOR_RECIP_FAILED_D,hr);
|
||
|
pUserEntry->Release();
|
||
|
pUserEntry = NULL;
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// 1. Translate the PR_EMS_AB_ASSOC_NT_ACCOUNT property
|
||
|
|
||
|
WCHAR name[MAX_PATH];
|
||
|
safecopy(name,pUserProperties[0].Value.lpszW);
|
||
|
if ( m_stat )
|
||
|
{
|
||
|
m_stat->DisplayPath(name);
|
||
|
m_stat->IncrementExamined(mailbox);
|
||
|
}
|
||
|
PISID pSid = (PISID)pUserProperties[1].Value.bin.lpb;
|
||
|
if (pSid != NULL)
|
||
|
{
|
||
|
|
||
|
LPSPropValue pNewPropValues = NULL;
|
||
|
LPSPropProblemArray pProblems = NULL;
|
||
|
|
||
|
// check if the sid is one we need to change
|
||
|
//TRACE (_T("DisplayName = %s "),pUserProperties[0].Value.lpszW);
|
||
|
PSID newSid = 0;
|
||
|
TAcctNode * node;
|
||
|
|
||
|
if ( IsValidSid(pSid) )
|
||
|
{
|
||
|
|
||
|
node = args->Cache()->Lookup(pSid);
|
||
|
if ( m_stat )
|
||
|
{
|
||
|
m_stat->IncrementOwnerExamined();
|
||
|
if ( verbose )
|
||
|
err.MsgWrite(0,DCT_MSG_EXAMINED_S,pUserProperties[0].Value.lpszW);
|
||
|
}
|
||
|
if ( node == (TAcctNode*)-1 && m_stat )
|
||
|
m_stat->IncrementOwnerNoTarget();
|
||
|
|
||
|
if ( node && (node != (TAcctNode *)-1) && node->IsValidOnTgt() )
|
||
|
newSid = args->Cache()->GetTgtSid(node);
|
||
|
else
|
||
|
newSid = NULL;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
newSid = NULL;
|
||
|
}
|
||
|
if ( newSid )
|
||
|
{
|
||
|
//TRACE (_T("needs to be translated\n"));
|
||
|
// update the entry, or maybe put it into a list of entries to be updated
|
||
|
// Allocate a buffer to set the property.
|
||
|
MCSASSERT ( IsValidSid(newSid) );
|
||
|
PSID pMapiSid;
|
||
|
DWORD dwSidLength = GetLengthSid(newSid);
|
||
|
|
||
|
hr = ResultFromScode((*pMAPIAllocateBuffer)((sizeof SPropValue) * 1, (void **)&pNewPropValues));
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
err.SysMsgWrite(ErrE,hr,DCT_MSG_FAILED_TO_ALLOCATE_BUFFER_D,hr);
|
||
|
goto exit_update_sid;
|
||
|
}
|
||
|
// Allocate a buffer for the SID
|
||
|
hr = ResultFromScode((*pMAPIAllocateBuffer)(dwSidLength, (void **)&pMapiSid));
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
err.SysMsgWrite(ErrE,hr,DCT_MSG_FAILED_TO_ALLOCATE_BUFFER_D,hr);
|
||
|
goto exit_update_sid;
|
||
|
}
|
||
|
// Copy the SID.
|
||
|
CopySid(dwSidLength,pMapiSid,newSid);
|
||
|
// Write the SID
|
||
|
pNewPropValues[0].ulPropTag = PR_EMS_AB_ASSOC_NT_ACCOUNT;
|
||
|
pNewPropValues[0].Value.bin.lpb = (UCHAR *)pMapiSid;
|
||
|
pNewPropValues[0].Value.bin.cb = dwSidLength;
|
||
|
MCSASSERT (IsValidSid (pMapiSid) );
|
||
|
|
||
|
if ( m_stat )
|
||
|
{
|
||
|
m_stat->IncrementOwnerChange(node,mailbox,NULL);
|
||
|
}
|
||
|
anychange = TRUE;
|
||
|
if ( ! args->NoChange() )
|
||
|
{
|
||
|
hr = pUserEntry->SetProps(1, pNewPropValues, &pProblems);
|
||
|
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
err.SysMsgWrite(ErrE,hr,DCT_MSG_UPDATE_ACCOUNT_FAILED_D, hr);
|
||
|
pProblems = NULL; // Don't try to free this if SetProps fails.
|
||
|
goto exit_update_sid;
|
||
|
}
|
||
|
|
||
|
// Save changes.
|
||
|
hr = pUserEntry->SaveChanges(KEEP_OPEN_READWRITE);
|
||
|
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
err.SysMsgWrite(ErrE,hr,DCT_MSG_SAVE_CHANGES_FAILED_D, hr);
|
||
|
goto exit_update_sid;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
exit_update_sid:
|
||
|
if (pMapiSid != NULL)
|
||
|
(*pMAPIFreeBuffer)(pMapiSid);
|
||
|
|
||
|
if (pProblems != NULL)
|
||
|
(*pMAPIFreeBuffer)(pProblems);
|
||
|
|
||
|
if (pNewPropValues != NULL)
|
||
|
(*pMAPIFreeBuffer)(pNewPropValues);
|
||
|
|
||
|
}
|
||
|
}
|
||
|
// 2. Translate the PR_EMS_AB_NT_SECURITY_DESCRIPTOR property
|
||
|
PSECURITY_DESCRIPTOR pSD = (PSECURITY_DESCRIPTOR)pUserProperties[3].Value.bin.lpb;
|
||
|
if ( pSD && PR_EMS_AB_NT_SECURITY_DESCRIPTOR == pUserProperties[3].ulPropTag )
|
||
|
{
|
||
|
TMapiSD tMailbox((SECURITY_DESCRIPTOR *)pSD);
|
||
|
|
||
|
tMailbox.SetName(pUserProperties[0].Value.lpszW);
|
||
|
|
||
|
if ( tMailbox.HasSecurity() )
|
||
|
{
|
||
|
TSD * pSD = tMailbox.GetSecurity();
|
||
|
|
||
|
bool changes = tMailbox.ResolveSDInternal(args->Cache(),m_stat,verbose,args->TranslationMode(),mailbox, FALSE);
|
||
|
|
||
|
if ( changes )
|
||
|
{
|
||
|
anychange = TRUE;
|
||
|
// need to write the changes
|
||
|
LPSPropValue pNewPropValues = NULL;
|
||
|
LPSPropProblemArray pProblems = NULL;
|
||
|
|
||
|
// update the entry
|
||
|
// Allocate a buffer to set the property.
|
||
|
SECURITY_DESCRIPTOR * pMapiSD;
|
||
|
SECURITY_DESCRIPTOR * pRelSD = (SECURITY_DESCRIPTOR *)pSD->MakeRelSD();
|
||
|
DWORD dwSDLength = GetSecurityDescriptorLength(pRelSD);
|
||
|
|
||
|
if ( ! pRelSD )
|
||
|
{
|
||
|
goto exit_update_sd;
|
||
|
}
|
||
|
hr = ResultFromScode((*pMAPIAllocateBuffer)((sizeof SPropValue) * 1, (void **)&pNewPropValues));
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
err.SysMsgWrite(ErrE,hr,DCT_MSG_FAILED_TO_ALLOCATE_BUFFER_D,hr);
|
||
|
goto exit_update_sd;
|
||
|
}
|
||
|
// Allocate a buffer for the SD
|
||
|
hr = ResultFromScode((*pMAPIAllocateBuffer)(dwSDLength, (void **)&pMapiSD));
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
err.SysMsgWrite(ErrE,hr,DCT_MSG_FAILED_TO_ALLOCATE_BUFFER_D,hr);
|
||
|
goto exit_update_sd;
|
||
|
}
|
||
|
|
||
|
// Copy the SD.
|
||
|
memcpy(pMapiSD,pRelSD,dwSDLength );
|
||
|
// Write the SD
|
||
|
free(pRelSD);
|
||
|
pNewPropValues[0].ulPropTag = PR_EMS_AB_NT_SECURITY_DESCRIPTOR;
|
||
|
pNewPropValues[0].Value.bin.lpb = (UCHAR *)pMapiSD;
|
||
|
pNewPropValues[0].Value.bin.cb = dwSDLength;
|
||
|
if ( ! args->NoChange() )
|
||
|
{
|
||
|
hr = pUserEntry->SetProps(1, pNewPropValues, &pProblems);
|
||
|
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
err.SysMsgWrite(ErrE,hr,DCT_MSG_RECIP_SD_WRITE_FAILED_SD,pUserProperties[0].Value.lpszW,hr);
|
||
|
pProblems = NULL; // Don't try to free this if SetProps fails.
|
||
|
goto exit_update_sd;
|
||
|
}
|
||
|
|
||
|
// Save changes.
|
||
|
hr = pUserEntry->SaveChanges(KEEP_OPEN_READONLY);
|
||
|
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
err.SysMsgWrite(ErrE,hr,DCT_MSG_RECIP_SD_SAVE_FAILED_SD,pUserProperties[0].Value.lpszW,hr);
|
||
|
goto exit_update_sd;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
exit_update_sd:
|
||
|
if (pMapiSD != NULL)
|
||
|
(*pMAPIFreeBuffer)(pMapiSD);
|
||
|
|
||
|
if (pProblems != NULL)
|
||
|
(*pMAPIFreeBuffer)(pProblems);
|
||
|
|
||
|
if (pNewPropValues != NULL)
|
||
|
(*pMAPIFreeBuffer)(pNewPropValues);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
if ( anychange && m_stat )
|
||
|
{
|
||
|
m_stat->IncrementChanged(mailbox);
|
||
|
if ( args->LogFileDetails() )
|
||
|
err.MsgWrite(0,DCT_MSG_CHANGED_S,pUserProperties[0].Value.lpszW);
|
||
|
}
|
||
|
(*pMAPIFreeBuffer)(pUserProperties);
|
||
|
pUserProperties = NULL;
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
BOOL
|
||
|
TGlobalDirectory::Scan(
|
||
|
SecurityTranslatorArgs * args, // in - translation settings
|
||
|
WCHAR const * container // in - distinguished name or display name of container to process
|
||
|
)
|
||
|
{
|
||
|
|
||
|
LPABCONT pRootEntry = NULL; // root of AB
|
||
|
ULONG ulObjectType = 0;
|
||
|
HRESULT hr;
|
||
|
// TAccountCache * cache = args->Cache();
|
||
|
|
||
|
if ( args->Cache()->IsCancelled() )
|
||
|
{
|
||
|
err.MsgWrite(0,DCT_MSG_OPERATION_ABORTED);
|
||
|
return FALSE;
|
||
|
}
|
||
|
if ( ! m_pAdrBook )
|
||
|
{
|
||
|
OpenGlobalList();
|
||
|
}
|
||
|
if ( ! m_pAdrBook )
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
/* SizedSPropTagArray(3, rgPropTags) =
|
||
|
{
|
||
|
3,
|
||
|
{
|
||
|
PR_ENTRYID,
|
||
|
PR_DISPLAY_NAME,
|
||
|
PR_DEPTH,
|
||
|
}
|
||
|
};
|
||
|
*/
|
||
|
// Open the root entry.
|
||
|
hr = m_pAdrBook->OpenEntry(0, NULL, NULL, 0, &ulObjectType, (LPUNKNOWN*)&pRootEntry);
|
||
|
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
err.SysMsgWrite(ErrE,hr,DCT_MSG_GET_AB_ROOT_FAILED_D,hr);
|
||
|
return FALSE;
|
||
|
}
|
||
|
ScanHierarchy(pRootEntry,args,container);
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
BOOL
|
||
|
TGlobalDirectory::ScanHierarchy(
|
||
|
LPABCONT pContainer, // in - interface pointer to address book
|
||
|
SecurityTranslatorArgs * args, // in - translation settings
|
||
|
WCHAR const * container // in - distinguished name or display name of container
|
||
|
)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
LPMAPITABLE pContainerTable = NULL;
|
||
|
LPSRowSet pContainerRows = NULL;
|
||
|
LPABCONT pEntry = NULL;
|
||
|
ULONG ulObjectType = 0;
|
||
|
BOOL foundContainer = FALSE;
|
||
|
|
||
|
MCSASSERT(pContainer);
|
||
|
|
||
|
if ( args->Cache()->IsCancelled() )
|
||
|
{
|
||
|
err.MsgWrite(0,DCT_MSG_OPERATION_ABORTED);
|
||
|
return FALSE;
|
||
|
}
|
||
|
// Get its hierarchical table.
|
||
|
SizedSPropTagArray(4, rgPropTags) =
|
||
|
{
|
||
|
4,
|
||
|
{
|
||
|
PR_ENTRYID,
|
||
|
PR_DISPLAY_NAME_A, // I tried to get the display name in unicode format, but it did not work.
|
||
|
PR_OBJECT_TYPE,
|
||
|
PR_EMS_AB_OBJ_DIST_NAME_A
|
||
|
}
|
||
|
};
|
||
|
|
||
|
|
||
|
hr = pContainer->GetHierarchyTable(CONVENIENT_DEPTH, &pContainerTable);
|
||
|
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
err.SysMsgWrite(ErrE,hr,DCT_MSG_GET_HIER_TABLE_FAILED_D,hr);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// Get a list of all rows.
|
||
|
hr = (*pHrQueryAllRows)(pContainerTable, (LPSPropTagArray)&rgPropTags, NULL, NULL, 0, &pContainerRows);
|
||
|
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
err.SysMsgWrite(ErrE,hr,DCT_MSG_GET_TABLE_CONTENTS_FAILED_D,hr);
|
||
|
pContainerTable->Release();
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
for (ULONG ulRow = (ULONG)0; ulRow < pContainerRows->cRows; ++ulRow)
|
||
|
{
|
||
|
if ( args->Cache()->IsCancelled() )
|
||
|
{
|
||
|
err.MsgWrite(0,DCT_MSG_OPERATION_ABORTED);
|
||
|
return FALSE;
|
||
|
}
|
||
|
hr = m_pAdrBook->OpenEntry(pContainerRows->aRow[ulRow].lpProps[0].Value.bin.cb,
|
||
|
(LPENTRYID)pContainerRows->aRow[ulRow].lpProps[0].Value.bin.lpb,
|
||
|
NULL,
|
||
|
MAPI_MODIFY, // or 0 if nochange mode
|
||
|
&ulObjectType,
|
||
|
(LPUNKNOWN *)&pEntry);
|
||
|
|
||
|
if (!SUCCEEDED(hr))
|
||
|
{
|
||
|
err.SysMsgWrite(ErrE,hr,DCT_MSG_OPEN_CONTAINER_FAILED_SD,pContainerRows->aRow[ulRow].lpProps[1].Value.lpszW,hr);
|
||
|
return FALSE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
|
||
|
// LPMAPITABLE pContainerTable = NULL; // container table
|
||
|
UCHAR containerA[LEN_DistName];
|
||
|
safecopy(containerA,container);
|
||
|
// look for the specified container
|
||
|
if ( NOT_PT_ERROR(pContainerRows->aRow[ulRow].lpProps[1] )
|
||
|
&& !UStrICmp(containerA,pContainerRows->aRow[ulRow].lpProps[1].Value.lpszA) )
|
||
|
{
|
||
|
foundContainer = TRUE;
|
||
|
break;
|
||
|
}
|
||
|
if ( container && container[0]== '/' )
|
||
|
{
|
||
|
// Check the distinguished name
|
||
|
LPSPropValue props;
|
||
|
ULONG ulCount;
|
||
|
HRESULT hr2;
|
||
|
|
||
|
hr2 = pEntry->GetProps((LPSPropTagArray)&rgPropTags,0,&ulCount,&props);
|
||
|
if ( SUCCEEDED(hr) )
|
||
|
{
|
||
|
if ( ulCount >= 4 && NOT_PT_ERROR(props[3]) )
|
||
|
{
|
||
|
if ( !UStrICmp(props[3].Value.lpszA,containerA) )
|
||
|
{
|
||
|
foundContainer = TRUE;
|
||
|
}
|
||
|
}
|
||
|
(*pMAPIFreeBuffer)(props);
|
||
|
props = NULL;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
err.SysMsgWrite(ErrE,hr2,DCT_MSG_GET_CONTAINER_INFO_FAILED_D,hr2);
|
||
|
}
|
||
|
if ( foundContainer )
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
// if we found the container, process it's contents
|
||
|
if ( foundContainer )
|
||
|
{
|
||
|
|
||
|
if ( args->LogFileDetails() )
|
||
|
err.MsgWrite(0,DCT_MSG_EXAMINING_CONTENTS_S,pContainerRows->aRow[ulRow].lpProps[1].Value.lpszW);
|
||
|
// Get its contents table.
|
||
|
hr = pEntry->GetContentsTable(0, &pContainerTable);
|
||
|
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
err.SysMsgWrite(ErrE,hr,DCT_MSG_GET_CONTAINER_INFO_FAILED_D,hr);
|
||
|
pEntry->Release();
|
||
|
pEntry = NULL;
|
||
|
return FALSE;
|
||
|
}
|
||
|
// need to scan contents and hierarchy for this item
|
||
|
m_pContainer = pEntry;
|
||
|
m_name = pContainerRows->aRow[ulRow].lpProps[1].Value.lpszW;
|
||
|
ScanContents(pContainerTable,args);
|
||
|
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
err.MsgWrite(ErrW,DCT_MSG_CONTAINER_NOT_FOUND_S,container);
|
||
|
}
|
||
|
|
||
|
(*pFreeProws)(pContainerRows);
|
||
|
pContainerRows = NULL;
|
||
|
pContainerTable->Release();
|
||
|
return TRUE;
|
||
|
}
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
// Scan the contents of the Container for sids that need to be converted.
|
||
|
BOOL
|
||
|
TGlobalDirectory::ScanContents(
|
||
|
LPMAPITABLE pContainerTable, // in - contents table for container
|
||
|
SecurityTranslatorArgs * args // in - translation settings
|
||
|
)
|
||
|
{
|
||
|
ULONG ulDLEntries;
|
||
|
// WCHAR em[500] = L"";
|
||
|
|
||
|
MCSASSERT (pContainerTable);
|
||
|
|
||
|
if ( args->Cache()->IsCancelled() )
|
||
|
{
|
||
|
err.MsgWrite(0,DCT_MSG_OPERATION_ABORTED);
|
||
|
return FALSE;
|
||
|
}
|
||
|
HRESULT hr = pContainerTable->GetRowCount(0, &ulDLEntries);
|
||
|
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
err.SysMsgWrite(ErrE,hr,DCT_MSG_COUNT_CONTAINER_FAILED_SD,m_name,hr);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// Determine which column of the table has the Entry ID.
|
||
|
LPSPropTagArray pDLPropTags = NULL;
|
||
|
|
||
|
hr = pContainerTable->QueryColumns(0, &pDLPropTags);
|
||
|
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
err.SysMsgWrite(ErrE,hr,DCT_MSG_CONTAINER_CORRUPTED_SD,m_name,hr);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
ULONG ulEntryIdColumn;
|
||
|
BOOL bFoundEntryId = FALSE;
|
||
|
for (UINT ulCol = 0; ulCol < pDLPropTags->cValues; ++ulCol)
|
||
|
{
|
||
|
if (pDLPropTags->aulPropTag[ulCol] == PR_ENTRYID)
|
||
|
{
|
||
|
ulEntryIdColumn = ulCol;
|
||
|
bFoundEntryId = TRUE;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
(*pMAPIFreeBuffer)(pDLPropTags);
|
||
|
pDLPropTags = NULL;
|
||
|
if (!bFoundEntryId)
|
||
|
{
|
||
|
err.SysMsgWrite(ErrE,hr,DCT_MSG_CONTAINER_CORRUPTED_SD,m_name,hr);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Peruse the rows for the SIDs and names.
|
||
|
LPSRowSet pDLRows = NULL;
|
||
|
LPMAPIPROP pUserEntry = NULL;
|
||
|
for (ULONG ulRow = 0; ulRow < ulDLEntries; ++ulRow)
|
||
|
{
|
||
|
if ( args->Cache()->IsCancelled() )
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
hr = pContainerTable->SeekRow(BOOKMARK_BEGINNING, ulRow, NULL);
|
||
|
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
err.SysMsgWrite(ErrE,hr,DCT_MSG_CANT_FIND_ENTRY_SD,m_name,hr);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
hr = pContainerTable->QueryRows(1, TBL_NOADVANCE, &pDLRows);
|
||
|
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
err.SysMsgWrite(ErrE,hr,DCT_MSG_CANT_LOAD_ENTRY_SD,m_name,hr);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// Get the current entry.
|
||
|
ULONG ulEntryType = 0;
|
||
|
|
||
|
if ( pDLRows && pDLRows->cRows )
|
||
|
{
|
||
|
hr = m_pContainer->OpenEntry(pDLRows->aRow[0].lpProps[ulEntryIdColumn].Value.bin.cb,
|
||
|
(ENTRYID *)pDLRows->aRow[0].lpProps[ulEntryIdColumn].Value.bin.lpb,
|
||
|
NULL,
|
||
|
MAPI_MODIFY,
|
||
|
&ulEntryType,
|
||
|
(LPUNKNOWN*)&pUserEntry);
|
||
|
}
|
||
|
(*pFreeProws)(pDLRows);
|
||
|
pDLRows = NULL;
|
||
|
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
err.SysMsgWrite(ErrE,hr,DCT_MSG_RECIP_LOAD_FAILED_SD,m_name,hr);
|
||
|
return FALSE;
|
||
|
}
|
||
|
if ( pUserEntry )
|
||
|
{
|
||
|
UpdateEntry(pUserEntry,ulEntryType, args);
|
||
|
pUserEntry->Release();
|
||
|
pUserEntry = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( args->Cache()->IsCancelled() )
|
||
|
{
|
||
|
err.MsgWrite(0,DCT_MSG_OPERATION_ABORTED);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
BOOL TGlobalDirectory::OpenContainerByRow(long row)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
ULONG ulObjectType = 0;
|
||
|
MCSASSERT( m_pRootRows );
|
||
|
// TODO: check that row is in valid range.
|
||
|
// Load the container that should contain the DL.
|
||
|
hr = m_pAdrBook->OpenEntry( m_pRootRows->aRow[row].lpProps[0].Value.bin.cb,
|
||
|
(LPENTRYID)m_pRootRows->aRow[row].lpProps[0].Value.bin.lpb,
|
||
|
NULL,
|
||
|
MAPI_MODIFY, // or 0 if nochange mode
|
||
|
&ulObjectType,
|
||
|
(LPUNKNOWN *)&m_pContainer);
|
||
|
|
||
|
(*pFreeProws)(m_pRootRows);
|
||
|
m_pRootRows = NULL;
|
||
|
|
||
|
if (FAILED(hr)) {
|
||
|
err.SysMsgWrite(ErrE,hr,DCT_MSG_OPEN_CONTAINER_FAILED_SD,L"",hr);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// Look for the DL in its container.
|
||
|
// LPMAPITABLE pContainerTable = NULL; // container table
|
||
|
|
||
|
// Get its contents table.
|
||
|
hr = m_pContainer->GetContentsTable(0, &m_pContainerTable);
|
||
|
|
||
|
if (FAILED(hr)) {
|
||
|
err.SysMsgWrite(ErrE,hr,DCT_MSG_GET_TABLE_CONTENTS_FAILED_D,hr);
|
||
|
m_pContainer->Release();
|
||
|
m_pContainer = NULL;
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
#define NDX_SID 3
|
||
|
#define NDX_SD 4
|
||
|
|
||
|
DWORD
|
||
|
TGlobalDirectory::GetLDAPPort(
|
||
|
WCHAR * server
|
||
|
)
|
||
|
{
|
||
|
DWORD port = LDAP_PORT;
|
||
|
// DWORD rc = 0;
|
||
|
DAPI_ENTRY attList;
|
||
|
DAPI_ENTRY * pValues;
|
||
|
ATT_VALUE atts[3];
|
||
|
WCHAR distName[300];
|
||
|
PDAPI_EVENT result = NULL;
|
||
|
DAPI_ENTRY * pAttributes = NULL;
|
||
|
|
||
|
if ( ! LoadDAPI() )
|
||
|
{
|
||
|
err.MsgWrite(ErrW,DCT_MSG_LDAP_PORT_DETECT_FAILED_S,server);
|
||
|
return port;
|
||
|
}
|
||
|
|
||
|
if ( DAPIOpen(server) )
|
||
|
{
|
||
|
|
||
|
// set up the attribute list
|
||
|
attList.unAttributes = 3;
|
||
|
attList.ulEvalTag = VALUE_ARRAY;
|
||
|
attList.rgEntryValues = atts;
|
||
|
|
||
|
atts[0].DapiType = DAPI_UNICODE;
|
||
|
atts[0].Value.pszW = ATT_OBJ_CLASS;
|
||
|
atts[0].size = UStrLen(atts[0].Value.pszW);
|
||
|
atts[0].pNextValue = NULL;
|
||
|
|
||
|
atts[1].DapiType = DAPI_UNICODE;
|
||
|
atts[1].Value.pszW = ATT_LDAP_PORT;
|
||
|
atts[1].size = UStrLen(atts[1].Value.pszW);
|
||
|
atts[1].pNextValue = NULL;
|
||
|
|
||
|
atts[2].DapiType = DAPI_UNICODE;
|
||
|
atts[2].Value.pszW = LDAP_USE_SITE_VALUES;
|
||
|
atts[2].size = UStrLen(atts[2].Value.pszW);
|
||
|
atts[2].pNextValue = NULL;
|
||
|
|
||
|
// construct the DN for the attribute
|
||
|
swprintf(distName,L"/cn=Configuration/cn=Servers/cn=%s%s",server,LDAP_PortNumber_DN_Part);
|
||
|
|
||
|
result = (*pDAPIRead)(m_dSession,DAPI_RAW_MODE,distName,&attList,
|
||
|
&pValues, &pAttributes);
|
||
|
|
||
|
|
||
|
if ( ! result )
|
||
|
{
|
||
|
if ( pValues && pValues->rgEntryValues[2].Value.iValue == 0 )
|
||
|
{
|
||
|
// not using site defaults - rgEntryValues[1] contains the correct value for this server
|
||
|
if ( pValues && pValues->rgEntryValues[1].DapiType == DAPI_INT )
|
||
|
{
|
||
|
port = pValues->rgEntryValues[1].Value.iValue;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// This server is using the default values for the site - need to look at the site level to find the correct value
|
||
|
(*pDAPIFreeMemory)(pValues);
|
||
|
swprintf(distName,L"/cn=Configuration%s",LDAP_PortNumber_DN_Part);
|
||
|
attList.unAttributes = 2; // don't want to get 'Use-Site-Defaults' this time (we're looking at the Site)
|
||
|
result = (*pDAPIRead)(m_dSession,DAPI_RAW_MODE,distName,&attList,
|
||
|
&pValues, &pAttributes);
|
||
|
if ( ! result )
|
||
|
{
|
||
|
if ( pValues && pValues->rgEntryValues[1].DapiType == DAPI_INT )
|
||
|
{
|
||
|
// Here's the default value for the site
|
||
|
port = pValues->rgEntryValues[1].Value.iValue;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
(*pDAPIFreeMemory)(result);
|
||
|
}
|
||
|
}
|
||
|
(*pDAPIFreeMemory)(pValues);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
(*pDAPIFreeMemory)(result);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
err.MsgWrite(ErrW,DCT_MSG_LDAP_PORT_DETECT_FAILED_S,server);
|
||
|
}
|
||
|
DAPIClose();
|
||
|
|
||
|
ReleaseDAPI();
|
||
|
|
||
|
return port;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
TGlobalDirectory::GetSiteNameForServer(
|
||
|
WCHAR const * server, // in - name of exchange server to use
|
||
|
CLdapEnum * e, // in - LDAP connection to use for query
|
||
|
WCHAR * siteName // out- distinguished name of exchange site for server
|
||
|
)
|
||
|
{
|
||
|
WCHAR * atts[6] = { L"distinguishedName", L"rdn",NULL };
|
||
|
WCHAR query[200];
|
||
|
DWORD rc;
|
||
|
WCHAR ** values = NULL;
|
||
|
siteName[0] = 0;
|
||
|
|
||
|
swprintf(query,L"(&(objectClass=computer)(rdn=%ls))",server);
|
||
|
rc = e->Open(query,L"",2,100,3,atts);
|
||
|
// there should be only one server with this name
|
||
|
if (! rc )
|
||
|
rc = e->Next(&values);
|
||
|
if (! rc )
|
||
|
{
|
||
|
if ( !UStrICmp(values[1],server) )
|
||
|
{
|
||
|
WCHAR serverPrefix[LEN_Path];
|
||
|
swprintf(serverPrefix,L"cn=%ls,cn=Servers,cn=Configuration,",values[1]);
|
||
|
if ( ! UStrICmp(values[0],serverPrefix,UStrLen(serverPrefix)) )
|
||
|
{
|
||
|
UStrCpy(siteName,values[0] + UStrLen(serverPrefix));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
err.MsgWrite(ErrE,DCT_MSG_GENERIC_S,values[0]);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
err.MsgWrite(ErrE,DCT_MSG_LDAP_CALL_FAILED_SD,server,ERROR_NOT_FOUND);
|
||
|
}
|
||
|
e->FreeData(values);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
err.SysMsgWrite(ErrE,e->m_connection.LdapMapErrorToWin32(rc),DCT_MSG_LDAP_CALL_FAILED_SD,server,rc);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
TGlobalDirectory::DoLdapTranslation(
|
||
|
WCHAR * server,
|
||
|
WCHAR * creds,
|
||
|
WCHAR * password,
|
||
|
SecurityTranslatorArgs * args,
|
||
|
WCHAR * basept,
|
||
|
WCHAR * query
|
||
|
)
|
||
|
{
|
||
|
CLdapEnum e;
|
||
|
WCHAR * atts[6] = { L"distinguishedName", L"rdn", L"cn", L"Assoc-NT-Account",L"NT-Security-Descriptor",NULL };
|
||
|
WCHAR ** values = NULL;
|
||
|
ULONG port = GetLDAPPort(server);
|
||
|
ULONG pageSize = 100;
|
||
|
WCHAR basepoint[LEN_Path] = L"";
|
||
|
|
||
|
e.m_connection.SetCredentials(creds,password);
|
||
|
|
||
|
DWORD rc = e.InitConnection(server,port);
|
||
|
BOOL anychange = FALSE;
|
||
|
BOOL verbose = args->LogVerbose();
|
||
|
BOOL bUseMapFile = args->UsingMapFile();
|
||
|
|
||
|
|
||
|
if (! rc )
|
||
|
{
|
||
|
if ( ! basept )
|
||
|
{
|
||
|
GetSiteNameForServer(server,&e,basepoint);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// use the user-specified basepoint
|
||
|
safecopy(basepoint,basept);
|
||
|
}
|
||
|
if ( query )
|
||
|
{
|
||
|
rc = e.Open(query,basepoint,2,pageSize,5,atts);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
rc = e.Open(L"(objectClass=*)",basepoint,2,pageSize,5,atts);
|
||
|
}
|
||
|
if ( ! rc )
|
||
|
{
|
||
|
do
|
||
|
{
|
||
|
rc = e.Next(&values);
|
||
|
anychange = FALSE;
|
||
|
if (! rc )
|
||
|
{
|
||
|
if ( args->Cache()->IsCancelled() )
|
||
|
{
|
||
|
err.MsgWrite(0,DCT_MSG_OPERATION_ABORTED);
|
||
|
return FALSE;
|
||
|
}
|
||
|
if ( m_stat )
|
||
|
{
|
||
|
m_stat->DisplayPath(values[0]);
|
||
|
m_stat->IncrementExamined(mailbox);
|
||
|
}
|
||
|
|
||
|
// update the Assoc-NT-Account, if any
|
||
|
if ( values[NDX_SID] && *values[NDX_SID] )
|
||
|
{
|
||
|
// convert the SID to a binary value and look it up in the cache
|
||
|
BYTE pSid[500];
|
||
|
|
||
|
if ( e.m_connection.StringToBytes(values[NDX_SID],pSid) )
|
||
|
{
|
||
|
|
||
|
// check if the sid is one we need to change
|
||
|
//TRACE (_T("DisplayName = %s "),pUserProperties[0].Value.lpszW);
|
||
|
PSID newSid = 0;
|
||
|
TAcctNode * node;
|
||
|
|
||
|
if ( IsValidSid(pSid) )
|
||
|
{
|
||
|
if (!bUseMapFile)
|
||
|
node = args->Cache()->Lookup(pSid);
|
||
|
else
|
||
|
node = args->Cache()->LookupWODomain(pSid);
|
||
|
if ( m_stat )
|
||
|
{
|
||
|
m_stat->IncrementOwnerExamined();
|
||
|
if ( verbose )
|
||
|
err.MsgWrite(0,DCT_MSG_EXAMINED_S,values[0]);
|
||
|
}
|
||
|
if ( node == (TAcctNode*)-1 && m_stat )
|
||
|
m_stat->IncrementOwnerNoTarget();
|
||
|
|
||
|
if ( node && (node != (TAcctNode *)-1) && node->IsValidOnTgt() )
|
||
|
{
|
||
|
if (!bUseMapFile)
|
||
|
newSid = args->Cache()->GetTgtSid(node);
|
||
|
else
|
||
|
newSid = args->Cache()->GetTgtSidWODomain(node);
|
||
|
}
|
||
|
else
|
||
|
newSid = NULL;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
newSid = NULL;
|
||
|
}
|
||
|
if ( newSid )
|
||
|
{
|
||
|
//TRACE (_T("needs to be translated\n"));
|
||
|
MCSASSERT ( IsValidSid(newSid) );
|
||
|
WCHAR newSidStr[1000];
|
||
|
|
||
|
if ( e.m_connection.BytesToString((BYTE*)newSid,newSidStr,GetLengthSid(newSid)) )
|
||
|
{
|
||
|
if ( m_stat )
|
||
|
{
|
||
|
m_stat->IncrementOwnerChange(node,mailbox,NULL);
|
||
|
}
|
||
|
anychange = TRUE;
|
||
|
if ( ! args->NoChange() )
|
||
|
{
|
||
|
rc = e.m_connection.UpdateSimpleStringValue(values[0],atts[NDX_SID],newSidStr);
|
||
|
|
||
|
if ( rc )
|
||
|
{
|
||
|
err.SysMsgWrite(ErrE,rc,DCT_MSG_UPDATE_ACCOUNT_FAILED_D, rc);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
// Update the NT-Security-Descriptor, if any
|
||
|
if ( values[NDX_SD] && *values[NDX_SD] )
|
||
|
{
|
||
|
// convert the SID to a binary value and look it up in the cache
|
||
|
BYTE * pSD = new BYTE[UStrLen(values[NDX_SD])];
|
||
|
if (!pSD)
|
||
|
return FALSE;
|
||
|
|
||
|
if ( e.m_connection.StringToBytes(values[NDX_SD],pSD) )
|
||
|
{
|
||
|
TMapiSD tMailbox((SECURITY_DESCRIPTOR *)pSD);
|
||
|
if ( tMailbox.HasSecurity() )
|
||
|
{
|
||
|
TSD * pSD2 = tMailbox.GetSecurity();
|
||
|
|
||
|
bool changes = tMailbox.ResolveSDInternal(args->Cache(),m_stat,verbose,args->TranslationMode(),mailbox, bUseMapFile);
|
||
|
|
||
|
if ( changes )
|
||
|
{
|
||
|
anychange = TRUE;
|
||
|
SECURITY_DESCRIPTOR * pRelSD = (SECURITY_DESCRIPTOR *)pSD2->MakeRelSD();
|
||
|
DWORD dwSDLength = GetSecurityDescriptorLength(pRelSD);
|
||
|
|
||
|
if ( ! args->NoChange() )
|
||
|
{
|
||
|
WCHAR * pSDString = new WCHAR[1 + dwSDLength * 2];
|
||
|
if (!pSDString)
|
||
|
{
|
||
|
delete [] pSD;
|
||
|
return FALSE;
|
||
|
}
|
||
|
if ( e.m_connection.BytesToString((BYTE*)pRelSD,pSDString,dwSDLength) )
|
||
|
{
|
||
|
rc = e.m_connection.UpdateSimpleStringValue(values[0],atts[NDX_SD],pSDString);
|
||
|
if ( rc )
|
||
|
{
|
||
|
err.SysMsgWrite(ErrE,rc,DCT_MSG_RECIP_SD_WRITE_FAILED_SD,values[0],rc);
|
||
|
if ( rc == ERROR_INVALID_PARAMETER )
|
||
|
{
|
||
|
// this error occurs when the security descriptor is too large
|
||
|
// don't abort in this case
|
||
|
rc = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
delete [] pSDString;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
delete [] pSD;
|
||
|
}
|
||
|
|
||
|
if ( anychange && m_stat )
|
||
|
{
|
||
|
m_stat->IncrementChanged(mailbox);
|
||
|
if ( args->LogFileDetails() )
|
||
|
err.MsgWrite(0,DCT_MSG_CHANGED_S,values[0]);
|
||
|
}
|
||
|
e.FreeData(values);
|
||
|
}
|
||
|
|
||
|
} while ( ! rc );
|
||
|
}
|
||
|
if ( rc && (rc != LDAP_COMPARE_FALSE) && (rc != ERROR_NOT_FOUND) )
|
||
|
{
|
||
|
err.SysMsgWrite(ErrE,e.m_connection.LdapMapErrorToWin32(rc),DCT_MSG_LDAP_CALL_FAILED_SD,server,rc);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
err.SysMsgWrite(ErrE,rc,DCT_MSG_CANNOT_CONNECT_TO_EXCHANGE_SERVER_SSD,server,creds,rc);
|
||
|
}
|
||
|
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
BOOL
|
||
|
TGlobalDirectory::DoDAPITranslation(
|
||
|
WCHAR * serv, // in - Exchange server
|
||
|
SecurityTranslatorArgs * args // in - translation options
|
||
|
)
|
||
|
{
|
||
|
// enumerate organizations, sites, and containers
|
||
|
if ( DAPIOpen( serv ) )
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
LPWSTR pOrganizations = NULL;
|
||
|
LPWSTR pSites = NULL;
|
||
|
LPWSTR pContainers = NULL;
|
||
|
|
||
|
WCHAR * server;
|
||
|
WCHAR container[1];
|
||
|
|
||
|
if ( args->Cache()->IsCancelled() )
|
||
|
{
|
||
|
err.MsgWrite(0,DCT_MSG_OPERATION_ABORTED);
|
||
|
return FALSE;
|
||
|
}
|
||
|
server = new WCHAR[wcslen(serv) + 1];
|
||
|
if (!server)
|
||
|
return FALSE;
|
||
|
UStrCpy(server,serv);
|
||
|
container[0] = 0;
|
||
|
// enumerate organizations
|
||
|
m_stat->DisplayPath(GET_STRING(IDS_ScanningExchangeDirectory));
|
||
|
hr = HrEnumOrganizations(container,server,&pOrganizations);
|
||
|
if ( SUCCEEDED(hr) )
|
||
|
{
|
||
|
for ( ; pOrganizations && *pOrganizations ; pOrganizations+=UStrLen(pOrganizations) + 1 )
|
||
|
{
|
||
|
if ( args->Cache()->IsCancelled() )
|
||
|
{
|
||
|
err.MsgWrite(0,DCT_MSG_OPERATION_ABORTED);
|
||
|
break;
|
||
|
}
|
||
|
WCHAR wOrganizations[300];
|
||
|
safecopy(wOrganizations,pOrganizations);
|
||
|
DAPITranslate(wOrganizations,args);
|
||
|
//enumerate sites
|
||
|
hr = HrEnumSites(server,pOrganizations,&pSites);
|
||
|
if ( SUCCEEDED(hr) )
|
||
|
{
|
||
|
for ( ; pSites && *pSites ; pSites+=UStrLen(pSites) + 1 )
|
||
|
{
|
||
|
if ( args->Cache()->IsCancelled() )
|
||
|
{
|
||
|
err.MsgWrite(0,DCT_MSG_OPERATION_ABORTED);
|
||
|
break;
|
||
|
}
|
||
|
WCHAR wSites[300];
|
||
|
safecopy(wSites,pSites);
|
||
|
DAPITranslate(wSites,args);
|
||
|
//enumerate containers
|
||
|
hr = HrEnumContainers(server,pSites,TRUE,&pContainers);
|
||
|
if ( SUCCEEDED(hr) )
|
||
|
{
|
||
|
for ( ; pContainers && *pContainers ; pContainers+=UStrLen(pContainers) + 1 )
|
||
|
{
|
||
|
if (args->Cache()->IsCancelled() )
|
||
|
{
|
||
|
err.MsgWrite(0,DCT_MSG_OPERATION_ABORTED);
|
||
|
break;
|
||
|
}
|
||
|
WCHAR wContainers[300];
|
||
|
safecopy(wContainers,pContainers);
|
||
|
DAPITranslate(wContainers,args);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
err.SysMsgWrite(ErrE,hr,DCT_MSG_ENUM_CONTAINERS_FAILED_SD,hr);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
err.SysMsgWrite(ErrE,hr,DCT_MSG_ENUM_SITES_FAILED_SD,hr);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
err.SysMsgWrite(ErrE,hr,DCT_MSG_ENUM_ORGS_FAILED_SD,hr);
|
||
|
}
|
||
|
|
||
|
DAPIClose();
|
||
|
}
|
||
|
m_stat->DisplayPath(L"");
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
BOOL
|
||
|
TGlobalDirectory::DAPIOpen(
|
||
|
WCHAR * server // in - name of exchange server
|
||
|
)
|
||
|
{
|
||
|
PDAPI_EVENT result;
|
||
|
BOOL bRc;
|
||
|
|
||
|
WCHAR * serv;
|
||
|
|
||
|
if ( ! LoadDAPI() )
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
serv = new WCHAR [wcslen(server) + 1];
|
||
|
if (!serv)
|
||
|
return FALSE;
|
||
|
UStrCpy(serv,&*server);
|
||
|
|
||
|
// initialize parms
|
||
|
m_dParms.dwDAPISignature = DAPI_SIGNATURE;
|
||
|
m_dParms.dwFlags = DAPI_RAW_MODE | DAPI_EVENT_SOME;
|
||
|
m_dParms.pszDSAName = serv; // computer name of directory service agent
|
||
|
m_dParms.pszBasePoint = NULL;
|
||
|
m_dParms.pszContainer = NULL;
|
||
|
m_dParms.pszNTDomain = NULL;
|
||
|
m_dParms.pszCreateTemplate = NULL;
|
||
|
m_dParms.pAttributes = NULL;
|
||
|
|
||
|
result = (*pDAPIStart)(&m_dSession,&m_dParms);
|
||
|
if ( result )
|
||
|
{
|
||
|
// this is an error - need to show a message or something
|
||
|
LogDapiError(ErrE,L"DAPIStart: ",result);
|
||
|
(*pDAPIFreeMemory)(result);
|
||
|
bRc = FALSE;
|
||
|
}
|
||
|
else
|
||
|
bRc = TRUE;
|
||
|
return bRc;
|
||
|
}
|
||
|
|
||
|
BOOL TGlobalDirectory::DAPIClose()
|
||
|
{
|
||
|
(*pDAPIEnd)(&m_dSession);
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
void StripQuotes(TCHAR * distName)
|
||
|
{
|
||
|
int nChars = UStrLen(distName);
|
||
|
int i, // input index
|
||
|
o; // output index
|
||
|
BOOL prevQuote = FALSE;
|
||
|
|
||
|
for ( i = 0,o = 0 ; i < nChars ; i++ )
|
||
|
{
|
||
|
if ( distName[i] != _T('"') || prevQuote )
|
||
|
{
|
||
|
prevQuote = FALSE;
|
||
|
distName[o] = distName[i];
|
||
|
o++;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// leave the output pointer where it is, so the character will be overwritten
|
||
|
prevQuote = TRUE;
|
||
|
}
|
||
|
}
|
||
|
distName[o] = 0;
|
||
|
}
|
||
|
|
||
|
BOOL TGlobalDirectory::DAPITranslate(WCHAR * dn, SecurityTranslatorArgs * args)
|
||
|
{
|
||
|
PDAPI_EVENT result;
|
||
|
TCHAR * distName; // distinguished name of object to read.
|
||
|
DAPI_ENTRY * pValues = NULL;
|
||
|
DAPI_ENTRY * pAttributes = NULL;
|
||
|
ATT_VALUE attributes[8]; // DapiType, Value, size, pNextValue
|
||
|
WCHAR * att_names[8];
|
||
|
bool verbose = false;
|
||
|
|
||
|
if ( args->Cache()->IsCancelled() )
|
||
|
{
|
||
|
err.MsgWrite(0,DCT_MSG_OPERATION_ABORTED);
|
||
|
return FALSE;
|
||
|
}
|
||
|
att_names[0] = L"Obj-Class";
|
||
|
att_names[1] = L"DSA-Signature";
|
||
|
att_names[2] = L"Directory Name";
|
||
|
att_names[3] = L"Obj-View-Containers";
|
||
|
att_names[4] = L"Obj-Dist-Name";
|
||
|
int sdIndex = 5;
|
||
|
att_names[sdIndex] = L"NT-Security-Descriptor";
|
||
|
att_names[6] = L"Organizational-Unit-Name";
|
||
|
att_names[7] = L"Organization-Name";
|
||
|
DAPI_ENTRY attr;
|
||
|
|
||
|
attr.unAttributes = 8;
|
||
|
attr.ulEvalTag = VALUE_ARRAY;
|
||
|
attr.rgEntryValues = attributes;
|
||
|
for ( int i = 0 ; i < 8 ; i++ )
|
||
|
{
|
||
|
attributes[i].DapiType = DAPI_UNICODE;
|
||
|
attributes[i].Value.pszW = att_names[i];
|
||
|
attributes[i].size = UStrLen(att_names[i]);
|
||
|
attributes[i].pNextValue = NULL;
|
||
|
}
|
||
|
// we may need DAPI_RESTRICT_ACCESS and/or DAPI_MODIFY_REPLACE_PROPERTIES
|
||
|
|
||
|
distName = new TCHAR[wcslen(dn) + 1];
|
||
|
if (!distName)
|
||
|
return FALSE;
|
||
|
UStrCpy(distName,(LPCTSTR)dn);
|
||
|
StripQuotes(distName);
|
||
|
result = (*pDAPIRead)(m_dSession,DAPI_RAW_MODE,distName,&attr,
|
||
|
&pValues, &pAttributes);
|
||
|
if ( m_stat )
|
||
|
{
|
||
|
WCHAR wpath[MAX_PATH+1];
|
||
|
safecopy(wpath,&*distName);
|
||
|
m_stat->DisplayPath(wpath);
|
||
|
|
||
|
m_stat->IncrementExamined(container);
|
||
|
|
||
|
if ( verbose )
|
||
|
err.MsgWrite(0,DCT_MSG_EXAMINED_S,&*dn);
|
||
|
}
|
||
|
if ( result )
|
||
|
{
|
||
|
// this is an error - need to show a message or something
|
||
|
LogDapiError(ErrE,L"DAPIRead: ",result);
|
||
|
(*pDAPIFreeMemory)(result);
|
||
|
}
|
||
|
if ( pValues && pValues->rgEntryValues[sdIndex].DapiType == DAPI_BINARY )
|
||
|
{
|
||
|
SECURITY_DESCRIPTOR * pSD = (SECURITY_DESCRIPTOR *)pValues->rgEntryValues[sdIndex].Value.lpBinary;
|
||
|
TMapiSD exContainer(pSD);
|
||
|
|
||
|
if ( exContainer.HasSecurity() )
|
||
|
{
|
||
|
exContainer.SetName(distName);
|
||
|
|
||
|
bool changes = exContainer.ResolveSDInternal(args->Cache(),m_stat,false,args->TranslationMode(),container, FALSE);
|
||
|
// ULONG usn = 0;
|
||
|
|
||
|
if ( changes )
|
||
|
{
|
||
|
if (m_stat)
|
||
|
{
|
||
|
m_stat->IncrementChanged(container);
|
||
|
if ( args->LogFileDetails() )
|
||
|
err.MsgWrite(0,DCT_MSG_CHANGED_S,&*dn);
|
||
|
}
|
||
|
|
||
|
// record the changes
|
||
|
if ( ! args->NoChange() )
|
||
|
{
|
||
|
TSD * tSD = exContainer.GetSecurity();
|
||
|
|
||
|
pSD = (SECURITY_DESCRIPTOR *)tSD->MakeRelSD();
|
||
|
pValues->rgEntryValues[sdIndex].Value.lpBinary = (UCHAR *)pSD;
|
||
|
// make sure the size is still correct. For NT 3.51 and 4 it
|
||
|
// should always be, since all the SIDs are the same length, but
|
||
|
// we don't want this to break if it gets a different-length SID
|
||
|
pValues->rgEntryValues[sdIndex].size = GetSecurityDescriptorLength(pSD);
|
||
|
|
||
|
// Change the directory name to match the distinguished name, otherwise you get
|
||
|
// object not found error.
|
||
|
pValues->rgEntryValues[2].Value.pszW = pValues->rgEntryValues[4].Value.pszW;
|
||
|
// Same problem for the OU-Name for Sites
|
||
|
if ( pValues->rgEntryValues[6].DapiType != DAPI_NO_VALUE )
|
||
|
{
|
||
|
pValues->rgEntryValues[6].Value.pszW = pValues->rgEntryValues[4].Value.pszW;
|
||
|
}
|
||
|
// Same problem for the O-name for organizations
|
||
|
if ( pValues->rgEntryValues[7].DapiType != DAPI_NO_VALUE )
|
||
|
{
|
||
|
pValues->rgEntryValues[7].Value.pszW = pValues->rgEntryValues[4].Value.pszW;
|
||
|
}
|
||
|
result = (*pDAPIWrite)(m_dSession,DAPI_WRITE_MODIFY,&attr,pValues,NULL,NULL,NULL);
|
||
|
if ( result )
|
||
|
{
|
||
|
LogDapiError(ErrE,L"DAPIWrite: ",result);
|
||
|
(*pDAPIFreeMemory)(result);
|
||
|
}
|
||
|
free(pSD);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
// cleanup
|
||
|
if ( pValues )
|
||
|
(*pDAPIFreeMemory)(pValues);
|
||
|
if ( pAttributes )
|
||
|
(*pDAPIFreeMemory)(pAttributes);
|
||
|
delete [] distName;
|
||
|
return TRUE;
|
||
|
}
|