#include "StdAfx.h"
#include <Winldap.h>

#define APL_NO_LDAP_DEFINE

#include "CaplLDAP.h"
#include <aplAggr.h>

CaplLDAP::CaplLDAP(void)
{
	m_pLdapConnection=0;
	bIsConnect=false;
	m_entry=0;
	m_answer=0;
	m_bALDMode=false;
}

CaplLDAP::~CaplLDAP(void)
{
	Disconnect();
}

void CaplLDAP::Disconnect()
{
	m_s_last_error=_T("");

	if(m_entry==m_answer)m_answer=0;
	if(0!=m_entry) {ldap_msgfree(m_entry); m_entry=0;}
	if(0!=m_answer) {ldap_msgfree(m_answer); m_answer=0;}

	if(0!=m_pLdapConnection) {ldap_unbind_s(m_pLdapConnection); m_pLdapConnection=0;}
	bIsConnect=false;

	m_domain_dns=_T("");
	m_domain_ldap=_T("");
	m_server=_T("");
	m_port=0;
	m_answer=0; m_entry=0;
}

LPCTSTR  CaplLDAP::GetLastError(){return LPCTSTR(m_s_last_error);};


bool CaplLDAP::GetCurrentWindowsUser(CString &user)
{
	user=_T("");
	TCHAR *sbuf=new TCHAR[1024];
	DWORD  buflen=1024;
	// User-a не определяем из окружения, а то больно просто будет подставить друго
	// а это чревато при автологоне
	BOOL b=GetUserName(sbuf,&buflen);
	if(TRUE==b) user=sbuf;  
	delete sbuf;
	if(TRUE==b) return true;
	return false;
}

bool CaplLDAP::GetCurrentWindowsDomainName(CString &domain)
{
	domain.GetEnvironmentVariable(_T("USERDNSDOMAIN"));
	if(domain==_T("")) return false;
	return true;
}

bool CaplLDAP::GetCurrentWindowsDomainServer(CString &server)
{
	server=_T("");

	CString tmp; tmp.GetEnvironmentVariable(_T("LOGONSERVER"));

	if(tmp!=_T(""))
	{
		int i=0; while(tmp[i]==_T('\\')) i++;
		server=tmp.Right(tmp.GetLength()-i);
	}
	return true;
}



//*************************************************************************************
bool CaplLDAP::Init(LPCTSTR domain, LPCTSTR server, ULONG port)  //Инициализирует LDAP, устанавливает соединение с серверм
{
	Disconnect();
	if(domain==0 || domain[0]==_T('\0')) {m_s_last_error=APL_T("Не задан домен."); return false;};

	CString buf,ldap_server=server;
	if(ldap_server==_T("")) ldap_server=domain;

	if(0==port)
	{
		int i=ldap_server.Find(_T(':'));
		if(i>0)
		{
			buf=ldap_server.Right(ldap_server.GetLength()-i);
			port=_atoi(buf);
		}
		else port=389;
	}
	
	m_pLdapConnection=ldap_init((PTCHAR)((LPCTSTR)ldap_server),port);
	if(0==m_pLdapConnection) {SetLDAPError(APL_T("Ошибка при инициализации LDAP (ldap_init).")); return false;};

	int  ldap_version   = LDAP_VERSION3;
	ldap_set_option(m_pLdapConnection, LDAP_OPT_PROTOCOL_VERSION, &ldap_version);

	ULONG connectSuccess = ldap_connect(m_pLdapConnection, NULL);
	if(0!=connectSuccess)
	{
		Disconnect();
		SetLDAPError(APL_T("Ошибка соединения с сервером LDAP (ldap_connect)."), connectSuccess);
		return false;
	}
	
	m_domain_dns=domain;
	m_server=server;
	m_port=port;

	m_domain_ldap=_T("DC=");
	m_domain_ldap+=m_domain_dns;
	m_domain_ldap.Replace(_T("."),_T(",DC="));

	return true;
}

//*************************************************************************************
bool CaplLDAP::TestUserConnect(LPCTSTR user_name,LPCTSTR password) 
{
	if(0==m_pLdapConnection)  {m_s_last_error=APL_T("Не инициализированно соединение LDAP."); return false;};
	m_s_last_error=_T("");

	if(0==user_name)
	{
		ULONG binf_rez=LDAP_OPERATIONS_ERROR;
		if(m_bALDMode)
			binf_rez = ldap_simple_bind_s( m_pLdapConnection, NULL, NULL );   // Анонимный коннект
		else
			binf_rez = ldap_bind_s(m_pLdapConnection,0,0,LDAP_AUTH_NTLM);  // Под текущим пользователем винды
			
		if(0==binf_rez)
			return true; // коннект успешен

		if(m_bALDMode)
			SetLDAPError(APL_T("Ошибка анонимного подключения."));
		else
			SetLDAPError(APL_T("Ошибка аутентификации под текущим пользователем windows."));

		return false;
	}
	if(user_name[0]==_T('\0')) return false;

	CString domain_user_name=user_name; domain_user_name+=_T("@"); domain_user_name+=m_domain_dns; //"Администратор@ad.ofc.gamma.ru";

	ULONG binf_rez=ldap_simple_bind_s(m_pLdapConnection,(PTCHAR)((LPCTSTR)domain_user_name),(PTCHAR)((LPCTSTR)password));
	if(0==binf_rez)
	{
		//ldap_unbind_s(m_pLdapConnection);
		return true; // коннект успешен
	}
	SetLDAPError(APL_T("Ошибка аутентификации. Проверьте имя пользователя или пароль."));
	return false;

	// Дальше имеет смысл, только если мы в домене

	/*
	// ищем пользователя  (чтобы определить был неверен пароль или что другое)
	CString base_dn="CN=Users,";
	base_dn+=m_domain_ldap;

	CString filter="(&(objectClass=user)(sAMAccountName=%s))"; 
	filter.Replace("%s",user_name);


	int  scope          = LDAP_SCOPE_SUBTREE;
	int  attrsonly      = 0;  //Читать все атрибуты
	LDAPMessage *answer;
	

	int entries_found=0;

	//int rez_search = ldap_search_s(pLdapConnection, base_dn, scope, "(&(objectClass=user)(sAMAccountName=test))", 0, 0, &answer);
	//int rez_search = ldap_search_s(pLdapConnection, base_dn, scope, filter, attrs, attrsonly, &answer);
	int rez_search = ldap_search_s(m_pLdapConnection,(PCHAR)((LPCSTR) base_dn), scope, (PCHAR)((LPCSTR)filter), 0, attrsonly, &answer);
	//int rez_search = ldap_search_s(pLdapConnection, base_dn, scope, filter, 0, attrsonly, &answer);
	
	if(0==rez_search) 	entries_found = ldap_count_entries(m_pLdapConnection, answer);  //Кол-во найденных записей

	if(entries_found!=1) m_s_last_error="Не найден пользователь.";
	else m_s_last_error="Неверный пароль.";

	return false;*/
}

//*************************************************************************************
bool CaplLDAP::Connect(LPCTSTR user_name,LPCTSTR password)
{
	if(0==m_pLdapConnection)  {m_s_last_error=APL_T("Не инициализированно соединение LDAP."); return false;};
	m_s_last_error=_T("");

	if(0==user_name) 
	{
		ULONG binf_rez=LDAP_OPERATIONS_ERROR;
		if(m_bALDMode)
			binf_rez = ldap_simple_bind_s( m_pLdapConnection, NULL, NULL );   // Анонимный коннект
		else
			binf_rez=ldap_bind_s(m_pLdapConnection,0,0,LDAP_AUTH_NTLM);  // Под текущим пользователем винды

		if(0==binf_rez)
		{
			bIsConnect=true;
			return true; // коннект успешен
		}

		if(m_bALDMode)
			SetLDAPError(APL_T("Ошибка анонимного подключения."));
		else
			SetLDAPError(APL_T("Ошибка аутентификации под текущим пользователем windows."));

		return false;
	}
	if(user_name[0]==_T('\0')) return false;

	CString domain_user_name=user_name; domain_user_name+=_T("@"); domain_user_name+=m_domain_dns; //"Администратор@ad.ofc.gamma.ru";

	ULONG binf_rez=ldap_simple_bind_s(m_pLdapConnection,(PTCHAR)((LPCTSTR)domain_user_name),(PTCHAR)((LPCTSTR)password));
	if(0==binf_rez)
	{
		bIsConnect=true;
		return true; // коннект успешен
	}
	SetLDAPError(APL_T("Ошибка аутентификации."));
	return false;
}


//*************************************************************************************

bool CaplLDAP::FindUser(LPCTSTR user_name)  //Ищет пользователя в домене  Возвращает 0, если не найден.
{
	if(0==m_pLdapConnection) {m_s_last_error=APL_T("Не инициализированно соединение LDAP."); return false;};
	if(!bIsConnect) {m_s_last_error=APL_T("Не установлено соединение LDAP."); return false;};

	int  scope          = LDAP_SCOPE_SUBTREE;
	CString filter        = _T("(&(objectClass=user)(sAMAccountName=%s))");
	if(m_bALDMode) filter=_T("(sn=%s)");

	filter.Replace(_T("%s"),user_name);
	TCHAR *attrs[]       = {_T("memberOf"), NULL};
	CString sBaseDN=m_domain_ldap;
	int  attrsonly      = 0;


	if(0!=m_entry) {ldap_msgfree(m_entry); m_entry=0;}
	if(0!=m_answer) {ldap_msgfree(m_answer); m_answer=0;}

	CString buf;

	int rez_search = ldap_search_s(m_pLdapConnection, (PTCHAR)LPCTSTR(sBaseDN), scope, (PTCHAR)LPCTSTR(filter), 0, attrsonly, &m_answer);
	if(0!=rez_search)
	{
		SetLDAPError(APL_T("Ошибка поиска в домене (ldap_search_s)."), rez_search);
		return false;
	} 

	int entries_found = ldap_count_entries(m_pLdapConnection, m_answer);  //Кол-во найденных записей

	bool bReturn=false;

	if(1==entries_found)
	{
		m_entry = ldap_first_entry(m_pLdapConnection, m_answer);
		bReturn=true;
	}
	else if	(0==entries_found) m_s_last_error=APL_T("Пользователь не найден");
	else m_s_last_error.Format(APL_T("Таких пользователей несколько (%i) "), entries_found);

	return bReturn;
}

//*************************************************************************************

bool CaplLDAP::GetObjectAttr(LPCTSTR attr,CString &value) // Читает атрибут ранее найденного объекта/
{
	value=_T("");
	if(0==m_pLdapConnection) {m_s_last_error=APL_T("Не инициализированно соединение LDAP."); return false;};
	if(!bIsConnect) {m_s_last_error=APL_T("Не установлено соединение LDAP."); return false;};
	if(0==attr) {m_s_last_error=APL_T("Не указан атрибут"); return false;};
	if(0==m_entry) {m_s_last_error=APL_T("Нет объекта"); return false;};

	TCHAR **values;
	int i;

	if ((values = ldap_get_values(m_pLdapConnection, m_entry, (PTCHAR)LPCTSTR(attr) )) != NULL)
	{
		for (i = 0; values[i] != NULL; i++) 
		{
			if(value!=_T("")) value+=_T(";");
			value+=values[i];
		}
		ldap_value_free(values);
		return true;
	}
	SetLDAPError(APL_T("Не найдено значение заданного атрибута."));
	return true; 
}

//*************************************************************************************
bool CaplLDAP::GetUserInfo(LPCTSTR user_name,CString &info) 
{
	info=_T("");

	if(0==m_pLdapConnection) {m_s_last_error=APL_T("Не инициализированно соединение LDAP."); return false;};
	if(!bIsConnect) {m_s_last_error=APL_T("Не установлено соединение LDAP."); return false;};

	CString buf;

	int  scope          = LDAP_SCOPE_SUBTREE;
	CString filter        = _T("(&(objectClass=user)(sAMAccountName=%s))");
	if(m_bALDMode) filter=_T("(sn=%s)");

	filter.Replace(_T("%s"),user_name);
	//char *attrs[]       = {"memberOf", NULL};

	//char *base_dn       = "CN=Users,DC=ad,DC=ofc,DC=cals,DC=ru";
	//CString sBaseDN="CN=Users,";  sBaseDN+=m_domain_ldap;  // Не сработало на КВЗ
	CString sBaseDN;  sBaseDN+=m_domain_ldap;
	int  attrsonly      = 0;
	LDAPMessage *answer, *entry;

	int rez_search = ldap_search_s(m_pLdapConnection, (PTCHAR)LPCTSTR(sBaseDN), scope, (PTCHAR)LPCTSTR(filter), 0, attrsonly, &answer);
	if(0!=rez_search)
	{
		SetLDAPError(APL_T("Ошибка поиска пользователя в домене (ldap_search_s)."), rez_search);
		return false;
	} 
	else info+=_T("ldap_search_s - ok\r\n");

	info+=_T("\r\n\r\nsBaseDN:");info+=sBaseDN;
	info+=_T("\r\nfilter:");info+=filter;
	info+=_T("\r\n\r\n");

	int entries_found = ldap_count_entries(m_pLdapConnection, answer);  //Кол-во найденных записей
	buf.Format(APL_T("\r\nНайдено %i записей"), entries_found);
	info+=buf; 

	int i,i_entry=0;
	for ( entry = ldap_first_entry(m_pLdapConnection, answer); 	entry != NULL; 		entry = ldap_next_entry(m_pLdapConnection, entry))
	{
		i_entry++;
		PTCHAR dn = ldap_get_dn(m_pLdapConnection, entry);
		buf.Format(_T("\r\n\r\n%i: %s\r\n"),i_entry, dn);
		info+=buf;

		PTCHAR attribute;
		BerElement *ber;
		TCHAR **values;

		for ( attribute = ldap_first_attribute(m_pLdapConnection, entry, &ber); 	attribute != NULL;  attribute = ldap_next_attribute(m_pLdapConnection, entry, ber)) 
		{
			buf=attribute;
			if(buf==_T("memberOf"))
			{
				buf=_T("");
			}
			
			if ((values = ldap_get_values(m_pLdapConnection, entry, attribute)) != NULL)
			{
				for (i = 0; values[i] != NULL; i++) {

					buf.Format(_T("   %s: %s\r\n"), attribute, values[i] );
					info+=buf;
				}
				ldap_value_free(values);
			}
		}
		ldap_memfree(dn);
	}
	ldap_msgfree(answer);
	ldap_msgfree(entry);
	return true;
}



//*************************************************************************************
bool CaplLDAP::Search(LPCTSTR BaseDN, LPCTSTR filter, CString &info) 
{
	info=_T("");

	if(0==m_pLdapConnection) {m_s_last_error=APL_T("Не инициализированно соединение LDAP."); return false;};
	if(!bIsConnect) {m_s_last_error=APL_T("Не установлено соединение LDAP."); return false;};

	info+=_T("\r\n\r\nsBaseDN:");info+=BaseDN;
	info+=_T("\r\nfilter:");info+=filter;
	info+=_T("\r\n\r\n");

	CString buf;
	int  scope          = LDAP_SCOPE_SUBTREE;
	int  attrsonly      = 0;
	LDAPMessage *answer, *entry;

	int rez_search = ldap_search_s(m_pLdapConnection, (PTCHAR)BaseDN, scope, (PTCHAR)LPCTSTR(filter), 0, attrsonly, &answer);
	if(0!=rez_search)
	{
		SetLDAPError(APL_T("Ошибка поиска в домене (ldap_search_s)."), rez_search);
		return false;
	} 
	else info+=_T("ldap_search_s - ok\r\n");

	int entries_found = ldap_count_entries(m_pLdapConnection, answer);  //Кол-во найденных записей
	buf.Format(APL_T("\r\nНайдено %i записей"), entries_found);
	info+=buf; 

	int i,i_entry=0;
	for ( entry = ldap_first_entry(m_pLdapConnection, answer); 	entry != NULL; 		entry = ldap_next_entry(m_pLdapConnection, entry))
	{
		i_entry++;
		PTCHAR dn = ldap_get_dn(m_pLdapConnection, entry);
		buf.Format(_T("\r\n\r\n%i: %s\r\n"),i_entry, dn);
		info+=buf;

		PTCHAR attribute;
		BerElement *ber;
		TCHAR **values;

		for ( attribute = ldap_first_attribute(m_pLdapConnection, entry, &ber); 	attribute != NULL;  attribute = ldap_next_attribute(m_pLdapConnection, entry, ber)) 
		{
			buf=attribute;
			if(buf==_T("memberOf"))
			{
				buf=_T("");
			}
			
			if ((values = ldap_get_values(m_pLdapConnection, entry, attribute)) != NULL)
			{
				for (i = 0; values[i] != NULL; i++) {

					buf.Format(_T("   %s: %s\r\n"), attribute, values[i] );
					info+=buf;
				}
				ldap_value_free(values);
			}
		}
		ldap_memfree(dn);
	}
	ldap_msgfree(answer);
	ldap_msgfree(entry);
	return true;
}

#define ERR_2_TEXT(par,text) case par : descr+= _T(" ") _T(#par) _T(": ") text; break;

CString CaplLDAP::GetLDAPErrorDescr(ULONG errcode)
{
	CString descr(_T(" LDAP error"));

	switch(errcode)
	{
		ERR_2_TEXT(LDAP_ADMIN_LIMIT_EXCEEDED, _T("Administration limit on the server was exceeded."));//0x0b
		ERR_2_TEXT(LDAP_AFFECTS_MULTIPLE_DSAS, _T("Multiple directory service agents are affected."));//0x47
		ERR_2_TEXT(LDAP_ALIAS_DEREF_PROBLEM, _T("Cannot dereference the alias."));//0x24
		ERR_2_TEXT(LDAP_ALIAS_PROBLEM, _T("The alias is invalid."));//0x21
		ERR_2_TEXT(LDAP_ALREADY_EXISTS, _T("The object already exists."));//0x44
		ERR_2_TEXT(LDAP_ATTRIBUTE_OR_VALUE_EXISTS, _T("The attribute exists or the value has been assigned."));//0x14
		ERR_2_TEXT(LDAP_AUTH_METHOD_NOT_SUPPORTED, _T("The authentication method is not supported. To determine the authentication methods supported by an Active Directory server, retrieve the SupportedSASLMechanisms property of rootDSE. For more information, see Serverless Binding and RootDSE."));//0x07
		ERR_2_TEXT(LDAP_AUTH_UNKNOWN, _T("Unknown authentication error occurred."));//0x56
		ERR_2_TEXT(LDAP_BUSY, _T("The server is busy."));//0x33
		ERR_2_TEXT(LDAP_CLIENT_LOOP, _T("Client loop was detected."));//0x60
		ERR_2_TEXT(LDAP_COMPARE_FALSE, _T("For ldap_compare_ext_s and ldap_compare_s, this message is returned if the function succeeds, and the attribute and known values do not match."));//0x05
		ERR_2_TEXT(LDAP_COMPARE_TRUE, _T("For ldap_compare_ext_s and ldap_compare_s, this message is returned if the function succeeds, and the attribute and known values match."));//0x06
		ERR_2_TEXT(LDAP_CONFIDENTIALITY_REQUIRED, _T("Confidentiality is required."));//0x0d
		ERR_2_TEXT(LDAP_CONNECT_ERROR, _T("Cannot establish the connection."));//0x5b
		ERR_2_TEXT(LDAP_CONSTRAINT_VIOLATION, _T("There was a constraint violation."));//0x13
		ERR_2_TEXT(LDAP_CONTROL_NOT_FOUND, _T("The LDAP function (ldap_parse_page_control, ldap_parse_sort_control, or ldap_parse_vlv_control) did not find the specified control."));//0x5d
		ERR_2_TEXT(LDAP_DECODING_ERROR, _T("Decoding error occurred."));//0x54
		ERR_2_TEXT(LDAP_ENCODING_ERROR, _T("Encoding error occurred."));//0x53
		ERR_2_TEXT(LDAP_FILTER_ERROR, _T("The search filter is bad."));//0x57
		ERR_2_TEXT(LDAP_INAPPROPRIATE_AUTH, _T("Authentication is inappropriate."));//0x30
		ERR_2_TEXT(LDAP_INAPPROPRIATE_MATCHING, _T("There was an inappropriate matching."));//0x12
		ERR_2_TEXT(LDAP_INSUFFICIENT_RIGHTS, _T("The user has insufficient access rights."));//0x32
		ERR_2_TEXT(LDAP_INVALID_CREDENTIALS, _T("The supplied credential is invalid."));//0x31
		ERR_2_TEXT(LDAP_INVALID_DN_SYNTAX, _T("The distinguished name has an invalid syntax."));//0x22
		ERR_2_TEXT(LDAP_INVALID_SYNTAX, _T("The syntax is invalid."));//0x15
		ERR_2_TEXT(LDAP_IS_LEAF, _T("The object is a leaf."));//0x23
		ERR_2_TEXT(LDAP_LOCAL_ERROR, _T("Local error occurred. If this error occurs during a binding operation, for more information, see ldap_bind_s."));//0x52
		ERR_2_TEXT(LDAP_LOOP_DETECT, _T("The chain of referrals has looped back to a referring server."));//0x36
		ERR_2_TEXT(LDAP_MORE_RESULTS_TO_RETURN, _T("More results are to be returned."));//0x5f
		ERR_2_TEXT(LDAP_NAMING_VIOLATION, _T("There was a naming violation."));//0x40
		ERR_2_TEXT(LDAP_NO_MEMORY, _T("The system is out of memory."));//0x5a
		ERR_2_TEXT(LDAP_NO_OBJECT_CLASS_MODS, _T("Cannot modify object class."));//0x45
		ERR_2_TEXT(LDAP_NO_RESULTS_RETURNED, _T("Results are not returned."));//0x5e
		ERR_2_TEXT(LDAP_NO_SUCH_ATTRIBUTE, _T("Requested attribute does not exist."));//0x10
		ERR_2_TEXT(LDAP_NO_SUCH_OBJECT, _T("Object does not exist."));//0x20
		ERR_2_TEXT(LDAP_NOT_ALLOWED_ON_NONLEAF, _T("Operation is not allowed on a nonleaf object."));//0x42
		ERR_2_TEXT(LDAP_NOT_ALLOWED_ON_RDN, _T("Operation is not allowed on RDN."));//0x43
		ERR_2_TEXT(LDAP_NOT_SUPPORTED, _T("The feature is not supported."));//0x5c
		ERR_2_TEXT(LDAP_OBJECT_CLASS_VIOLATION, _T("There was an object class violation."));//0x41
		ERR_2_TEXT(LDAP_OPERATIONS_ERROR, _T("Operations error occurred."));//0x01
		ERR_2_TEXT(LDAP_OTHER, _T("Unknown error occurred."));//0x50
		ERR_2_TEXT(LDAP_PARAM_ERROR, _T("A bad parameter was passed to a routine."));//0x59
		ERR_2_TEXT(LDAP_PARTIAL_RESULTS, _T("Partial results and referrals received."));//0x09
		// 	Note: Same error code as LDAP_REFERRAL_V2. The server returns the same result code for these two similar instances, v2 referral and continuation references.
		// 	  For further information, see the protocol reference, Referrals in LDAPv2 and LDAPv3.
		ERR_2_TEXT(LDAP_PROTOCOL_ERROR, _T("Protocol error occurred."));//0x02
		ERR_2_TEXT(LDAP_REFERRAL, _T("A referral was returned from the server."));//0x0a
		ERR_2_TEXT(LDAP_REFERRAL_LIMIT_EXCEEDED, _T("The referral limit has been exceeded."));//0x61
		ERR_2_TEXT(LDAP_RESULTS_TOO_LARGE, _T("Results returned are too large."));//0x46
		ERR_2_TEXT(LDAP_SERVER_DOWN, _T("Cannot contact the LDAP server."));//0x51
		ERR_2_TEXT(LDAP_SIZELIMIT_EXCEEDED, _T("Size limit was exceeded."));//0x04
		ERR_2_TEXT(LDAP_STRONG_AUTH_REQUIRED, _T("Strong authentication is required."));//0x08
		ERR_2_TEXT(LDAP_SUCCESS, _T("The call completed successfully."));//0x00
		ERR_2_TEXT(LDAP_TIMELIMIT_EXCEEDED, _T("Time limit, set by the server side time limit parameter, was exceeded."));//0x03
		ERR_2_TEXT(LDAP_TIMEOUT, _T("The search was aborted due to exceeding the limit of the client side timeout parameter."));//0x55
		ERR_2_TEXT(LDAP_UNAVAILABLE, _T("The server is unavailable."));//0x34
		ERR_2_TEXT(LDAP_UNAVAILABLE_CRIT_EXTENSION, _T("The control is critical and the server does not support the control."));//0x0c
		ERR_2_TEXT(LDAP_UNDEFINED_TYPE, _T("Type is not defined."));//0x11
		ERR_2_TEXT(LDAP_UNWILLING_TO_PERFORM, _T("The server is not willing to handle directory requests."));//0x35
		ERR_2_TEXT(LDAP_USER_CANCELLED, _T("The user has canceled the operation."));//0x58
		ERR_2_TEXT(LDAP_VIRTUAL_LIST_VIEW_ERROR, _T("An error occurred when attempting to perform a requested Virtual List View operation. A detailed error code is returned in the ldctl_value field of the LDAP_CONTROL_VLVRESPONSE control."));//0x4c
	default:
		descr.Format(_T(" 0x%x: "), errcode); 
		descr += ldap_err2string(errcode);
		break;
	}
	return descr;
}

	  
void CaplLDAP::SetLDAPError(LPCTSTR mess)
{
	SetLDAPError(mess, LdapGetLastError());

}
void CaplLDAP::SetLDAPError(LPCTSTR mess, ULONG code)
{
	m_s_last_error = mess;
	m_s_last_error += GetLDAPErrorDescr(code);
}

