/* ----------------------------------------------------------------------

	Module:		ULS.DLL (Service Provider)
	File:		spluser.cpp
	Content:	This file contains the local user object.
	History:
	10/15/96	Chu, Lon-Chan [lonchanc]
				Created.

	Copyright (c) Microsoft Corporation 1996-1997

   ---------------------------------------------------------------------- */

#include "ulsp.h"
#include "spinc.h"

// Array of constant strings for user object's attribute names
//
const TCHAR *c_apszUserStdAttrNames[COUNT_ENUM_USERATTR] =
{
	TEXT ("cn"),
	TEXT ("givenname"),
	TEXT ("surname"),
	TEXT ("rfc822mailbox"),
	TEXT ("location"),
#ifdef USE_DEFAULT_COUNTRY
	TEXT ("aCountryName"),
#endif
	TEXT ("comment"),
	TEXT ("sipaddress"),
	TEXT ("sflags"),
	TEXT ("c"),

	TEXT ("ssecurity"),
	TEXT ("sttl"),

	TEXT ("objectClass"),
	TEXT ("o"),
};


/* ---------- public methods ----------- */


UlsLdap_CLocalUser::
UlsLdap_CLocalUser ( VOID )
{
	// Reference count
	//
	m_cRefs = 0;

	// User object's signature
	//
	m_uSignature = USEROBJ_SIGNATURE;

	// Clean up attached server info structure
	//
	ZeroMemory (&m_ServerInfo, sizeof (m_ServerInfo));

	// Clean up the scratch buffer for caching pointers to attribute values
	//
	ZeroMemory (&m_UserInfo, sizeof (m_UserInfo));

	// Clean up DN (old and current ones)
	m_pszDN = NULL;
	m_pszOldDN = NULL;

	// Clean up the refresh search filter
	//
	m_pszRefreshFilter = NULL;

	// Indicate this user is not registered yet
	//
	SetRegNone ();

	// Reset time to live value
	m_uTTL = ULS_DEF_REFRESH_MINUTE; // in unit of minute: no effect on current ils, but to avoid legacy issue later
	m_dwIPAddress = 0;
}


UlsLdap_CLocalUser::
~UlsLdap_CLocalUser ( VOID )
{
	// Invalidate the user object's signature
	//
	m_uSignature = (ULONG) -1;

	// Free server info structure
	//
	::IlsFreeServerInfo (&m_ServerInfo);

	// Free DN (old and current ones)
	//
	MemFree (m_pszDN);
	MemFree (m_pszOldDN);

	// Free the refresh search filter
	//
	MemFree (m_pszRefreshFilter);
}


ULONG UlsLdap_CLocalUser::
AddRef ( VOID )
{
	InterlockedIncrement (&m_cRefs);
	return m_cRefs;
}


ULONG UlsLdap_CLocalUser::
Release ( VOID )
{
	MyAssert (m_cRefs != 0);

	if (m_cRefs != 0)
	{
		InterlockedDecrement (&m_cRefs);
	}

	ULONG cRefs = m_cRefs;
	if (cRefs == 0)
		delete this;

	return cRefs;
}


HRESULT UlsLdap_CLocalUser::
Register ( ULONG *puRespID, SERVER_INFO *pServerInfo, LDAP_USERINFO *pInfo )
{
	MyAssert (puRespID != NULL);
	MyAssert (pInfo != NULL);

	MyAssert (	pServerInfo->pszServerName != NULL &&
				pServerInfo->pszServerName[0] != TEXT ('\0'));
	MyAssert (	pServerInfo->pszBaseDN != NULL &&
				pServerInfo->pszBaseDN[0] != TEXT ('\0'));

	// cache the server info
	HRESULT hr = ::IlsCopyServerInfo (&m_ServerInfo, pServerInfo);
	if (hr != S_OK)
		return hr;

	// cache user info
	hr = CacheUserInfo (pInfo);
	if (hr != S_OK)
		return hr;

	// get ip address
	m_dwIPAddress = 0;
	hr = ::GetLocalIPAddress (&m_dwIPAddress);
	if (hr != S_OK)
		return hr;

	// Create IP address string
	//
	m_UserInfo.apszStdAttrValues[ENUM_USERATTR_IP_ADDRESS] = &m_UserInfo.szIPAddress[0];
	::GetLongString (m_dwIPAddress, &m_UserInfo.szIPAddress[0]);

	// Create client signature string
	//
	m_UserInfo.apszStdAttrValues[ENUM_USERATTR_CLIENT_SIG] = &m_UserInfo.szClientSig[0];
	::GetLongString (g_dwClientSig, &m_UserInfo.szClientSig[0]);

	// Create TTL string
	//
	m_UserInfo.apszStdAttrValues[ENUM_USERATTR_TTL] = &m_UserInfo.szTTL[0];
	::GetLongString (m_uTTL, &m_UserInfo.szTTL[0]);

	// ideally, o= and c= should be read in from registiry
	// but for now, we simply hard code it
	m_UserInfo.apszStdAttrValues[ENUM_USERATTR_OBJECT_CLASS] = (TCHAR *) &c_szRTPerson[0];
	m_UserInfo.apszStdAttrValues[ENUM_USERATTR_O] = (TCHAR *) &c_szDefO[0];
#ifdef USE_DEFAULT_COUNTRY
	m_UserInfo.apszStdAttrValues[ENUM_USERATTR_C] = (TCHAR *) &c_szDefC[0];
#endif

	// build DN
	hr = BuildDN ();
	if (hr != S_OK)
		return hr;

	// build refreh filter
	m_pszRefreshFilter = UserCreateRefreshFilter (m_UserInfo.apszStdAttrValues[ENUM_USERATTR_CN]);
	if (m_pszRefreshFilter == NULL)
		return ULS_E_MEMORY;

	// build modify array for ldap_add()
	LDAPMod **ppMod = NULL;
	hr = CreateRegisterModArr (&ppMod);
	if (hr != S_OK)
		return hr;
	MyAssert (ppMod != NULL);

	// so far, we are done with local preparation

	// get the connection object
	UlsLdap_CSession *pSession = NULL;
	hr = g_pSessionContainer->GetSession (&pSession, &m_ServerInfo);
	if (hr != S_OK)
	{
		MemFree (ppMod);
		return hr;
	}
	MyAssert (pSession != NULL);

	// get the ldap session
	LDAP *ld = pSession->GetLd ();
	MyAssert (ld != NULL);

	// send the data over the wire
	ULONG uMsgID = ldap_add (ld, GetDN (), ppMod);
	MemFree (ppMod);
	if (uMsgID == -1)
	{
		hr = ::LdapError2Hresult (ld->ld_errno);
		pSession->Disconnect ();
		return hr;
	}

	// construct a pending info
	PENDING_INFO PendingInfo;
	::FillDefPendingInfo (&PendingInfo, ld, uMsgID, INVALID_MSG_ID);
	PendingInfo.uLdapResType = LDAP_RES_ADD;
	PendingInfo.uNotifyMsg = WM_ULS_REGISTER_USER;
	PendingInfo.hObject = (HANDLE) this;

	// queue it
	hr = g_pPendingQueue->EnterRequest (pSession, &PendingInfo);
	if (hr != S_OK)
	{
		ldap_abandon (ld, uMsgID);
		pSession->Disconnect ();
		MyAssert (FALSE);
	}

	*puRespID = PendingInfo.uRespID;
	return hr;
}


HRESULT UlsLdap_CLocalUser::
UnRegister ( ULONG *puRespID )
{
	MyAssert (puRespID != NULL);

	// Make sure that there is not refresh scheduled for this object
	//
	if (g_pRefreshScheduler != NULL)
	{
		g_pRefreshScheduler->RemoveUserObject (this);
	}
	else
	{
		MyAssert (FALSE);
	}

	// Unregister it locally
	//
	if (! IsRegRemotely ())
	{
		*puRespID = ::GetUniqueNotifyID ();
		SetRegNone ();
		PostMessage (g_hWndNotify, WM_ULS_UNREGISTER_USER, *puRespID, S_OK);
		return S_OK;
	}

	SetRegNone ();

	// Get the session object
	//
	UlsLdap_CSession *pSession = NULL;
	HRESULT hr = g_pSessionContainer->GetSession (&pSession, &m_ServerInfo);
	if (hr != S_OK)
		return hr;
	MyAssert (pSession != NULL);

	// Get the ldap session
	//
	LDAP *ld = pSession->GetLd ();
	MyAssert (ld != NULL);

	// LONCHANC: notify global user object of this unregister user


	// send the data over the wire
	ULONG uMsgID = ldap_delete (ld, GetDN ());
	if (uMsgID == -1)
	{
		hr = ::LdapError2Hresult (ld->ld_errno);
		pSession->Disconnect ();
		return hr;
	}

	// construct a pending info
	PENDING_INFO PendingInfo;
	::FillDefPendingInfo (&PendingInfo, ld, uMsgID, INVALID_MSG_ID);
	PendingInfo.uLdapResType = LDAP_RES_DELETE;
	PendingInfo.uNotifyMsg = WM_ULS_UNREGISTER_USER;

	// queue it
	hr = g_pPendingQueue->EnterRequest (pSession, &PendingInfo);
	if (hr != S_OK)
	{
		ldap_abandon (ld, uMsgID);
		pSession->Disconnect ();
		MyAssert (FALSE);
	}

	*puRespID = PendingInfo.uRespID;
	return hr;
}


HRESULT UlsLdap_CLocalUser::
SetStdAttrs ( ULONG *puRespID, LDAP_USERINFO *pInfo )
{
	MyAssert (puRespID != NULL);
	MyAssert (pInfo != NULL);

	ULONG uMsgID_modify, uMsgID_modrdn;
	UlsLdap_CSession *pSession;
	LDAP *ld;
	HRESULT hr;

	// Get the session object
	//
	hr = g_pSessionContainer->GetSession (&pSession, GetServerInfo ());
	if (hr != S_OK)
		return hr;
	MyAssert (pSession != NULL);

	// Get the ldap session
	//
	ld = pSession->GetLd ();
	MyAssert (ld != NULL);

	// Change cn?
	//
	if (pInfo->uOffsetEMailName != 0)
	{
		// Cache user info such that cn is refreshed
		//
		hr = CacheUserInfo (pInfo);
		if (hr != S_OK)
		{
			pSession->Disconnect ();
			return hr;
		}

		// We have to use ldap_modrdn to modify cn and this must be
		// done before any other attribute changes
		//
		uMsgID_modrdn = ldap_modrdn2 (
							ld, GetDN (),
							m_UserInfo.apszStdAttrValues[ENUM_USERATTR_CN],
							1);
		if (uMsgID_modrdn == -1)
		{
			pSession->Disconnect ();
			hr = ::LdapError2Hresult (ld->ld_errno);
			return hr;
		}

		// Update DN
		//
		BuildDN ();
	}
	else
	{
		uMsgID_modrdn = INVALID_MSG_ID;
	}

	// Set standard attributes
	//
	hr = UlsLdap_CStdAttrs::SetStdAttrs (	NULL,
											&uMsgID_modify,
											0,
											(VOID *) pInfo,
											GetServerInfo (),
											GetDN ());
	if (hr != S_OK)
	{
		if (uMsgID_modrdn != INVALID_MSG_ID)
		{
			ldap_abandon (ld, uMsgID_modrdn);
			pSession->Disconnect ();
		}
		return hr;
	}

	// Construct a pending info
	//
	PENDING_INFO PendingInfo;
	if (uMsgID_modrdn == INVALID_MSG_ID)
		::FillDefPendingInfo (&PendingInfo, ld, uMsgID_modify, INVALID_MSG_ID);
	else
		::FillDefPendingInfo (&PendingInfo, ld, uMsgID_modrdn, uMsgID_modify);
	PendingInfo.uLdapResType = LDAP_RES_MODIFY;
	PendingInfo.uNotifyMsg = WM_ULS_SET_USER_INFO;
	PendingInfo.hObject = (HANDLE) this; // for DN rollback

	// Queue it
	//
	hr = g_pPendingQueue->EnterRequest (pSession, &PendingInfo);
	if (hr != S_OK)
	{
		if (uMsgID_modrdn != INVALID_MSG_ID)
		{
			ldap_abandon (ld, uMsgID_modrdn);
			pSession->Disconnect ();
		}
		ldap_abandon (ld, uMsgID_modify);
		MyAssert (FALSE);
	}

	*puRespID = PendingInfo.uRespID;
	return hr;
}


VOID UlsLdap_CLocalUser::
RollbackDN ( VOID )
{
	if (m_pszOldDN != NULL)
	{
		MemFree (m_pszDN);
		m_pszDN = m_pszOldDN;
		m_pszOldDN = NULL;
	}
}


HRESULT UlsLdap_CLocalUser::
UpdateIPAddress ( BOOL fPrimary )
{
	// Update cached ip address
	//
	HRESULT hr = ::GetLocalIPAddress (&m_dwIPAddress);
	if (hr != S_OK)
		return hr;

	// Update the ip address string
	//
	::GetLongString (m_dwIPAddress, &m_UserInfo.szIPAddress[0]);

	// Update ip address info on the server ONLY if primary
	//
	if (! fPrimary)
		return hr;

	// Update IP address on the server
	//
	return ::IlsUpdateIPAddress (	GetServerInfo (),
									GetDN (),
									(TCHAR *) c_apszUserStdAttrNames[ENUM_USERATTR_IP_ADDRESS],
									&m_UserInfo.szIPAddress[0],
									ISBU_MODOP_MODIFY_USER,
									GetPrefixCount (),
									GetPrefixString ());
}


/* ---------- protected methods ----------- */


HRESULT UlsLdap_CLocalUser::
SendRefreshMsg ( VOID )
{
	if (m_pszRefreshFilter == NULL)
		return ULS_E_POINTER;

	// Get local ip address
	//
	DWORD dwIPAddress = 0;
	HRESULT hr = ::GetLocalIPAddress (&dwIPAddress);
	if (hr != S_OK)
	{
		MyDebugMsg ((ZONE_KA, "KA: cannot get my ip address\r\n"));
		return hr;
	}

	// If dwIPAddress is 0, then we are not on the network any more
	// start relogon process
	//
	if (dwIPAddress == 0)
	{
		MyDebugMsg ((ZONE_KA, "KA: my ip address is null\r\n"));

		// Indicate that I am not connected to the server anymore
		//
		SetRegLocally ();

		// Second, notify this app of the network being down
		//
		PostMessage (g_hWndHidden, WM_ULS_NETWORK_DOWN, TRUE, (LPARAM) this);

		// Report error
		//
		return ULS_E_NETWORK_DOWN;
;
	}
	else
	// If dwIPAddress and m_dwIPAddress, alert
	//
	if (dwIPAddress != m_dwIPAddress)
	{
		// Notify the com to start changing ip address
		// the actual change can happen later
		//
		PostMessage (g_hWndHidden, WM_ULS_IP_ADDRESS_CHANGED, TRUE, (LPARAM) this);
	}

	// get the connection object
	UlsLdap_CSession *pSession = NULL;
	hr = g_pSessionContainer->GetSession (&pSession, &m_ServerInfo);
	if (hr != S_OK)
	{
		MyDebugMsg ((ZONE_KA, "KA: network down, hr=0x%lX\r\n", hr));

		// Indicate that I am not connected to the server anymore
		//
		SetRegLocally ();

		// Second, notify the com of network down
		//
		PostMessage (g_hWndHidden, WM_ULS_NETWORK_DOWN, TRUE, (LPARAM) this);

		// Report error
		//
		return ULS_E_NETWORK_DOWN;
	}
	MyAssert (pSession != NULL);

	// get the ldap session
	LDAP *ld = pSession->GetLd ();
	MyAssert (ld != NULL);

	// Set attributes to return
	//
	TCHAR *apszAttrNames[3];
	apszAttrNames[0] = STR_CN;
	apszAttrNames[1] = (TCHAR *) c_apszUserStdAttrNames[ENUM_USERATTR_TTL];
	apszAttrNames[2] = NULL;

	// Update options in ld
	//
	ld->ld_sizelimit = 0;	// no limit in the num of entries to return
	ld->ld_timelimit = 0;	// no limit on the time to spend on the search
	ld->ld_deref = LDAP_DEREF_ALWAYS;

	// Send search query
	//
	MyDebugMsg ((ZONE_KA, "KA: calling ldap_search()...\r\n"));
	ULONG uMsgID = ::ldap_search (ld, (TCHAR *) &c_szDefUserBaseDN[0],	// base DN
									LDAP_SCOPE_BASE,	// scope
									m_pszRefreshFilter,
									&apszAttrNames[0],	// attrs[]
									0	// both type and value
									);
	if (uMsgID == -1)
	{
		MyDebugMsg ((ZONE_KA, "KA: ldap_search() failed\r\n"));
		hr = ::LdapError2Hresult (ld->ld_errno);
		pSession->Disconnect ();
		return hr;
	}

	// Let's wait for the result
	//
	LDAP_TIMEVAL TimeVal;
	TimeVal.tv_usec = 0;
	TimeVal.tv_sec = (m_ServerInfo.nTimeout != 0) ?
							m_ServerInfo.nTimeout :
							90;
	LDAPMessage *pLdapMsg = NULL;
	INT ResultType = ::ldap_result (ld, uMsgID, 0, &TimeVal, &pLdapMsg);

	// Deal with timeout or error
	//
	if (ResultType != LDAP_RES_SEARCH_ENTRY &&
		ResultType != LDAP_RES_SEARCH_RESULT)
	{
		MyDebugMsg ((ZONE_KA, "KA: result type mismatches!\r\n"));
		hr = ULS_E_TIMEOUT;
		goto MyExit;
	}

	if (pLdapMsg != NULL)
	{
		switch (pLdapMsg->lm_returncode)
		{
		case LDAP_NO_SUCH_OBJECT:
			MyDebugMsg ((ZONE_KA, "KA: no such object!\r\n"));

			// Indicate that I am not connected to the server anymore
			//
			SetRegLocally ();

			// Second, notify this app to relogon
			//
			PostMessage (g_hWndHidden, WM_ULS_NEED_RELOGON, TRUE, (LPARAM) this);

			// Report error
			//
			hr = ULS_E_NEED_RELOGON;
			break;

		case LDAP_SUCCESS:
			// Get the new refresh period
			//
			hr = ::IlsParseRefreshPeriod (
						ld,
						pLdapMsg,
						c_apszUserStdAttrNames[ENUM_USERATTR_TTL],
						&m_uTTL);
			break;

		default:
			MyDebugMsg ((ZONE_KA, "KA: unknown lm_returncode=%ld\r\n", pLdapMsg->lm_returncode));
			MyAssert (FALSE);
			break;
		}
	}

MyExit:

	// Free message
	//
	if (pLdapMsg != NULL)
		ldap_msgfree (pLdapMsg);

	// Free up the session
	//
	pSession->Disconnect ();
	return hr;
}


/* ---------- private methods ----------- */


HRESULT UlsLdap_CLocalUser::
CreateRegisterModArr ( LDAPMod ***pppMod )
{
	if (pppMod == NULL)
		return ULS_E_POINTER;

	ULONG cAttrs = COUNT_ENUM_USERATTR;
	ULONG cbMod = ::IlsCalcModifyListSize (cAttrs);
	*pppMod = (LDAPMod **) MemAlloc (cbMod);
	if (*pppMod == NULL)
		return ULS_E_MEMORY;

	LDAPMod *pMod;
	for (ULONG i = 0; i < cAttrs; i++)
	{
		pMod = ::IlsGetModifyListMod (pppMod, cAttrs, i);
		(*pppMod)[i] = pMod;
		pMod->mod_op = LDAP_MOD_ADD;

		FillModArrAttr (pMod, i);
	}

// the following overwrote givenname attribute
//	::IlsFixUpModOp ((*pppMod)[0], LDAP_MOD_ADD);
	(*pppMod)[cAttrs] = NULL;
	return S_OK;
}


HRESULT UlsLdap_CLocalUser::
CreateSetStdAttrsModArr ( LDAPMod ***pppMod )
{
	MyAssert (pppMod != NULL);
	DWORD dwFlags = m_UserInfo.dwFlags;

	HRESULT hr;
	ULONG cTotal = 0;
	hr = ::FillDefStdAttrsModArr (	pppMod,
									dwFlags,
									COUNT_ENUM_USERINFO,
									&cTotal,
									ISBU_MODOP_MODIFY_USER,
									GetPrefixCount (),
									GetPrefixString ());
	if (hr != S_OK)
		return hr;

	// Start indexing
	//
	ULONG i = GetPrefixCount ();

	// Fill in standard attributes
	//
	if (dwFlags & USEROBJ_F_FIRST_NAME)
		FillModArrAttr ((*pppMod)[i++], ENUM_USERATTR_FIRST_NAME);

	if (dwFlags & USEROBJ_F_LAST_NAME)
		FillModArrAttr ((*pppMod)[i++], ENUM_USERATTR_LAST_NAME);

	if (dwFlags & USEROBJ_F_EMAIL_NAME)
		FillModArrAttr ((*pppMod)[i++], ENUM_USERATTR_EMAIL_NAME);

	if (dwFlags & USEROBJ_F_CITY_NAME)
		FillModArrAttr ((*pppMod)[i++], ENUM_USERATTR_CITY_NAME);

	if (dwFlags & USEROBJ_F_COUNTRY_NAME)
		FillModArrAttr ((*pppMod)[i++], ENUM_USERATTR_COUNTRY_NAME);

	if (dwFlags & USEROBJ_F_COMMENT)
		FillModArrAttr ((*pppMod)[i++], ENUM_USERATTR_COMMENT);

	if (dwFlags & USEROBJ_F_IP_ADDRESS)
		FillModArrAttr ((*pppMod)[i++], ENUM_USERATTR_IP_ADDRESS);

	if (dwFlags & USEROBJ_F_FLAGS)
		FillModArrAttr ((*pppMod)[i++], ENUM_USERATTR_FLAGS);

	MyAssert (i == cTotal);

	return S_OK;
}


VOID UlsLdap_CLocalUser::
FillModArrAttr ( LDAPMod *pMod, LONG AttrIdx )
{
	pMod->mod_type = (TCHAR *) c_apszUserStdAttrNames[AttrIdx];

	// single valued attr
	TCHAR **ppsz = (TCHAR **) (pMod + 1);
	pMod->mod_values = ppsz;
	*ppsz++ = (m_UserInfo.apszStdAttrValues[AttrIdx] != NULL) ?
				m_UserInfo.apszStdAttrValues[AttrIdx] :
				(TCHAR *) &c_szEmptyString[0];

	*ppsz = NULL;
}


HRESULT UlsLdap_CLocalUser::
CacheInfo ( VOID *pInfo )
{
	return CacheUserInfo ((LDAP_USERINFO *) pInfo);
}


HRESULT UlsLdap_CLocalUser::
CacheUserInfo ( LDAP_USERINFO *pInfo )
{
	ZeroMemory (&m_UserInfo, sizeof (m_UserInfo));
	TCHAR *pszName;

	if (pInfo->uOffsetName != INVALID_OFFSET)
	{
		pszName = (TCHAR *) (((BYTE *) pInfo) + pInfo->uOffsetName);
		m_UserInfo.apszStdAttrValues[ENUM_USERATTR_CN] = pszName;
		// m_UserInfo.dwFlags |= USEROBJ_F_NAME;
	}

	if (pInfo->uOffsetFirstName != INVALID_OFFSET)
	{
		pszName = (TCHAR *) (((BYTE *) pInfo) + pInfo->uOffsetFirstName);
		m_UserInfo.apszStdAttrValues[ENUM_USERATTR_FIRST_NAME] = pszName;
		m_UserInfo.dwFlags |= USEROBJ_F_FIRST_NAME;
	}

	if (pInfo->uOffsetLastName != INVALID_OFFSET)
	{
		pszName = (TCHAR *) (((BYTE *) pInfo) + pInfo->uOffsetLastName);
		m_UserInfo.apszStdAttrValues[ENUM_USERATTR_LAST_NAME] = pszName;
		m_UserInfo.dwFlags |= USEROBJ_F_LAST_NAME;
	}

	if (pInfo->uOffsetEMailName != INVALID_OFFSET)
	{
		pszName = (TCHAR *) (((BYTE *) pInfo) + pInfo->uOffsetEMailName);
		m_UserInfo.apszStdAttrValues[ENUM_USERATTR_EMAIL_NAME] = pszName;
		m_UserInfo.dwFlags |= USEROBJ_F_EMAIL_NAME;
	}

	if (pInfo->uOffsetCityName != INVALID_OFFSET)
	{
		pszName = (TCHAR *) (((BYTE *) pInfo) + pInfo->uOffsetCityName);
		m_UserInfo.apszStdAttrValues[ENUM_USERATTR_CITY_NAME] = pszName;
		m_UserInfo.dwFlags |= USEROBJ_F_CITY_NAME;
	}

	if (pInfo->uOffsetCountryName != INVALID_OFFSET)
	{
		pszName = (TCHAR *) (((BYTE *) pInfo) + pInfo->uOffsetCountryName);
		m_UserInfo.apszStdAttrValues[ENUM_USERATTR_COUNTRY_NAME] = pszName;
		m_UserInfo.dwFlags |= USEROBJ_F_COUNTRY_NAME;
	}

	if (pInfo->uOffsetComment != INVALID_OFFSET)
	{
		pszName = (TCHAR *) (((BYTE *) pInfo) + pInfo->uOffsetComment);
		m_UserInfo.apszStdAttrValues[ENUM_USERATTR_COMMENT] = pszName;
		m_UserInfo.dwFlags |= USEROBJ_F_COMMENT;
	}

	if (pInfo->uOffsetIPAddress != INVALID_OFFSET)
	{
		pszName = (TCHAR *) (((BYTE *) pInfo) + pInfo->uOffsetIPAddress);
		m_UserInfo.apszStdAttrValues[ENUM_USERATTR_IP_ADDRESS] = pszName;
		m_UserInfo.dwFlags |= USEROBJ_F_IP_ADDRESS;
	}

	if (pInfo->dwFlags != INVALID_USER_FLAGS)
	{
		::GetLongString (pInfo->dwFlags, &m_UserInfo.szFlags[0]);
		m_UserInfo.apszStdAttrValues[ENUM_USERATTR_FLAGS] = &m_UserInfo.szFlags[0];
		m_UserInfo.dwFlags |= USEROBJ_F_FLAGS;
	}

	return S_OK;
}


HRESULT UlsLdap_CLocalUser::
BuildDN ( VOID )
{
	MyAssert (m_UserInfo.apszStdAttrValues[ENUM_USERATTR_CN] != NULL);

	TCHAR szDN[MAX_DN_LENGTH];
	szDN[0] = TEXT ('\0');

	TCHAR *pszDN = &szDN[0];

	if (m_UserInfo.apszStdAttrValues[ENUM_USERATTR_CN] != NULL)
	{
		wsprintf (pszDN, TEXT ("%s=%s"),
					STR_CN, m_UserInfo.apszStdAttrValues[ENUM_USERATTR_CN]);
		pszDN += lstrlen (pszDN);
	}

	if (m_UserInfo.apszStdAttrValues[ENUM_USERATTR_O] != NULL)
	{
		wsprintf (pszDN, TEXT (", %s=%s"),
					STR_O, m_UserInfo.apszStdAttrValues[ENUM_USERATTR_O]);
		pszDN += lstrlen (pszDN);
	}

	if (m_UserInfo.apszStdAttrValues[ENUM_USERATTR_C] != NULL)
	{
		wsprintf (pszDN, TEXT (", %s=%s"),
					STR_C, m_UserInfo.apszStdAttrValues[ENUM_USERATTR_C]);
		pszDN += lstrlen (pszDN);
	}

	wsprintf (pszDN, TEXT (", %s"), &c_szDefUserBaseDN[0]);

	TCHAR *psz = My_strdup (&szDN[0]);
	if (psz == NULL)
		return ULS_E_MEMORY;

	MemFree (m_pszOldDN);
	m_pszOldDN = m_pszDN;
	m_pszDN = psz;
	return S_OK;
}