Click here to Skip to main content
15,868,420 members
Articles / Programming Languages / C++

.NET Client Classes for openldap/winldap

Rate me:
Please Sign up or sign in to vote.
4.88/5 (17 votes)
11 Mar 2003CPOL5 min read 245K   2.3K   62  
An example of using LDAP/OpenLDAP with .Net
/**************************************************************************
   THIS CODE AND INFORMATION IS PROVIDED 'AS IS' WITHOUT WARRANTY OF
   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
   PARTICULAR PURPOSE.
   Author: Leon Finker  9/2002
**************************************************************************/
#include "common.h"
#include "ldap_net.h"
#include "helpers.h"

namespace ldap_net
{
////////////////////////////////////////////////////////////////////////
// LdapAttribute
String* LdapAttribute::get_StringValues()[]
{ 
	CIntPtrStringAnsi ptrAttribname(m_attribname);
	char** vals = ::ldap_get_values(m_ldap, m_start, ptrAttribname);
	ptrAttribname.Free();
	Collections::Specialized::StringCollection* sc = new Collections::Specialized::StringCollection();
	for(int i=0; vals[i] != 0; ++i)
		sc->Add(new String(vals[i]));

	ldap_value_free(vals);

	String* sa[] = new String*[sc->Count];
	sc->CopyTo(sa, 0);
	sc->Clear();
	return sa;
}
/* get_BinaryValues */
Array* LdapAttribute::get_BinaryValues()
{ 
	CIntPtrStringAnsi ptrAttribname(m_attribname);
	berval** vals = ldap_get_values_len(m_ldap, m_start, ptrAttribname);
	ptrAttribname.Free();
	Array* arr[] = new Array*[ldap_count_values_len(vals)];
	for(int i=0; vals[i] != 0; ++i)
	{
		Byte ba[] = new Byte[vals[i]->bv_len];
		Marshal::Copy(IntPtr(vals[i]->bv_val), ba, 0, ba->Length);
		arr[i]=ba;
	}

	ldap_value_free_len(vals);
	return arr;
}
/* MoveNext */
bool LdapAttribute::MoveNext()
{  
	char* attrn;
	if(m_current == 0)
	{
		BerElement* temp;
		attrn = ldap_first_attribute(m_ldap, m_start, &temp);
		m_current = temp;
	}
	else
	{
		attrn = ldap_next_attribute(m_ldap, m_start, m_current);
	}
	if(attrn != 0)
	{
		m_attribname = Marshal::PtrToStringAnsi(attrn);
		ldap_memfree(attrn);
	}
	else
	{
		if(m_current != 0)
			ber_free(m_current, 0);
		
		m_current = 0;
		return false;
	}
	return true;
}

////////////////////////////////////////////////////////////////////////
// LdapEntry

bool LdapEntry::MoveNext()
{
	if(m_current == 0)
		m_current = ldap_first_entry(m_ldap, m_start);
	else
		m_current = ldap_next_entry(m_ldap,m_current);
	if(m_current != 0)
	{
		char* dn = ldap_get_dn(m_ldap, m_current);
		if(dn != 0)
		{
			m_dn = Marshal::PtrToStringAnsi(dn);
			ldap_memfree(dn);
		}
		return true;
	}
	else
		return false;
}

///////////////////////////////////////////////////////////////////////////////
// LDAPModify
// WHAT a f MESS this is!!!
//might have a bug, be carefull
LDAPModify::LDAPModify(const char* dn, ListDictionary * attrvals, long op)
{
	Collections::IDictionaryEnumerator* dicenum = attrvals->GetEnumerator();
	IntPtr ptrMods = Marshal::AllocCoTaskMem((attrvals->Count+1) * sizeof(::LDAPMod*)); //+1 for null
	/*1*/ppMods = (LDAPMod**)ptrMods.ToPointer();
	for(int i=0; i< attrvals->Count; ++i)
		/*2*/ppMods[i] = (::LDAPMod*)Marshal::AllocCoTaskMem(sizeof(::LDAPMod)).ToPointer();

	for(int i=0; dicenum->MoveNext(); ++i)
	{
		String* skey=dynamic_cast<String*>(dicenum->Key);
		if(skey == 0)
			throw new InvalidCastException(S"Invalid type for attribute name, String expected");
		
		ppMods[i]->mod_op = op;
		bool binary=false;
		
		String* svalarr[]= dynamic_cast<String*[]>(dicenum->Value);
		Object* barr[]   = dynamic_cast<Object*[]>(dicenum->Value);
		Byte ba[]		 = dynamic_cast<Byte[]>(dicenum->Value);
		String* sval	 = dynamic_cast<String*>(dicenum->Value);

		int nValsCount = 0;
		if(svalarr != 0)
		{
			binary = false;
			nValsCount = svalarr->Count;
		}
		else if(barr != 0) //check has to be after svalarr
		{
			binary = true;
			nValsCount = barr->Count;
		}
		else if(sval != 0)
		{
			binary = false;
			nValsCount = 1;
		}
		else if(ba != 0)
		{
			binary = true;
			nValsCount = 1;
		}
		else
		{
			if(dicenum->Value != 0)
				throw new InvalidCastException(String::Concat("Expected String, String[], Array of Byte[], or Byte[] for values but got ", dicenum->Value->GetType()->ToString()));
		}
		
		ppMods[i]->mod_op |= LDAP_MOD_BVALUES;
		/*3*/ppMods[i]->mod_type=(char*)Marshal::StringToCoTaskMemAnsi(skey).ToPointer();
		
		/*4*/ppMods[i]->mod_vals.modv_bvals = (berval**)Marshal::AllocCoTaskMem((nValsCount+1) * sizeof(berval*)).ToPointer(); //+1 for null;
		for(int j=0; j < nValsCount; ++j)
		{
			/*5*/ppMods[i]->mod_vals.modv_bvals[j]=(berval*)Marshal::AllocCoTaskMem(sizeof(berval)).ToPointer();
			char* pTemp=0;
			int nLen = 0;
			if(!binary)
			{
				if(sval)
					pTemp = (char*)Marshal::StringToCoTaskMemAnsi(sval).ToPointer();
				else
					pTemp = (char*)Marshal::StringToCoTaskMemAnsi(svalarr[j]).ToPointer();
				nLen = strlen(pTemp);
			}
			else
			{
				Byte baTemp[];
				if(ba != 0)
				{
					baTemp = ba;
					nLen = baTemp->Count;
					pTemp = (char*)Marshal::AllocCoTaskMem(nLen).ToPointer();
					Marshal::Copy(baTemp, 0, IntPtr(pTemp), nLen);
				}
				else
				{
					if(dynamic_cast<Byte[]>(barr[j]) == 0)
						throw new InvalidCastException(String::Concat("Expected Byte[] for values but got ", dicenum->Value->GetType()->ToString()));
					baTemp = (Byte[])barr[j];
					nLen = baTemp->Count;
					pTemp = (char*)Marshal::AllocCoTaskMem(nLen).ToPointer();
					Marshal::Copy(baTemp, 0, IntPtr(pTemp), nLen);
				}
			}
			/*6*/ppMods[i]->mod_vals.modv_bvals[j]->bv_val=pTemp;
			ppMods[i]->mod_vals.modv_bvals[j]->bv_len = nLen;
		}
		ppMods[i]->mod_vals.modv_bvals[nValsCount]=0;//must null terminate for ldap
	}
	ppMods[attrvals->Count] = 0; //need to null terminate for ldap
}

/* ~LDAPModify */
LDAPModify::~LDAPModify()
{
	for(int i=0; ppMods[i] != 0; ++i)
	{
		for(int j=0; ppMods[i]->mod_vals.modv_bvals[j] != 0; ++j)
		{
			/*6*/Marshal::FreeCoTaskMem(IntPtr(ppMods[i]->mod_vals.modv_bvals[j]->bv_val));
			/*5*/Marshal::FreeCoTaskMem(IntPtr(ppMods[i]->mod_vals.modv_bvals[j]));
		}
		
		/*4*/Marshal::FreeCoTaskMem(IntPtr(ppMods[i]->mod_vals.modv_bvals));

		/*3*/Marshal::FreeCoTaskMem(IntPtr(ppMods[i]->mod_type));
		/*2*/Marshal::FreeCoTaskMem(IntPtr(ppMods[i]));
	}
	/*1*/Marshal::FreeCoTaskMem(ppMods);
}

/////////////////////////////////////////////////////////////////////////////////
// LdapClient
LdapClient::LdapClient(String* HostName, UInt32 port, bool prot_ver3, bool ssl)
{
	CIntPtrStringAnsi ptrHost(HostName);
	
#ifdef OPENLDAP_NET
	m_ldap = ::ldap_init(ptrHost, port);	
	//LDAP* temp; //this code is needed for connecting with ssl to AD, because ldap_start_tls_s won't work on win2k
	              //but ldap_start_tls_s should work with windows 2003 and AD
	//ldap_initialize(&temp, (char*)ptr.ToPointer()); //for uri based hostname i.e. ldaps://localhost (ssl)
	//m_ldap = ldap;
#else
	m_ldap = ::ldap_sslinit(ptrHost, port, ssl);	
#endif

	ptrHost.Free();

	if(m_ldap == 0)
		GetLDAPErrorThrow(0);
	if(prot_ver3)
	{
		LDAPINT ver = LDAP_VERSION3;
		LDAPINT nRet = ::ldap_set_option(m_ldap, LDAP_OPT_PROTOCOL_VERSION, &ver);
		//MS specific: nRet = ::ldap_set_option(m_ldap, LDAP_OPT_SERVER_CERTIFICATE, &ServerCertCallback);
		if(nRet != LDAP_SUCCESS)
			GetLDAPErrorThrow(nRet);
	}
#ifdef OPENLDAP_NET
	if(ssl)
	{
		//when using winldap this function is only available on winxp+
		LDAPINT nRet = ::ldap_start_tls_s(m_ldap,0,0);
		if(nRet != LDAP_SUCCESS)
			GetLDAPErrorThrow(nRet);
	}
#endif

}

/* get_DefaultSSLPort */
UInt32 LdapClient::get_DefaultSSLPort() 
{ 
#ifdef OPENLDAP_NET
	return LDAPS_PORT;
#else
	return LDAP_SSL_PORT;
#endif
}

/* ldap_search_s */
Int32 LdapClient::ldap_search_ext_s(String* base, Misc::LDAPSearchScope scope, String* filter, String* attrs[], bool attrsonly, int timeoutsecs, int sizelimit, [Out] LdapResult** res)
{
	CIntPtrStringAnsi ptrBase(base);
	CIntPtrStringAnsi ptrFilter(filter);
	LDAPMessage *msg=0;

	char** pAttrs = 0;
	if(attrs->Count > 0)
	{
		pAttrs = (char**)(Marshal::AllocCoTaskMem((attrs->Count+1) * sizeof(char*)).ToPointer());
		for(int i=0; i<attrs->Count; ++i)
		{
			IntPtr ptr = Marshal::StringToCoTaskMemAnsi(attrs[i]);
			pAttrs[i] = (char*)ptr.ToPointer();
		}
		pAttrs[attrs->Count] = 0;
	}

	//LDAPINT nRet = ::ldap_search_s(m_ldap, ptrBase, scope, ptrFilter, pAttrs, attrsonly, &msg);
#ifdef WINLDAP_NET
	l_timeval time={0};
#elif OPENLDAP_NET
	timeval	  time={0};
#endif
	time.tv_sec = timeoutsecs;
	LDAPINT nRet = ::ldap_search_ext_s(m_ldap, ptrBase, scope, ptrFilter, pAttrs, attrsonly, 0, 0, &time, sizelimit, &msg);
	ptrBase.Free();
	ptrFilter.Free();
	if(attrs->Count > 0)
	{
		for(int i=0; i < attrs->Count; ++i)
		{
			char* p = pAttrs[i];
			Marshal::FreeCoTaskMem(IntPtr(p));
			pAttrs[i]=0;
		}
		Marshal::FreeCoTaskMem(pAttrs);
	}
	if(nRet != LDAP_SUCCESS && nRet != LDAP_PARTIAL_RESULTS && nRet != LDAP_SIZELIMIT_EXCEEDED)
	{
		if(msg)
			ldap_msgfree(msg);
		GetLDAPErrorThrow(nRet);
	}
	LDAPINT nCount = ldap_count_entries(m_ldap, msg);
	*res = new LdapResult(m_ldap, msg);
	if(nRet == LDAP_PARTIAL_RESULTS || nRet == LDAP_SIZELIMIT_EXCEEDED)
		throw new Exceptions::LDAPExceptionPartialResult(nCount);
	return nCount;
}

/* ldap_add_s */
void LdapClient::ldap_add_s(String* dn, ListDictionary * attrvals)
{
#ifdef _DEBUG
	Diagnostics::Debug::Assert(attrvals->Count > 0,"Nothing to add.");
#endif
	CIntPtrStringAnsi ptrDN(dn);
	LDAPModify mod(ptrDN, attrvals, LDAP_MOD_ADD);
	LDAPINT nRet = ::ldap_add_s(m_ldap, ptrDN, mod.ppMods);
	ptrDN.Free();
	if(nRet != LDAP_SUCCESS)
		GetLDAPErrorThrow(nRet);
}

/* ldap_compare_s */
bool LdapClient::ldap_compare_s(String* dn, String* attr, String* val)
{
	CIntPtrStringAnsi ptrDN(dn);
	CIntPtrStringAnsi ptrAttr(attr);
	CIntPtrStringAnsi ptrVal(val);
	LDAPINT nRet = ::ldap_compare_s(m_ldap, ptrDN, ptrAttr, ptrVal);
	ptrDN.Free();
	ptrAttr.Free();
	ptrVal.Free();
	if(nRet != LDAP_COMPARE_TRUE && nRet != LDAP_COMPARE_FALSE)
		GetLDAPErrorThrow(nRet);

	return nRet == LDAP_COMPARE_TRUE;
}

/* ldap_delete_s */
void LdapClient::ldap_delete_s(String* dn)
{
	CIntPtrStringAnsi ptrDN(dn);
	LDAPINT nRet = ::ldap_delete_s(m_ldap, ptrDN);
	ptrDN.Free();
	if(nRet != LDAP_SUCCESS)
		GetLDAPErrorThrow(nRet);
}

/* ldap_simple_bind_s */
void LdapClient::ldap_simple_bind_s(String* who, String* passwd)
{
	CIntPtrStringAnsi ptrWho(who);
	CIntPtrStringAnsi ptrPasswd(passwd);
	LDAPINT nRet = ::ldap_simple_bind_s(m_ldap, ptrWho, ptrPasswd);
	ptrWho.Free();
	ptrPasswd.Free();
	if(nRet != LDAP_SUCCESS)
		GetLDAPErrorThrow(nRet);
}

/*ldap_modify_s*/
void LdapClient::ldap_bind_s(String* who, String* passwd, Misc::AuthMethod method)
{
	CIntPtrStringAnsi ptrWho(who);
	CIntPtrStringAnsi ptrPasswd(passwd);
	LDAPINT nRet = ::ldap_bind_s(m_ldap, ptrWho, ptrPasswd, method);
	ptrWho.Free();
	ptrPasswd.Free();
	if(nRet != LDAP_SUCCESS)
		GetLDAPErrorThrow(nRet);	
}

/*ldap_modify_s*/
void LdapClient::ldap_modify_s(String* dn, ListDictionary * attrvals, long op)
{
#ifdef _DEBUG
	Diagnostics::Debug::Assert(attrvals->Count > 0,"Nothing to modify.");
#endif
	CIntPtrStringAnsi ptrDN(dn);
	LDAPModify mod(ptrDN, attrvals, op);
	LDAPINT nRet = ::ldap_modify_s(m_ldap, ptrDN, mod.ppMods);
	ptrDN.Free();
	if(nRet != LDAP_SUCCESS)
		GetLDAPErrorThrow(nRet);
}

/*GetLDAPErrorThrow*/
void LdapClient::GetLDAPErrorThrow(LDAPINT nErr)
{
	if(nErr == 0)
		::ldap_get_option(m_ldap, LDAP_OPT_ERROR_NUMBER, &nErr);
	char* pError = ::ldap_err2string(nErr);
	if(pError == 0)
		pError = "unknown error";
	throw new Exceptions::LDAPException(pError);
}

}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions