Windows2003-3790/inetcore/outlookexpress/wabw/wabapi/ldapcont.c
2020-09-30 16:53:55 +02:00

8808 lines
297 KiB
C

//*******************************************************************
//
// Copyright(c) Microsoft Corporation, 1996
//
// FILE: LDAPCONT.C
//
// PURPOSE: IMAPIContainer implementation for WAB's LDAP container.
//
// HISTORY:
// 96/07/08 markdu Created.
// 96/08/05 markdu BUG 34023 Always start with a filter that causes
// the search to return only objects of class 'person'.
// If organization is supplied, add it to the base of the
// search instead of the filter to narrow the scope.
// 96/08/06 markdu Changed FindRow to always return the first row.
// This is OK in our current implementation since FindRow
// is used only to create and fill a new table.
// 96/08/07 markdu BUG 34201 If search fails due to undefined
// attribute type, try a second search with different
// attributes.
// 96/08/07 markdu BUG 35326 Added attribute mappings for address.
// 96/08/08 markdu BUG 35481 If search returns no error and no
// results, treat this as "not found".
// 96/08/09 markdu BUG 35604 Use ReAllocString to make sure string
// buffers are large enough to hold the required strings
// before doing wsprintf or lstrcpy.
// 96/09/28 markdu BUG 36766 If the LDAP server says there are
// multiple matches for ResolveName, mark the entry as
// ambiguous if we got some back (these will be displayed
// in the check names box). If we got no results back,
// mark the entry as resolved so we don't display an empty
// list of names.
// 96/09/29 markdu BUG 36529 Search for "OfficePager" attribute
// instead of "pager" since we don't have home pager in the UI.
// 96/09/29 markdu BUG 36528 Fix mappings of home/office fax numbers.
// 96/09/29 markdu BUG 37425 Add "mail" to the simple search.
// 96/10/02 markdu BUG 37426 If the search filter does not include
// common name, add to the filter so that we only get entries
// that have a common name. Otherwise the entry will not be
// displayed.
// 96/10/02 markdu BUG 37424 Improve simple search by breaking the
// search string into multiple components.
// 96/10/09 vikramm - extended the LDAPServerParam structure and
// modified the corresponding Get/Set functions. Added server
// IDs to the server entries
// 96/10/18 markdu Rewrote ParseSRestriction.
// 96/11/10 markdu BUG 9735 Use global handle for cancel dialog.
// 96/11/17 markdu BUG 9846 Only enable the cancel dialog when it is displayed.
// 96/11/18 vikramm - updated params to FixDisplayName
// 96/11/20 markdu Use synchronous bind for Sicily authentication.
// 96/11/22 markdu BUG 10539 Replace dollar signs with line feeds in postalAddress.
// 96/11/27 markdu BUG 6779 Get alternate email addresses.
// 96/11/28 markdu BUG 6779 Do not add primary email address to contacts
// list if it is already present. Also, if not primary address is
// returned, but we have a contacts list, copy one from there to primary.
// 96/12/03 markdu BUG 11941 Don't call lstrlen on NULL pointers.
// 96/12/03 markdu BUG 11924 Restructure return values for cancel dialog.
// 96/12/04 markdu BUG 11923 Escape invalid characters in search filters.
// Also changed all ++ operations on strings to CharNext().
// 96/12/09 markdu BUG 10537 Map error codes returned from ldap_bind to indicate
// that the problem was with the logon credentials.
// 96/12/10 markdu BUG 12699 Call CharNext before setting char to null.
// 96/12/14 markdu Got rid of globals gfUseSynchronousBind and ghDlgCancel.
// 96/12/19 markdu Post- code review clean up.
// 96/12/19 markdu BUG 12608 Commented out temporary work-around for 10537.
// 96/12/22 markdu BUG 11785 Replace wsprintf with lstrcat.
//
// 97/04/30 vikramm Added labeledURI attribute
// 97/05/19 vikramm Exchange DS wont return display-name attribute
// 97/05/19 vikramm Add username,passowrd for ISV DS binding
//*******************************************************************
#include "_apipch.h"
#define LDAP_NTDS_ENTRY 0x80000000 // When doing LDAP searches, we need to identify if certain entries originate
// on an NTDS server so that we can mark them accordingly when corresponding
// LDAP URLs are passed to extension property sheets .. this enables the NTDS
// extension sheets to make performance optimizations as appropriate
// This flag is saved on the LDAP entryID as part of the ulcNumProps param ..
// It's a bit of a hack but requires least number of changes ...just have to be
// careful to negate this flag before using the ulcNumProps ..
void SetAccountStringAW(LPTSTR * lppAcctStr,LPSTR lpszData);
void SetAccountStringWA(LPSTR szStrA, LPTSTR lpszData, int cbsz);
static const TCHAR szBindDNMSFTUser[] = TEXT("client=MS_OutlookAddressBook,o=Microsoft,c=US"); //NULL;
static const TCHAR szBindCredMSFTPass[] = TEXT("wabrules"); //NULL;
extern HRESULT HrGetLDAPSearchRestriction(LDAP_SEARCH_PARAMS LDAPsp, LPSRestriction lpSRes);
static const LPTSTR szNULLString = TEXT("NULL");
// Global handle for LDAP client DLL
HINSTANCE ghLDAPDLLInst = NULL;
ULONG ulLDAPDLLRefCount = 0;
// DLL instance handle for account manager dll
static HINSTANCE g_hInstImnAcct = NULL;
// Global place to store the account manager object
IImnAccountManager2 * g_lpAccountManager = NULL;
// LDAP jump table is defined here...
LDAPCONT_Vtbl vtblLDAPCONT =
{
VTABLE_FILL
(LDAPCONT_QueryInterface_METHOD *) IAB_QueryInterface,
(LDAPCONT_AddRef_METHOD *) WRAP_AddRef,
(LDAPCONT_Release_METHOD *) CONTAINER_Release,
(LDAPCONT_GetLastError_METHOD *) IAB_GetLastError,
(LDAPCONT_SaveChanges_METHOD *) WRAP_SaveChanges,
(LDAPCONT_GetProps_METHOD *) WRAP_GetProps,
(LDAPCONT_GetPropList_METHOD *) WRAP_GetPropList,
(LDAPCONT_OpenProperty_METHOD *) CONTAINER_OpenProperty,
(LDAPCONT_SetProps_METHOD *) WRAP_SetProps,
(LDAPCONT_DeleteProps_METHOD *) WRAP_DeleteProps,
(LDAPCONT_CopyTo_METHOD *) WRAP_CopyTo,
(LDAPCONT_CopyProps_METHOD *) WRAP_CopyProps,
(LDAPCONT_GetNamesFromIDs_METHOD *) WRAP_GetNamesFromIDs,
(LDAPCONT_GetIDsFromNames_METHOD *) WRAP_GetIDsFromNames,
LDAPCONT_GetContentsTable,
LDAPCONT_GetHierarchyTable,
LDAPCONT_OpenEntry,
LDAPCONT_SetSearchCriteria,
LDAPCONT_GetSearchCriteria,
LDAPCONT_CreateEntry,
LDAPCONT_CopyEntries,
LDAPCONT_DeleteEntries,
LDAPCONT_ResolveNames
};
// LDAPVUE (table view class)
// Implementes in-memory IMAPITable class on top of TADs
// This is a copy of vtblVUE with FindRow overridden with the LDAP FindRow.
VUE_Vtbl vtblLDAPVUE =
{
VTABLE_FILL
(VUE_QueryInterface_METHOD FAR *) UNKOBJ_QueryInterface,
(VUE_AddRef_METHOD FAR *) UNKOBJ_AddRef,
VUE_Release,
(VUE_GetLastError_METHOD FAR *) UNKOBJ_GetLastError,
VUE_Advise,
VUE_Unadvise,
VUE_GetStatus,
VUE_SetColumns,
VUE_QueryColumns,
VUE_GetRowCount,
VUE_SeekRow,
VUE_SeekRowApprox,
VUE_QueryPosition,
LDAPVUE_FindRow,
LDAPVUE_Restrict,
VUE_CreateBookmark,
VUE_FreeBookmark,
VUE_SortTable,
VUE_QuerySortOrder,
VUE_QueryRows,
VUE_Abort,
VUE_ExpandRow,
VUE_CollapseRow,
VUE_WaitForCompletion,
VUE_GetCollapseState,
VUE_SetCollapseState
};
// Interfaces supported by this object
LPIID LDAPCONT_LPIID[LDAPCONT_cInterfaces] =
{
(LPIID)&IID_IABContainer,
(LPIID)&IID_IMAPIContainer,
(LPIID)&IID_IMAPIProp
};
// LDAP function names
static const TCHAR cszLDAPClientDLL[] = TEXT("WLDAP32.DLL");
static const char cszLDAPSSLInit[] = "ldap_sslinitW";
static const char cszLDAPSetOption[] = "ldap_set_optionW";
static const char cszLDAPOpen[] = "ldap_openW";
static const char cszLDAPBind[] = "ldap_bindW";
static const char cszLDAPBindS[] = "ldap_bind_sW";
static const char cszLDAPUnbind[] = "ldap_unbind";
static const char cszLDAPSearch[] = "ldap_searchW";
static const char cszLDAPSearchS[] = "ldap_search_sW";
static const char cszLDAPSearchST[] = "ldap_search_stW";
static const char cszLDAPAbandon[] = "ldap_abandon";
static const char cszLDAPResult[] = "ldap_result";
static const char cszLDAPResult2Error[] = "ldap_result2error";
static const char cszLDAPMsgFree[] = "ldap_msgfree";
static const char cszLDAPFirstEntry[] = "ldap_first_entry";
static const char cszLDAPNextEntry[] = "ldap_next_entry";
static const char cszLDAPCountEntries[] = "ldap_count_entries";
static const char cszLDAPFirstAttr[] = "ldap_first_attributeW";
static const char cszLDAPNextAttr[] = "ldap_next_attributeW";
static const char cszLDAPGetValues[] = "ldap_get_valuesW";
static const char cszLDAPGetValuesLen[] = "ldap_get_values_lenW";
static const char cszLDAPCountValues[] = "ldap_count_valuesW";
static const char cszLDAPCountValuesLen[] = "ldap_count_values_len";
static const char cszLDAPValueFree[] = "ldap_value_freeW";
static const char cszLDAPValueFreeLen[] = "ldap_value_free_len";
static const char cszLDAPGetDN[] = "ldap_get_dnW";
static const char cszLDAPMemFree[] = "ldap_memfreeW";
static const char cszLDAPConnect[] = "ldap_connect";
static const char cszLDAPInit[] = "ldap_initW";
static const char cszLDAPErr2String[] = "ldap_err2stringW";
static const char cszLDAPCreatePageControl[] = "ldap_create_page_controlW";
static const char cszLDAPSearchExtS[] = "ldap_search_ext_sW";
static const char cszLDAPSearchExt[] = "ldap_search_extW";
static const char cszLDAPParseResult[] = "ldap_parse_resultW";
static const char cszLDAPParsePageControl[] = "ldap_parse_page_controlW";
static const char cszLDAPControlFree[] = "ldap_control_freeW";
static const char cszLDAPControlSFree[] = "ldap_controls_freeW";
// Registry keys
const LPTSTR szAllLDAPServersValueName = TEXT("All LDAP Server Names");
// Global function pointers for LDAP API
LPLDAPOPEN gpfnLDAPOpen = NULL;
LPLDAPINIT gpfnLDAPInit = NULL;
LPLDAPCONNECT gpfnLDAPConnect = NULL;
LPLDAPSSLINIT gpfnLDAPSSLInit = NULL;
LPLDAPSETOPTION gpfnLDAPSetOption = NULL;
LPLDAPBIND gpfnLDAPBind = NULL;
LPLDAPBINDS gpfnLDAPBindS = NULL;
LPLDAPUNBIND gpfnLDAPUnbind = NULL;
LPLDAPSEARCH gpfnLDAPSearch = NULL;
LPLDAPSEARCHS gpfnLDAPSearchS = NULL;
LPLDAPSEARCHST gpfnLDAPSearchST = NULL;
LPLDAPABANDON gpfnLDAPAbandon = NULL;
LPLDAPRESULT gpfnLDAPResult = NULL;
LPLDAPRESULT2ERROR gpfnLDAPResult2Error = NULL;
LPLDAPMSGFREE gpfnLDAPMsgFree = NULL;
LPLDAPFIRSTENTRY gpfnLDAPFirstEntry = NULL;
LPLDAPNEXTENTRY gpfnLDAPNextEntry = NULL;
LPLDAPCOUNTENTRIES gpfnLDAPCountEntries = NULL;
LPLDAPFIRSTATTR gpfnLDAPFirstAttr = NULL;
LPLDAPNEXTATTR gpfnLDAPNextAttr = NULL;
LPLDAPGETVALUES gpfnLDAPGetValues = NULL;
LPLDAPGETVALUESLEN gpfnLDAPGetValuesLen = NULL;
LPLDAPCOUNTVALUES gpfnLDAPCountValues = NULL;
LPLDAPCOUNTVALUESLEN gpfnLDAPCountValuesLen = NULL;
LPLDAPVALUEFREE gpfnLDAPValueFree = NULL;
LPLDAPVALUEFREELEN gpfnLDAPValueFreeLen = NULL;
LPLDAPGETDN gpfnLDAPGetDN = NULL;
LPLDAPMEMFREE gpfnLDAPMemFree = NULL;
LPLDAPERR2STRING gpfnLDAPErr2String = NULL;
LPLDAPCREATEPAGECONTROL gpfnLDAPCreatePageControl = NULL;
LPLDAPSEARCHEXT_S gpfnLDAPSearchExtS = NULL;
LPLDAPSEARCHEXT gpfnLDAPSearchExt = NULL;
LPLDAPPARSERESULT gpfnLDAPParseResult = NULL;
LPLDAPPARSEPAGECONTROL gpfnLDAPParsePageControl = NULL;
LPLDAPCONTROLFREE gpfnLDAPControlFree = NULL;
LPLDAPCONTROLSFREE gpfnLDAPControlsFree = NULL;
// API table for LDAP function addresses to fetch
// BUGBUG this global array should be in data seg
#define NUM_LDAPAPI_PROCS 36
APIFCN LDAPAPIList[NUM_LDAPAPI_PROCS] =
{
{ (PVOID *) &gpfnLDAPOpen, cszLDAPOpen },
{ (PVOID *) &gpfnLDAPConnect, cszLDAPConnect },
{ (PVOID *) &gpfnLDAPInit, cszLDAPInit },
{ (PVOID *) &gpfnLDAPSSLInit, cszLDAPSSLInit },
{ (PVOID *) &gpfnLDAPSetOption, cszLDAPSetOption },
{ (PVOID *) &gpfnLDAPBind, cszLDAPBind },
{ (PVOID *) &gpfnLDAPBindS, cszLDAPBindS },
{ (PVOID *) &gpfnLDAPUnbind, cszLDAPUnbind },
{ (PVOID *) &gpfnLDAPSearch, cszLDAPSearch },
{ (PVOID *) &gpfnLDAPSearchS, cszLDAPSearchS },
{ (PVOID *) &gpfnLDAPSearchST, cszLDAPSearchST },
{ (PVOID *) &gpfnLDAPAbandon, cszLDAPAbandon },
{ (PVOID *) &gpfnLDAPResult, cszLDAPResult },
{ (PVOID *) &gpfnLDAPResult2Error, cszLDAPResult2Error },
{ (PVOID *) &gpfnLDAPMsgFree, cszLDAPMsgFree },
{ (PVOID *) &gpfnLDAPFirstEntry, cszLDAPFirstEntry },
{ (PVOID *) &gpfnLDAPNextEntry, cszLDAPNextEntry },
{ (PVOID *) &gpfnLDAPCountEntries, cszLDAPCountEntries },
{ (PVOID *) &gpfnLDAPFirstAttr, cszLDAPFirstAttr },
{ (PVOID *) &gpfnLDAPNextAttr, cszLDAPNextAttr },
{ (PVOID *) &gpfnLDAPGetValues, cszLDAPGetValues },
{ (PVOID *) &gpfnLDAPGetValuesLen, cszLDAPGetValuesLen },
{ (PVOID *) &gpfnLDAPCountValues, cszLDAPCountValues },
{ (PVOID *) &gpfnLDAPCountValuesLen, cszLDAPCountValuesLen },
{ (PVOID *) &gpfnLDAPValueFree, cszLDAPValueFree },
{ (PVOID *) &gpfnLDAPValueFreeLen, cszLDAPValueFreeLen },
{ (PVOID *) &gpfnLDAPGetDN, cszLDAPGetDN },
{ (PVOID *) &gpfnLDAPMemFree, cszLDAPMemFree },
{ (PVOID *) &gpfnLDAPErr2String, cszLDAPErr2String },
{ (PVOID *) &gpfnLDAPCreatePageControl, cszLDAPCreatePageControl },
{ (PVOID *) &gpfnLDAPSearchExtS, cszLDAPSearchExtS },
{ (PVOID *) &gpfnLDAPSearchExt, cszLDAPSearchExt },
{ (PVOID *) &gpfnLDAPParseResult, cszLDAPParseResult },
{ (PVOID *) &gpfnLDAPParsePageControl,cszLDAPParsePageControl },
{ (PVOID *) &gpfnLDAPControlFree, cszLDAPControlFree },
{ (PVOID *) &gpfnLDAPControlsFree, cszLDAPControlSFree }
};
// LDAP attribute names
static const TCHAR cszAttr_display_name[] = TEXT("display-name");
static const TCHAR cszAttr_cn[] = TEXT("cn");
static const TCHAR cszAttr_commonName[] = TEXT("commonName");
static const TCHAR cszAttr_mail[] = TEXT("mail");
static const TCHAR cszAttr_otherMailbox[] = TEXT("otherMailbox");
static const TCHAR cszAttr_givenName[] = TEXT("givenName");
static const TCHAR cszAttr_sn[] = TEXT("sn");
static const TCHAR cszAttr_surname[] = TEXT("surname");
static const TCHAR cszAttr_st[] = TEXT("st");
static const TCHAR cszAttr_c[] = TEXT("c");
static const TCHAR cszAttr_o[] = TEXT("o");
static const TCHAR cszAttr_organizationName[] = TEXT("organizationName");
static const TCHAR cszAttr_ou[] = TEXT("ou");
static const TCHAR cszAttr_organizationalUnitName[] = TEXT("organizationalUnitName");
static const TCHAR cszAttr_URL[] = TEXT("URL");
static const TCHAR cszAttr_homePhone[] = TEXT("homePhone");
static const TCHAR cszAttr_facsimileTelephoneNumber[] = TEXT("facsimileTelephoneNumber");
static const TCHAR cszAttr_otherFacsimileTelephoneNumber[]= TEXT("otherFacsimileTelephoneNumber");
static const TCHAR cszAttr_OfficeFax[] = TEXT("OfficeFax");
static const TCHAR cszAttr_mobile[] = TEXT("mobile");
static const TCHAR cszAttr_otherPager[] = TEXT("otherPager");
static const TCHAR cszAttr_OfficePager[] = TEXT("OfficePager");
static const TCHAR cszAttr_pager[] = TEXT("pager");
static const TCHAR cszAttr_info[] = TEXT("info");
static const TCHAR cszAttr_title[] = TEXT("title");
static const TCHAR cszAttr_telephoneNumber[] = TEXT("telephoneNumber");
static const TCHAR cszAttr_l[] = TEXT("l");
static const TCHAR cszAttr_homePostalAddress[] = TEXT("homePostalAddress");
static const TCHAR cszAttr_postalAddress[] = TEXT("postalAddress");
static const TCHAR cszAttr_streetAddress[] = TEXT("streetAddress");
static const TCHAR cszAttr_street[] = TEXT("street");
static const TCHAR cszAttr_department[] = TEXT("department");
static const TCHAR cszAttr_comment[] = TEXT("comment");
static const TCHAR cszAttr_co[] = TEXT("co");
static const TCHAR cszAttr_postalCode[] = TEXT("postalCode");
static const TCHAR cszAttr_physicalDeliveryOfficeName[] = TEXT("physicalDeliveryOfficeName");
static const TCHAR cszAttr_initials[] = TEXT("initials");
static const TCHAR cszAttr_userCertificatebinary[] = TEXT("userCertificate;binary");
static const TCHAR cszAttr_userSMIMECertificatebinary[] = TEXT("userSMIMECertificate;binary");
static const TCHAR cszAttr_userCertificate[] = TEXT("userCertificate");
static const TCHAR cszAttr_userSMIMECertificate[] = TEXT("userSMIMECertificate");
static const TCHAR cszAttr_labeledURI[] = TEXT("labeledURI");
static const TCHAR cszAttr_conferenceInformation[] = TEXT("conferenceInformation");
static const TCHAR cszAttr_Manager[] = TEXT("Manager");
static const TCHAR cszAttr_Reports[] = TEXT("Reports");
static const TCHAR cszAttr_IPPhone[] = TEXT("IPPhone");
static const TCHAR cszAttr_anr[] = TEXT("anr");
// List of attributes that we ask the server to return on OpenEntry calls
// This list includes the userCertificates property.
// Also includes the labeledURI property
static const TCHAR *g_rgszOpenEntryAttrs[] =
{
cszAttr_display_name,
cszAttr_cn,
cszAttr_commonName,
cszAttr_mail,
cszAttr_otherMailbox,
cszAttr_givenName,
cszAttr_sn,
cszAttr_surname,
cszAttr_st,
cszAttr_c,
cszAttr_co,
cszAttr_organizationName,
cszAttr_o,
cszAttr_ou,
cszAttr_organizationalUnitName,
cszAttr_URL,
cszAttr_homePhone,
cszAttr_facsimileTelephoneNumber,
cszAttr_otherFacsimileTelephoneNumber,
cszAttr_OfficeFax,
cszAttr_mobile,
cszAttr_otherPager,
cszAttr_OfficePager,
cszAttr_pager,
cszAttr_info,
cszAttr_title,
cszAttr_telephoneNumber,
cszAttr_l,
cszAttr_homePostalAddress,
cszAttr_postalAddress,
cszAttr_streetAddress,
cszAttr_street,
cszAttr_department,
cszAttr_comment,
cszAttr_postalCode,
cszAttr_physicalDeliveryOfficeName,
cszAttr_initials,
cszAttr_conferenceInformation,
cszAttr_userCertificatebinary,
cszAttr_userSMIMECertificatebinary,
cszAttr_labeledURI,
cszAttr_Manager,
cszAttr_Reports,
cszAttr_IPPhone,
NULL
};
// List of attributes that we put in the advanced find combo
//
// This list needs to be kept in sync with the string resources
// idsLDAPFilterField*
//
const TCHAR *g_rgszAdvancedFindAttrs[] =
{
cszAttr_cn,
cszAttr_mail,
cszAttr_givenName,
cszAttr_sn,
cszAttr_o,
/*
cszAttr_homePostalAddress,
cszAttr_postalAddress,
cszAttr_streetAddress,
cszAttr_street,
cszAttr_st,
cszAttr_c,
cszAttr_postalCode,
cszAttr_department,
cszAttr_title,
cszAttr_co,
cszAttr_ou,
cszAttr_homePhone,
cszAttr_telephoneNumber,
cszAttr_facsimileTelephoneNumber,
cszAttr_OfficeFax,
cszAttr_mobile,
cszAttr_pager,
cszAttr_OfficePager,
cszAttr_conferenceInformation,
cszAttr_Manager,
cszAttr_Reports,
*/
NULL
};
/* Only use the above list for all kinds of searches since now we do a single
search for all attributes and cache the results
// List of attributes that we ask the server to return on FindRow calls
// This list DOES NOT include the userCertificates property, since when we
// got the certs they would have been added to the WAB store and then would
// have to be deleted. We only get the certs if the user asks for properties.
static const TCHAR *g_rgszFindRowAttrs[] =
{
cszAttr_display_name,
cszAttr_cn,
cszAttr_commonName,
cszAttr_mail,
cszAttr_otherMailbox,
cszAttr_givenName,
cszAttr_sn,
cszAttr_surname,
cszAttr_st,
cszAttr_c,
cszAttr_co,
cszAttr_organizationName,
cszAttr_o,
cszAttr_ou,
cszAttr_organizationalUnitName,
cszAttr_URL,
cszAttr_homePhone,
cszAttr_facsimileTelephoneNumber,
cszAttr_OfficeFax,
cszAttr_mobile,
cszAttr_OfficePager,
cszAttr_pager,
cszAttr_info,
cszAttr_title,
cszAttr_telephoneNumber,
cszAttr_l,
cszAttr_homePostalAddress,
cszAttr_postalAddress,
cszAttr_streetAddress,
cszAttr_street,
cszAttr_department,
cszAttr_comment,
cszAttr_postalCode,
cszAttr_physicalDeliveryOfficeName,
cszAttr_initials,
cszAttr_conferenceInformation,
// DO NOT PUT cszAttr_userCertificatebinary in here!
// Also no need to pu cszAttr_labeledURI here!
NULL
};
*/
// MAPI property to LDAP attribute map
// [PaulHi] 3/17/99 We have special PR_PAGER_TELEPHONE_NUMBER parsing code now. If any new
// PR_PAGER_TELEPHONE_NUMBER attributes are added then also add them to the atszPagerAttr[].
// Search on '[PaulHi] 3/17/99' to find the places that use this array.
// BUGBUG this global array should be in data seg
#define NUM_ATTRMAP_ENTRIES 42
ATTRMAP gAttrMap[NUM_ATTRMAP_ENTRIES] =
{
{ PR_DISPLAY_NAME, cszAttr_display_name },
{ PR_DISPLAY_NAME, cszAttr_cn },
{ PR_DISPLAY_NAME, cszAttr_commonName },
{ PR_GIVEN_NAME, cszAttr_givenName },
{ PR_MIDDLE_NAME, cszAttr_initials },
{ PR_SURNAME, cszAttr_sn },
{ PR_SURNAME, cszAttr_surname },
{ PR_EMAIL_ADDRESS, cszAttr_mail },
{ PR_WAB_SECONDARY_EMAIL_ADDRESSES, cszAttr_otherMailbox },
{ PR_COUNTRY, cszAttr_co },
{ PR_COUNTRY, cszAttr_c },
{ PR_STATE_OR_PROVINCE, cszAttr_st },
{ PR_LOCALITY, cszAttr_l },
{ PR_HOME_ADDRESS_STREET, cszAttr_homePostalAddress },
{ PR_STREET_ADDRESS, cszAttr_streetAddress },
{ PR_STREET_ADDRESS, cszAttr_street },
{ PR_STREET_ADDRESS, cszAttr_postalAddress },
{ PR_POSTAL_CODE, cszAttr_postalCode },
{ PR_HOME_TELEPHONE_NUMBER, cszAttr_homePhone },
{ PR_MOBILE_TELEPHONE_NUMBER, cszAttr_mobile },
{ PR_PAGER_TELEPHONE_NUMBER, cszAttr_pager },
{ PR_PAGER_TELEPHONE_NUMBER, cszAttr_otherPager },
{ PR_PAGER_TELEPHONE_NUMBER, cszAttr_OfficePager },
{ PR_BUSINESS_TELEPHONE_NUMBER, cszAttr_telephoneNumber },
{ PR_BUSINESS_HOME_PAGE, cszAttr_URL },
{ PR_HOME_FAX_NUMBER, cszAttr_otherFacsimileTelephoneNumber},
{ PR_BUSINESS_FAX_NUMBER, cszAttr_facsimileTelephoneNumber },
{ PR_BUSINESS_FAX_NUMBER, cszAttr_OfficeFax },
{ PR_TITLE, cszAttr_title },
{ PR_COMPANY_NAME, cszAttr_organizationName },
{ PR_COMPANY_NAME, cszAttr_o },
{ PR_DEPARTMENT_NAME, cszAttr_ou },
{ PR_DEPARTMENT_NAME, cszAttr_organizationalUnitName },
{ PR_DEPARTMENT_NAME, cszAttr_department },
{ PR_OFFICE_LOCATION, cszAttr_physicalDeliveryOfficeName },
{ PR_COMMENT, cszAttr_info },
{ PR_COMMENT, cszAttr_comment },
{ PR_USER_X509_CERTIFICATE, cszAttr_userCertificatebinary },
{ PR_USER_X509_CERTIFICATE, cszAttr_userSMIMECertificatebinary },
{ PR_USER_X509_CERTIFICATE, cszAttr_userCertificate },
{ PR_USER_X509_CERTIFICATE, cszAttr_userSMIMECertificate },
{ PR_WAB_LDAP_LABELEDURI, cszAttr_labeledURI },
};
// Filter strings
static const TCHAR cszDefaultCountry[] = TEXT("US");
static const TCHAR cszBaseFilter[] = TEXT("%s=%s");
static const TCHAR cszAllEntriesFilter[] = TEXT("(objectclass=*)");
static const TCHAR cszCommonNamePresentFilter[] = TEXT("(cn=*)");
static const TCHAR cszStar[] = TEXT("*");
static const TCHAR cszOpenParen[] = TEXT("(");
static const TCHAR cszCloseParen[] = TEXT(")");
static const TCHAR cszEqualSign[] = TEXT("=");
static const TCHAR cszAnd[] = TEXT("&");
static const TCHAR cszOr[] = TEXT("|");
static const TCHAR cszAllPersonFilter[] = TEXT("(objectCategory=person)");
static const TCHAR cszAllGroupFilter[] = TEXT("(objectCategory=group)");
// Set TRUE if CoInitialize is called
BOOL fCoInitialize = FALSE;
enum
{
use_ldap_v3 = 0,
use_ldap_v2
};
// Definitions
#define FIRST_PASS 0
#define SECOND_PASS 1
#define UMICH_PASS SECOND_PASS
// Number of extra characters needed when allocating filters
#define FILTER_EXTRA_BASIC 4 // (, =, ), *
#define FILTER_EXTRA_OP 3 // (, &/|, )
#define FILTER_OP_AND 0 // use AND operator
#define FILTER_OP_OR 1 // use OR operator
// When we allocate a new buffer for the MAPI property array, we will need at most
// as many entries as there were in the input list of LDAP attributes plus a few extras.
// The number of extras is different in each case, but for simplicity (and safety)
// we define NUM_EXTRA_PROPS to be the maximum number of extras that we need to
// allocate space for. Extras are need for:
// PR_ADDRTYPE
// PR_CONTACT_EMAIL_ADDRESSES
// PR_CONTACT_ADDRTYPES
// PR_CONTACT_DEFAULT_ADDRESS_INDEX
// PR_ENTRY_ID
// PR_INSTANCE_KEY
// PR_RECORD_KEY
// PR_WAB_TEMP_CERT_HASH
// PR_WAB_LDAP_RAWCERT
// PR_WAB_LDAP_RAWCERTSMIME
#define NUM_EXTRA_PROPS 10
// Local function prototypes
HRESULT LDAPSearchWithoutContainer(HWND hWnd,
LPLDAPURL lplu,
LPSRestriction lpres,
LPTSTR lpAdvFilter,
BOOL bReturnSinglePropArray,
ULONG ulFlags,
LPRECIPIENT_INFO * lppContentsList,
LPULONG lpulcProps,
LPSPropValue * lppPropArray);
LPTSTR MAPIPropToLDAPAttr(const ULONG ulPropTag);
ULONG LDAPAttrToMAPIProp(const LPTSTR szAttr);
HRESULT ParseSRestriction(LPSRestriction lpRes, LPTSTR FAR * lplpszFilter, LPTSTR * lplpszSimpleFilter, LPTSTR * lplpszNTFilter, DWORD dwPass, BOOL bUnicode);
HRESULT GetLDAPServerName(LPLDAPCONT lpLDAPCont, LPTSTR * lppServer);
BOOL FixPropArray(LPSPropValue lpPropArray, ULONG * lpulcProps);
HRESULT HRFromLDAPError(ULONG ulErr, LDAP* pLDAP, SCODE scDefault);
HRESULT TranslateAttrs(LDAP* pLDAP, LDAPMessage* lpEntry, LPTSTR lpServer, ULONG* pulcProps, LPSPropValue lpPropArray);
ULONG OpenConnection(LPTSTR lpszServer, LDAP** ppLDAP, ULONG* pulTimeout, ULONG* pulMsgID, BOOL* pfSyncBind, ULONG ulLdapType, LPTSTR lpszBindDN, DWORD dwAuthType);
void EncryptDecryptText(LPBYTE lpb, DWORD dwSize);
HRESULT CreateSimpleSearchFilter(LPTSTR FAR * lplpszFilter, LPTSTR FAR * lplpszAltFilter, LPTSTR * lplpszSimpleFilter, LPTSTR lpszInput, DWORD dwPass);
HRESULT GetLDAPSearchBase(LPTSTR FAR * lplpszBase, LPTSTR lpszServer);
INT_PTR CALLBACK DisplayLDAPCancelDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
ULONG SearchWithCancel(LDAP** ppLDAP, LPTSTR szBase,ULONG ulScope,LPTSTR szFilter,LPTSTR szNTFilter,
LPTSTR* ppszAttrs,ULONG ulAttrsonly,LDAPMessage** lplpResult,LPTSTR lpszServer,
BOOL fShowAnim,LPTSTR lpszBindDN,DWORD dwAuthType,
BOOL fResolveMultiple,LPADRLIST lpAdrList,LPFlagList lpFlagList,BOOL fUseSynchronousBind, BOOL * lpbIsNTDS, BOOL bUnicode);
BOOL CenterWindow (HWND hwndChild, HWND hwndParent);
BOOL ResolveDoNextSearch(PLDAPSEARCHPARAMS pLDAPSearchParams, HWND hDlg, BOOL bSecondPass);
BOOL ResolveProcessResults(PLDAPSEARCHPARAMS pLDAPSearchParams, HWND hDlg);
BOOL BindProcessResults(PLDAPSEARCHPARAMS pLDAPSearchParams, HWND hDlg, BOOL * lpbNoMoreSearching);
ULONG CountDollars(LPTSTR lpszStr);
void DollarsToLFs(LPTSTR lpszSrcStr, LPTSTR lpszDestStr, DWORD cchDestStr);
BOOL IsSMTPAddress(LPTSTR lpszStr, LPTSTR * lpptszName);
ULONG CountIllegalChars(LPTSTR lpszStr);
void EscapeIllegalChars(LPTSTR lpszSrcStr, LPTSTR lpszDestStr, ULONG cchDestStr);
BOOL bIsSimpleSearch(LPTSTR lpszServer);
BOOL DoSyncLDAPSearch(PLDAPSEARCHPARAMS pLDAPSearchParams);
ULONG CheckErrorResult(PLDAPSEARCHPARAMS pLDALSearchParams, ULONG ulExpectedResult);
#ifdef PAGED_RESULT_SUPPORT
BOOL ProcessLDAPPagedResultCookie(PLDAPSEARCHPARAMS pLDAPSearchParams);
void InitLDAPPagedSearch(BOOL fSynchronous, PLDAPSEARCHPARAMS pLDAPSearchParams, LPTSTR lpFilter);
BOOL bSupportsLDAPPagedResults(PLDAPSEARCHPARAMS pLDAPSearchParams);
#endif //#ifdef PAGED_RESULT_SUPPORT
BOOL bCheckIfNTDS(PLDAPSEARCHPARAMS pLDAPSearchParams);
HRESULT BuildBasicFilter(
LPTSTR FAR* lplpszFilter,
LPTSTR lpszA,
LPTSTR lpszB,
BOOL fStartsWith);
HRESULT BuildOpFilter(
LPTSTR FAR* lplpszFilter,
LPTSTR lpszA,
LPTSTR lpszB,
DWORD dwOp);
//*******************************************************************
//
// FUNCTION: LDAPCONT_GetHierarchyTable
//
// RETURNS: This function is not implemented.
//
// HISTORY:
// 96/07/08 markdu Created.
//
//*******************************************************************
STDMETHODIMP
LDAPCONT_GetHierarchyTable (
LPLDAPCONT lpLDAPCont,
ULONG ulFlags,
LPMAPITABLE * lppTable)
{
LPTSTR lpszMessage = NULL;
ULONG ulLowLevelError = 0;
HRESULT hr = ResultFromScode(MAPI_E_NO_SUPPORT);
DebugTraceResult(LDAPCONT_GetHierarchyTable, hr);
return hr;
}
//*******************************************************************
//
// FUNCTION: LDAPCONT_SetSearchCriteria
//
// RETURNS: This function is not implemented.
//
// HISTORY:
// 96/07/08 markdu Created.
//
//*******************************************************************
STDMETHODIMP
LDAPCONT_SetSearchCriteria(
LPLDAPCONT lpLDAPCont,
LPSRestriction lpRestriction,
LPENTRYLIST lpContainerList,
ULONG ulSearchFlags)
{
HRESULT hr = ResultFromScode(MAPI_E_NO_SUPPORT);
DebugTraceResult(LDAPCONT_SetSearchCriteria, hr);
return hr;
}
//*******************************************************************
//
// FUNCTION: LDAPCONT_GetSearchCriteria
//
// RETURNS: This function is not implemented.
//
// HISTORY:
// 96/07/08 markdu Created.
//
//*******************************************************************
STDMETHODIMP
LDAPCONT_GetSearchCriteria(
LPLDAPCONT lpLDAPCont,
ULONG ulFlags,
LPSRestriction FAR * lppRestriction,
LPENTRYLIST FAR * lppContainerList,
ULONG FAR * lpulSearchState)
{
HRESULT hr = ResultFromScode(MAPI_E_NO_SUPPORT);
DebugTraceResult(LDAPCONT_GetSearchCriteria, hr);
return hr;
}
//*******************************************************************
//
// FUNCTION: LDAPCONT_CreateEntry
//
// RETURNS: This function is not implemented.
//
// HISTORY:
// 96/07/08 markdu Created.
//
//*******************************************************************
STDMETHODIMP
LDAPCONT_CreateEntry(
LPLDAPCONT lpLDAPCont,
ULONG cbEntryID,
LPENTRYID lpEntryID,
ULONG ulCreateFlags,
LPMAPIPROP FAR * lppMAPIPropEntry)
{
HRESULT hr = ResultFromScode(MAPI_E_NO_SUPPORT);
DebugTraceResult(LDAPCONT_CreateEntry, hr);
return hr;
}
//*******************************************************************
//
// FUNCTION: LDAPCONT_CopyEntries
//
// RETURNS: This function is not implemented.
//
// HISTORY:
// 96/07/08 markdu Created.
//
//*******************************************************************
STDMETHODIMP
LDAPCONT_CopyEntries (
LPLDAPCONT lpLDAPCont,
LPENTRYLIST lpEntries,
ULONG_PTR ulUIParam,
LPMAPIPROGRESS lpProgress,
ULONG ulFlags)
{
HRESULT hr = ResultFromScode(MAPI_E_NO_SUPPORT);
DebugTraceResult(LDAPCONT_CopyEntries, hr);
return hr;
}
//*******************************************************************
//
// FUNCTION: LDAPCONT_DeleteEntries
//
// RETURNS: This function is not implemented.
//
// HISTORY:
// 96/07/08 markdu Created.
//
//*******************************************************************
STDMETHODIMP
LDAPCONT_DeleteEntries (
LPLDAPCONT lpLDAPCont,
LPENTRYLIST lpEntries,
ULONG ulFlags)
{
HRESULT hr = ResultFromScode(MAPI_E_NO_SUPPORT);
DebugTraceResult(LDAPCONT_DeleteEntries, hr);
return hr;
}
//*******************************************************************
//
// FUNCTION: LDAPCONT_GetContentsTable
//
// PURPOSE: Opens a table of the contents of the container
//
// PARAMETERS: lpLDAPCont -> Container object
// ulFlags =
// lppTable -> returned table object
//
// RETURNS: HRESULT
//
// HISTORY:
// 96/07/08 markdu Created.
//
//*******************************************************************
STDMETHODIMP
LDAPCONT_GetContentsTable (
LPLDAPCONT lpLDAPCont,
ULONG ulFlags,
LPMAPITABLE * lppTable)
{
HRESULT hResult = hrSuccess;
LPTABLEDATA lpTableData = NULL;
SCODE sc;
LPTSTR lpszServer = NULL;
#ifdef PARAMETER_VALIDATION
if (IsBadReadPtr(lpLDAPCont, sizeof(LPVOID)))
{
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
if (lpLDAPCont->lpVtbl != &vtblLDAPCONT)
{
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
if (ulFlags & ~(MAPI_DEFERRED_ERRORS|MAPI_UNICODE))
{
DebugTraceArg(LDAPCONT_GetContentsTable, TEXT("Unknown flags"));
// return ResultFromScode(MAPI_E_UNKNOWN_FLAGS);
}
if (IsBadWritePtr(lppTable, sizeof(LPMAPITABLE)))
{
DebugTraceArg(LDAPCONT_GetContentsTable, TEXT("Invalid Table parameter"));
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
#endif // PARAMETER_VALIDATION
if (hResult = GetLDAPServerName(lpLDAPCont,
&lpszServer)) {
DebugTraceResult( TEXT("GetLDAPServerName"), hResult);
goto exit;
}
if (FAILED(sc = CreateTableData(
NULL, // LPCIID
(ALLOCATEBUFFER FAR *) MAPIAllocateBuffer,
(ALLOCATEMORE FAR *) MAPIAllocateMore,
MAPIFreeBuffer,
NULL, // lpvReserved,
TBLTYPE_DYNAMIC, // ulTableType,
PR_RECORD_KEY, // ulPropTagIndexCol,
(LPSPropTagArray)&ITableColumns, // LPSPropTagArray lpptaCols,
lpszServer, // server name goes here
sizeof(TCHAR)*(lstrlen(lpszServer) + 1), // size of servername
lpLDAPCont->pmbinOlk,
ulFlags,
&lpTableData)))
{ // LPTABLEATA FAR * lplptad
DebugTrace(TEXT("CreateTable failed %x\n"), sc);
hResult = ResultFromScode(sc);
goto exit;
}
if (lpTableData)
{
hResult = lpTableData->lpVtbl->HrGetView(lpTableData,
NULL, // LPSSortOrderSet lpsos,
ContentsViewGone, // CALLERRELEASE FAR * lpfReleaseCallback,
0, // ULONG ulReleaseData,
lppTable); // LPMAPITABLE FAR * lplpmt)
// Replace the vtable with our new one that overrides FindRow
(*lppTable)->lpVtbl = (IMAPITableVtbl FAR *) &vtblLDAPVUE;
}
exit:
FreeBufferAndNull(&lpszServer);
// Cleanup table if failure
if (HR_FAILED(hResult))
{
if (lpTableData)
UlRelease(lpTableData);
}
DebugTraceResult(LDAPCONT_GetContentsTable, hResult);
return hResult;
}
//*******************************************************************
//
// FUNCTION: LDAP_OpenMAILUSER
//
// PURPOSE: Opens a LDAP MAILUSER object
//
// PARAMETERS: cbEntryID = size of lpEntryID.
// lpEntryID -> entryid to check.
// The entryid contains all the returned properties on this LDAP
// contact. All we need to do is reverse engineer the decrypted
// props ...
//
// RETURNS: HRESULT
//
// HISTORY:
// 96/07/08 markdu Created.
// 97/09/18 vikramm Revamped totally
//
//*******************************************************************
HRESULT LDAP_OpenMAILUSER(
LPIAB lpIAB,
ULONG cbEntryID,
LPENTRYID lpEntryID,
LPCIID lpInterface,
ULONG ulFlags,
ULONG * lpulObjType,
LPUNKNOWN * lppUnk)
{
HRESULT hr;
HRESULT hrDeferred = hrSuccess;
SCODE sc;
LPMAILUSER lpMailUser = NULL;
LPMAPIPROP lpMapiProp = NULL;
ULONG ulcProps = 0;
LPSPropValue lpPropArray = NULL;
LPTSTR szBase;
ULONG ulResult;
ULONG ulcEntries;
LPTSTR lpServer = NULL;
LPTSTR lpDN = NULL;
LPBYTE lpPropBuf = NULL;
ULONG ulcNumProps = 0;
ULONG cbPropBuf = 0;
ULONG i = 0;
ULONG i2;
LPSPropValue lpPropCert = NULL;
ULONG ulcCert = 0;
#ifdef PARAMETER_VALIDATION
//
// Parameter Validataion
//
if (lpInterface && IsBadReadPtr(lpInterface, sizeof(IID))) {
return(ResultFromScode(MAPI_E_INVALID_PARAMETER));
}
if (ulFlags & ~(MAPI_MODIFY | MAPI_DEFERRED_ERRORS | MAPI_BEST_ACCESS)) {
DebugTraceArg(LDAP_OpenMAILUSER, TEXT("Unknown flags"));
// return(ResultFromScode(MAPI_E_UNKNOWN_FLAGS));
}
if (IsBadWritePtr(lpulObjType, sizeof(ULONG))) {
return(ResultFromScode(MAPI_E_INVALID_PARAMETER));
}
if (IsBadWritePtr(lppUnk, sizeof(LPUNKNOWN))) {
return(ResultFromScode(MAPI_E_INVALID_PARAMETER));
}
#endif
// What interface was requested?
// We've basically got 1 interface here... IMailUser
if (lpInterface != NULL) {
if (! ((! memcmp(lpInterface, &IID_IMailUser, sizeof(IID))) ||
(! memcmp(lpInterface, &IID_IMAPIProp, sizeof(IID))))) {
hr = ResultFromScode(MAPI_E_INTERFACE_NOT_SUPPORTED);
goto exit;
}
}
// Make sure the Entry ID is a WAB_LDAP_MAILUSER.
if (WAB_LDAP_MAILUSER != IsWABEntryID( cbEntryID, lpEntryID,
&lpServer, &lpDN, &lpPropBuf,
(LPVOID *) &ulcNumProps, (LPVOID *) &cbPropBuf))
{
return(ResultFromScode(MAPI_E_INVALID_ENTRYID));
}
// The ulcNumProps entry is overloaded with a flag indicating whether that entry was an NTDS entry or not
// Make sure to clear that flag ...
if(ulcNumProps & LDAP_NTDS_ENTRY)
ulcNumProps &= ~LDAP_NTDS_ENTRY;
ulcProps = ulcNumProps;
hr = HrGetPropArrayFromBuffer( lpPropBuf, cbPropBuf,
ulcNumProps, 3, // add 3 extra props for PR_ENTRYID, RECORD_KEY and INSTANCE_KEY
&lpPropArray);
if(HR_FAILED(hr))
goto exit;
//
// Need to scan these props and see if there is a LDAPCert in there...
// If the LDAP cert exists, need to convert it into a MAPI Cert
//
// Only one of PR_WAB_LDAP_RAWCERT and PR_WAB_LDAP_RAWCERTSMIME should be
// processed. If we find the certsmime then don't do the rawcert version
//
for (i=0, i2=-1;i<ulcNumProps;i++)
{
if( lpPropArray[i].ulPropTag == PR_WAB_LDAP_RAWCERT )
i2 = i;
else if ( lpPropArray[i].ulPropTag == PR_WAB_LDAP_RAWCERTSMIME )
{
if (i2 != -1)
{
DWORD j;
// Clear out PR_WAB_LDAP_RAWCERT as unnecessary
for (j=0; j<lpPropArray[i2].Value.MVbin.cValues; j++) {
LocalFreeAndNull((LPVOID *) (&(lpPropArray[i2].Value.MVbin.lpbin[j].lpb)));
lpPropArray[i2].Value.MVbin.lpbin[j].cb = 0;
}
//
// Free up the RawCert as we dont need it now
//
lpPropArray[i2].ulPropTag = PR_NULL;
LocalFreeAndNull((LPVOID *) (&(lpPropArray[i2].Value.MVbin.lpbin)));
}
// Remember which one is PR_WAB_LDAP_RAWCERTSMIME
i2 = i;
break;
}
}
if (i2 != -1)
{
//
// Find the RAWCERT_COUNT - must be one if there is a raw cert
//
ULONG j = 0, ulRawCount = lpPropArray[i2].Value.MVbin.cValues;
// We are putting the MAPI certs in a seperate MAPIAlloced array
// because 1. LDAPCertToMAPICert expects a MAPIAlloced array
// 2. The lpPropArray is LocalAlloced 3. Mixing them both is a
// recipe for disaster
//
Assert(!lpPropCert);
if(sc = MAPIAllocateBuffer(2 * sizeof(SPropValue), &lpPropCert))
goto NoCert;
lpPropCert[0].ulPropTag = PR_USER_X509_CERTIFICATE;
lpPropCert[0].dwAlignPad = 0;
lpPropCert[0].Value.MVbin.cValues = 0;
lpPropCert[1].ulPropTag = PR_WAB_TEMP_CERT_HASH;
lpPropCert[1].dwAlignPad = 0;
lpPropCert[1].Value.MVbin.cValues = 0;
for(j=0;j<ulRawCount;j++)
{
if(lpPropArray[i2].ulPropTag == PR_WAB_LDAP_RAWCERT)
{
// Put the certs into the prop array.
hr = HrLDAPCertToMAPICert(lpPropCert, 0, 1,
(DWORD)(lpPropArray[i2].Value.MVbin.lpbin[j].cb),
(PBYTE)(lpPropArray[i2].Value.MVbin.lpbin[j].lpb),
1);
}
else
{
hr = AddPropToMVPBin(lpPropCert, 0,
lpPropArray[i2].Value.MVbin.lpbin[j].lpb,
lpPropArray[i2].Value.MVbin.lpbin[j].cb, TRUE);
}
LocalFreeAndNull((LPVOID *) (&(lpPropArray[i2].Value.MVbin.lpbin[j].lpb)));
lpPropArray[i2].Value.MVbin.lpbin[j].cb = 0;
}
// If no certs were put into PR_USER_X509_CERTIFICATE, then
// set these props to PR_NULL so they will be removed.
if (0 == lpPropCert[0].Value.MVbin.cValues)
{
lpPropCert[0].ulPropTag = PR_NULL;
lpPropCert[1].ulPropTag = PR_NULL;
}
else if (0 == lpPropCert[1].Value.MVbin.cValues)
{
// It's ok to have no entries in PR_WAB_TEMP_CERT_HASH, but
// the prop should be set to PR_NULL in that case.
lpPropCert[1].ulPropTag = PR_NULL;
}
//
// Free up the RawCert as we dont need it now
//
lpPropArray[i2].ulPropTag = PR_NULL;
LocalFreeAndNull((LPVOID *) (&(lpPropArray[i2].Value.MVbin.lpbin)));
NoCert:
;
}
// Fill in the entry ID.
lpPropArray[ulcProps].Value.bin.cb = cbEntryID;
lpPropArray[ulcProps].Value.bin.lpb = LocalAlloc(LMEM_ZEROINIT, cbEntryID);
if (!lpPropArray[ulcProps].Value.bin.lpb)
{
hr = MAPI_E_NOT_ENOUGH_MEMORY;
goto exit;
}
MemCopy(lpPropArray[ulcProps].Value.bin.lpb, lpEntryID, cbEntryID);
lpPropArray[ulcProps].ulPropTag = PR_ENTRYID;
lpPropArray[ulcProps].dwAlignPad = 0;
ulcProps++;
lpPropArray[ulcProps].ulPropTag = PR_INSTANCE_KEY;
lpPropArray[ulcProps].Value.bin.cb =
lpPropArray[ulcProps - 1].Value.bin.cb;
lpPropArray[ulcProps].Value.bin.lpb =
lpPropArray[ulcProps - 1].Value.bin.lpb;
ulcProps++;
lpPropArray[ulcProps].ulPropTag = PR_RECORD_KEY;
lpPropArray[ulcProps].Value.bin.cb =
lpPropArray[ulcProps - 2].Value.bin.cb;
lpPropArray[ulcProps].Value.bin.lpb =
lpPropArray[ulcProps - 2].Value.bin.lpb;
ulcProps++;
// Create a new MAILUSER object
hr = HrNewMAILUSER(lpIAB, NULL,
MAPI_MAILUSER, 0, &lpMapiProp);
if (HR_FAILED(hr))
{
goto exit;
}
HrSetMAILUSERAccess((LPMAILUSER)lpMapiProp, MAPI_MODIFY);
if (ulcProps && lpPropArray)
{
// If the entry had properties, set them in our returned object
if (HR_FAILED(hr = lpMapiProp->lpVtbl->SetProps(lpMapiProp,
ulcProps, // number of properties to set
lpPropArray, // property array
NULL))) // problem array
{
goto exit;
}
}
if (lpPropCert)
{
// If the entry had properties, set them in our returned object
if (HR_FAILED(hr = lpMapiProp->lpVtbl->SetProps(lpMapiProp,
2, // number of properties to set
lpPropCert, // property array
NULL))) // problem array
{
goto exit;
}
}
HrSetMAILUSERAccess((LPMAILUSER)lpMapiProp, ulFlags);
*lpulObjType = MAPI_MAILUSER;
*lppUnk = (LPUNKNOWN)lpMapiProp;
/*****
#ifdef DEBUG
{
ULONG i;
BOOL bFound = FALSE;
for(i=0;i<ulcProps;i++)
{
if(lpPropArray[i].ulPropTag == PR_WAB_LDAP_LABELEDURI)
{
DebugPrintTrace(( TEXT("***LABELEDURI: %s\n"),lpPropArray[i].Value.LPSZ));
bFound = TRUE;
break;
}
}
if(!bFound)
{
// Put in a test URL for testing purposes only
// Look in the registry for the test URL
TCHAR szKey[MAX_PATH];
ULONG cb = CharSizeOf(szKey);
if(ERROR_SUCCESS == RegQueryValue(HKEY_CURRENT_USER,
"Software\\Microsoft\\WAB\\TestUrl",
szKey,
&cb))
{
lpPropArray[ulcProps].ulPropTag = PR_WAB_LDAP_LABELEDURI;
sc = MAPIAllocateMore(sizeof(TCHAR)*(lstrlen(szKey)+1), lpPropArray,
(LPVOID *)&(lpPropArray[ulcProps].Value.LPSZ));
StrCpyN(lpPropArray[ulcProps].Value.LPSZ, szKey, lstrlen(szKey)+1);
ulcProps++;
}
}
}
#endif //DEBUG
*****/
exit:
if(ulcProps)
ulcProps -= 2; // -2 because the last 2 props are fake ones RECORD_KEY and INSTANCE_KEY
// Free the temp prop value array
LocalFreePropArray(NULL, ulcProps, &lpPropArray);
if(lpPropCert)
MAPIFreeBuffer(lpPropCert);
// Check if we had a deferred error to return instead of success.
if (hrSuccess == hr)
hr = hrDeferred;
DebugTraceResult(LDAP_OpenMAILUSER, hr);
return hr;
}
//*******************************************************************
//
// FUNCTION: LDAPCONT_OpenEntry
//
// PURPOSE: Opens an entry. Calls up to IAB's OpenEntry.
//
// PARAMETERS: lpLDAPCont -> Container object
// cbEntryID = size of entryid
// lpEntryID -> EntryID to open
// lpInterface -> requested interface or NULL for default.
// ulFlags =
// lpulObjType -> returned object type
// lppUnk -> returned object
//
// RETURNS: HRESULT
//
// HISTORY:
// 96/07/08 markdu Created.
//
//*******************************************************************
STDMETHODIMP
LDAPCONT_OpenEntry(
LPLDAPCONT lpLDAPCont,
ULONG cbEntryID,
LPENTRYID lpEntryID,
LPCIID lpInterface,
ULONG ulFlags,
ULONG * lpulObjType,
LPUNKNOWN * lppUnk)
{
HRESULT hr;
#ifdef PARAMETER_VALIDATION
//
// Parameter Validataion
//
// Is this one of mine??
if (IsBadReadPtr(lpLDAPCont, sizeof(LDAPCONT)))
{
return(ResultFromScode(MAPI_E_INVALID_PARAMETER));
}
if (lpLDAPCont->lpVtbl != &vtblLDAPCONT)
{
return(ResultFromScode(MAPI_E_INVALID_PARAMETER));
}
if (lpInterface && IsBadReadPtr(lpInterface, sizeof(IID)))
{
return(ResultFromScode(MAPI_E_INVALID_PARAMETER));
}
if (ulFlags & ~(MAPI_MODIFY | MAPI_DEFERRED_ERRORS | MAPI_BEST_ACCESS))
{
DebugTraceArg(LDAPCONT_OpenEntry, TEXT("Unknown flags"));
//return(ResultFromScode(MAPI_E_UNKNOWN_FLAGS));
}
if (IsBadWritePtr(lpulObjType, sizeof(ULONG)))
{
return(ResultFromScode(MAPI_E_INVALID_PARAMETER));
}
if (IsBadWritePtr(lppUnk, sizeof(LPUNKNOWN)))
{
return(ResultFromScode(MAPI_E_INVALID_PARAMETER));
}
// Check the entryid parameter. It needs to be big enough to hold an entryid.
// NULL is OK
/*
if (lpEntryID)
{
if (cbEntryID < sizeof(MAPI_ENTRYID)
|| IsBadReadPtr((LPVOID)lpEntryID, (UINT)cbEntryID))
{
return(ResultFromScode(MAPI_E_INVALID_PARAMETER));
}
// if (! FValidEntryIDFlags(lpEntryID->abFlags))
// {
// DebugTrace(TEXT("LDAPCONT_OpenEntry(): Undefined bits set in EntryID flags\n"));
// return(ResultFromScode(MAPI_E_INVALID_PARAMETER));
// }
}
*/
#endif // PARAMETER_VALIDATION
EnterCriticalSection(&lpLDAPCont->cs);
// Should just call IAB::OpenEntry()...
hr = lpLDAPCont->lpIAB->lpVtbl->OpenEntry(lpLDAPCont->lpIAB,
cbEntryID, lpEntryID,
lpInterface, ulFlags,
lpulObjType, lppUnk);
LeaveCriticalSection(&lpLDAPCont->cs);
DebugTraceResult(LDAPCONT_OpenEntry, hr);
return hr;
}
//*******************************************************************
//
// FUNCTION: LDAPCONT_ResolveNames
//
// PURPOSE: Resolve names from this container.
//
// PARAMETERS: lpLDAPCont -> Container object
// lptagColSet -> Set of property tags to get from each
// resolved match.
// ulFlags = 0 or MAPI_UNICODE
// lpAdrList -> [in] set of addresses to resolve, [out] resolved
// addresses.
// lpFlagList -> [in/out] resolve flags.
//
// RETURNS: HRESULT
//
// HISTORY:
// 96/07/08 markdu Created.
//
//*******************************************************************
STDMETHODIMP
LDAPCONT_ResolveNames(
LPLDAPCONT lpLDAPCont,
LPSPropTagArray lptagaColSet,
ULONG ulFlags,
LPADRLIST lpAdrList,
LPFlagList lpFlagList)
{
LPADRENTRY lpAdrEntry;
SCODE sc;
ULONG ulAttrIndex;
ULONG ulEntryIndex;
LPSPropTagArray lpPropTags;
LPSPropValue lpPropArray = NULL;
LPSPropValue lpPropArrayNew = NULL;
ULONG ulcPropsNew;
ULONG ulcProps = 0;
HRESULT hr = hrSuccess;
LDAP* pLDAP = NULL;
LDAPMessage* lpResult = NULL;
LDAPMessage* lpEntry;
LPTSTR szAttr;
LPTSTR szDN;
ULONG ulResult = 0;
ULONG ulcEntries;
ULONG ulcAttrs = 0;
LPTSTR* aszAttrs = NULL;
LPTSTR lpServer = NULL;
BOOL fInitDLL = FALSE;
BOOL fRet = FALSE;
LPTSTR szFilter = NULL;
LPTSTR szNameFilter = NULL;
LPTSTR szEmailFilter = NULL;
LPTSTR szBase = NULL;
DWORD dwSzBaseSize = 0;
DWORD dwSzFilterSize = 0;
ULONG ulMsgID;
HWND hDlg;
MSG msg;
LDAPSEARCHPARAMS LDAPSearchParams;
LPPTGDATA lpPTGData=GetThreadStoragePointer();
BOOL bUnicode = (ulFlags & MAPI_UNICODE);
DebugTrace(TEXT("ldapcont.c::LDAPCONT_ResolveNames()\n"));
#ifdef PARAMETER_VALIDATION
if (BAD_STANDARD_OBJ(lpLDAPCont, LDAPCONT_, ResolveNames, lpVtbl))
{
// jump table not large enough to support this method
DebugTraceArg(LDAPCONT_ResolveNames, TEXT("Bad object/vtbl"));
return(ResultFromScode(MAPI_E_INVALID_PARAMETER));
}
// BUGBUG: Should also check lptagColSet, lpAdrList and lpFlagList!
if (ulFlags & ~MAPI_UNICODE)
{
DebugTraceArg(LDAPCONT_ResolveNames, TEXT("Unknown flags used"));
// return(ResultFromScode(MAPI_E_UNKNOWN_FLAGS));
}
#endif // PARAMETER_VALIDATION
// Load the client functions
if (! (fInitDLL = InitLDAPClientLib()))
{
hr = ResultFromScode(MAPI_E_UNCONFIGURED);
goto exit;
}
// open a connection
hr = GetLDAPServerName(lpLDAPCont, &lpServer);
if (hrSuccess != hr)
goto exit;
Assert(lpServer);
// Set up the search base now so we only have to do it once.
hr = GetLDAPSearchBase(&szBase, lpServer);
if (hrSuccess != hr)
goto exit;
// Allocate a new buffer for the attribute array. We will need at most
// as many entries as there are in the input list of MAPI properties.
if(lptagaColSet) // use only if requested .. ignore otherwise
{
lpPropTags = lptagaColSet;// ? lptagaColSet : (LPSPropTagArray)&ptaResolveDefaults;
sc = MAPIAllocateBuffer((lpPropTags->cValues + 1) * sizeof(LPTSTR), // +1 as this needs to be NULL terminated array
(LPVOID *)&aszAttrs);
if (sc)
return(ResultFromScode(sc));
// Cycle through the properties in the array and build a filter to get
// the equivalent LDAP attributes.
ulcAttrs = 0;
for (ulAttrIndex = 0; ulAttrIndex < lpPropTags->cValues; ulAttrIndex++)
{
szAttr = (LPTSTR)MAPIPropToLDAPAttr(lpPropTags->aulPropTag[ulAttrIndex]);
if (szAttr)
{
// Add the attribute to the filter
aszAttrs[ulcAttrs] = szAttr;
ulcAttrs++;
}
}
aszAttrs[ulcAttrs] = NULL;
}
ulResult = SearchWithCancel(&pLDAP, szBase, LDAP_SCOPE_SUBTREE, TEXT(""), NULL,
aszAttrs ? (LPTSTR*)aszAttrs : (LPTSTR*)g_rgszOpenEntryAttrs,
0, &lpResult, lpServer, TRUE, NULL, 0, TRUE, lpAdrList, lpFlagList, FALSE, NULL, bUnicode);
// Stuff the parameters into the structure to be passed to the dlg proc
//LDAPSearchParams.ppLDAP = &pLDAP;
//LDAPSearchParams.szBase = (LPTSTR)szBase;
//LDAPSearchParams.ulScope = LDAP_SCOPE_SUBTREE;
//LDAPSearchParams.ulError = LDAP_SUCCESS;
//LDAPSearchParams.ppszAttrs = aszAttrs ? (LPTSTR*)aszAttrs : (LPTSTR*)g_rgszOpenEntryAttrs; //if specific props requested, ask only for those else ask for everything
//LDAPSearchParams.ulAttrsonly = 0;
//LDAPSearchParams.lplpResult = &lpResult;
//LDAPSearchParams.lpszServer = lpServer;
//LDAPSearchParams.fShowAnim = TRUE;
//LDAPSearchParams.fResolveMultiple = TRUE;
//LDAPSearchParams.lpAdrList = lpAdrList;
//LDAPSearchParams.lpFlagList = lpFlagList;
//LDAPSearchParams.fUseSynchronousBind = FALSE;
//LDAPSearchParams.dwAuthType = 0;
// Check the return codes. Only report fatal errors that caused the cancellation
// of the entire search set, not individual errors that occurred on a single search.
if (LDAP_SUCCESS != ulResult)
{
hr = HRFromLDAPError(ulResult, pLDAP, MAPI_E_CALL_FAILED);
}
exit:
FreeBufferAndNull(&lpServer);
// close the connection
if (pLDAP)
{
gpfnLDAPUnbind(pLDAP);
pLDAP = NULL;
}
// Free the search base memory
LocalFreeAndNull(&szBase);
FreeBufferAndNull((LPVOID *)&aszAttrs);
if (fInitDLL) {
DeinitLDAPClientLib();
}
DebugTraceResult(LDAPCONT_ResolveNames, hr);
return hr;
}
//*******************************************************************
//
// FUNCTION: LDAP_Restrict
//
// PURPOSE: Uses the supplied restriction to set the contentstable
// for this LDAP container
// In reality, we just call find rows and let it do the
// LDAP search to fill this table ..
// We only did this because Outlook needed it to be consistent
// and to do PR_ANR searches. If the search is not a PR_ANR search,
// we will default to the standard VUE_Restrict Method ..
//
// PARAMETERS: lpvue - table view object
// lpres - restriction to convert into LDAP search
// ulFlags -
//
// RETURNS: HRESULT
//
// HISTORY:
// 97/04/04 vikramm Created.
//
//*******************************************************************
STDMETHODIMP
LDAPVUE_Restrict(
LPVUE lpvue,
LPSRestriction lpres,
ULONG ulFlags )
{
HRESULT hr = E_FAIL;
SRestriction sRes = {0}, sPropRes = {0};
SCODE sc;
LPTSTR lpsz = NULL;
BOOL bUnicode = TRUE;
SPropValue SProp = {0};
#if !defined(NO_VALIDATION)
VALIDATE_OBJ(lpvue,LDAPVUE_,Restrict,lpVtbl);
Validate_IMAPITable_Restrict(
lpvue,
lpres,
ulFlags );
#endif
if( lpres->res.resProperty.ulPropTag != PR_ANR_A &&
lpres->res.resProperty.ulPropTag != PR_ANR_W)
{
// dont know what this is, so call the default method ..
return HrVUERestrict(lpvue,
lpres,
ulFlags);
}
bUnicode = (PROP_TYPE(lpres->res.resProperty.ulPropTag)==PT_UNICODE);
LockObj(lpvue->lptadParent);
// Most probably this is an outlook search .. just search and see
// what we can get to fill this table by calling FindRows
lpsz = bUnicode ? lpres->res.resProperty.lpProp->Value.lpszW :
ConvertAtoW(lpres->res.resProperty.lpProp->Value.lpszA);
// Change the Restriction so FindRows can understand it ..
if( !lpsz || !lstrlen(lpsz) )
{
hr = MAPI_E_INVALID_PARAMETER;
goto out;
}
sRes.rt = RES_AND;
sRes.res.resAnd.cRes = 1;
sRes.res.resAnd.lpRes = &sPropRes;
sPropRes.rt = RES_PROPERTY;
sPropRes.res.resProperty.relop = RELOP_EQ;
sPropRes.res.resProperty.lpProp = &SProp;
if(bUnicode)
{
SProp.ulPropTag = sPropRes.res.resProperty.ulPropTag = PR_DISPLAY_NAME_W;
SProp.Value.lpszW = lpres->res.resProperty.lpProp->Value.lpszW;
}
else
{
SProp.ulPropTag = sPropRes.res.resProperty.ulPropTag = PR_DISPLAY_NAME_A;
SProp.Value.lpszA = lpres->res.resProperty.lpProp->Value.lpszA;
}
/*
{
// create a new restriction based on the PR_ANR displayname
// passed on to us
//
LDAP_SEARCH_PARAMS LDAPsp ={0};
if(lstrlen(lpsz) < MAX_UI_STR)
StrCpyN(LDAPsp.szData[ldspDisplayName], lpsz);
else
{
CopyMemory(LDAPsp.szData[ldspDisplayName],lpsz,sizeof(TCHAR)*(MAX_UI_STR-2));
LDAPsp.szData[ldspDisplayName][MAX_UI_STR-1]='\0';
}
hr = HrGetLDAPSearchRestriction(LDAPsp, &sRes);
}
*/
hr = lpvue->lpVtbl->FindRow(lpvue,
&sRes,
BOOKMARK_BEGINNING,
0);
// Clear out cached rows from before, if any
{
/*
ULONG cDeleted = 0;
hr = lpvue->lptadParent->lpVtbl->HrDeleteRows(lpvue->lptadParent,
TAD_ALL_ROWS, // ulFlags
NULL,
&cDeleted);
if (hrSuccess != hr)
goto out;
// Also need to release any current views on the object or caller will get
// hosed ...
// Replace the row set in the view
COFree(lpvue, lpvue->parglprows);
lpvue->parglprows = NULL;
lpvue->bkEnd.uliRow = 0;
*/
}
// Now that we've filled up the table with more entries, set the ANR restriction on it
hr = HrVUERestrict( lpvue,
lpres,
ulFlags);
out:
sc = GetScode(hr);
/*
if(sRes.res.resAnd.lpRes)
MAPIFreeBuffer(sRes.res.resAnd.lpRes);
*/
UnlockObj(lpvue->lptadParent);
if(!bUnicode)
LocalFreeAndNull(&lpsz);
return HrSetLastErrorIds(lpvue, sc, 0);
}
//*******************************************************************
//
// FUNCTION: HrLDAPEntryToMAPIEntry
//
// PURPOSE: Converts an LDAP entry into a MAPI entry
//
// PARAMETERS:
//
// RETURNS: HRESULT
//
//*******************************************************************
HRESULT HrLDAPEntryToMAPIEntry(LDAP * pLDAP,
LDAPMessage* lpEntry,
LPTSTR lpEIDData1, // for creating entryid
ULONG ulcNumAttrs,
BOOL bIsNTDSEntry,
ULONG * lpulcProps,
LPSPropValue * lppPropArray)
{
LPTSTR szDN;
SCODE sc;
HRESULT hr = E_FAIL;
LPSPropValue lpPropArray = NULL;
ULONG ulcProps = 0;
LPBYTE lpBuf = NULL;
ULONG cbBuf = 0;
LDAPSERVERPARAMS Params = {0};
LPTSTR lpServer = NULL;
// initialize search control parameters
GetLDAPServerParams(lpEIDData1, &Params);
if(!Params.lpszName || !lstrlen(Params.lpszName))
{
DWORD cchSize = (lstrlen(lpEIDData1)+1);
Params.lpszName = LocalAlloc(LMEM_ZEROINIT, sizeof(TCHAR)*cchSize);
if(!Params.lpszName)
goto exit;
StrCpyN(Params.lpszName, lpEIDData1, cchSize);
}
lpServer = Params.lpszName;
if(!ulcNumAttrs)
ulcNumAttrs = NUM_ATTRMAP_ENTRIES;
else
ulcNumAttrs += 2; //add space for pr_instance_key and pr_record_key
// Allocate a new buffer for the MAPI property array.
sc = MAPIAllocateBuffer((ulcNumAttrs+ NUM_EXTRA_PROPS) * sizeof(SPropValue),
(LPVOID *)&lpPropArray);
if (sc)
{
hr = ResultFromScode(sc);
goto exit;
}
// Cycle through the attributes and store them.
hr = TranslateAttrs(pLDAP, lpEntry, lpServer, &ulcProps, lpPropArray);
if (hrSuccess != hr)
{
goto exit;
}
// Fix up the PR_DISPLAY_NAME
// MSN's ldap server returns cn = email address.
// If this is the case, set it to GIVEN_NAME + SURNAME.
// Exchange DS wont return a display name, just returns a
// sn + givenName. In that case build a display name
FixPropArray(lpPropArray, &ulcProps);
// Generate an Entry ID using the DN of the entry.
szDN = gpfnLDAPGetDN(pLDAP, lpEntry);
if (NULL == szDN)
{
hr = HRFromLDAPError(LDAP_ERROR, pLDAP, MAPI_E_CALL_FAILED);
goto exit;
}
//
// Convert the lpPropArray, so far, into a flat buffer which we shall
// cache inside the LDAP entryid .. Important to note that this
// cached prop array does not have entryid information in it ...
// The entryid information will need to be tagged on at such time when
// we extract the prop array from the flat buffer in LDAP_OpenEntry
//
hr = HrGetBufferFromPropArray( ulcProps,
lpPropArray,
&cbBuf,
&lpBuf);
if (hrSuccess != hr)
{
goto exit;
}
hr = CreateWABEntryID(WAB_LDAP_MAILUSER,
(LPTSTR)lpEIDData1,// lpvue->lpvDataSource, // server name
(LPVOID)szDN,
(LPVOID)lpBuf,
(bIsNTDSEntry ? (ulcProps|LDAP_NTDS_ENTRY) : ulcProps),
cbBuf,
(LPVOID)lpPropArray,
(LPULONG) (&lpPropArray[ulcProps].Value.bin.cb),
(LPENTRYID *)&lpPropArray[ulcProps].Value.bin.lpb);
// Free the DN memory now that it is copied.
gpfnLDAPMemFree(szDN);
if (hrSuccess != hr)
{
goto exit;
}
lpPropArray[ulcProps].ulPropTag = PR_ENTRYID;
lpPropArray[ulcProps].dwAlignPad = 0;
ulcProps++;
// Make certain we have proper indicies.
// For now, we will equate PR_INSTANCE_KEY and PR_RECORD_KEY to PR_ENTRYID.
lpPropArray[ulcProps].ulPropTag = PR_INSTANCE_KEY;
lpPropArray[ulcProps].Value.bin.cb =
lpPropArray[ulcProps - 1].Value.bin.cb;
lpPropArray[ulcProps].Value.bin.lpb =
lpPropArray[ulcProps - 1].Value.bin.lpb;
ulcProps++;
lpPropArray[ulcProps].ulPropTag = PR_RECORD_KEY;
lpPropArray[ulcProps].Value.bin.cb =
lpPropArray[ulcProps - 2].Value.bin.cb;
lpPropArray[ulcProps].Value.bin.lpb =
lpPropArray[ulcProps - 2].Value.bin.lpb;
ulcProps++;
*lpulcProps = ulcProps;
*lppPropArray = lpPropArray;
exit:
if(HR_FAILED(hr) && lpPropArray)
MAPIFreeBuffer(lpPropArray);
if(lpBuf)
LocalFreeAndNull(&lpBuf);
FreeLDAPServerParams(Params);
return hr;
}
//*******************************************************************
//
// FUNCTION: LDAP_FindRow
//
// PURPOSE: Search LDAP server, and add rows to the table object
// for server entries that match the restriction.
//
// PARAMETERS: lpvue - table view object
// lpres - restriction to convert into LDAP search
// bkOrigin - current bookmark
// ulFlags -
//
// If we are doing an advanced search, we will hack through an advanced
// filter instead of lpres and pass a flag of LDAP_USE_ADVANCED_FILTER
//
// RETURNS: HRESULT
//
// HISTORY:
// 96/07/10 markdu Created.
//
//*******************************************************************
STDMETHODIMP
LDAPVUE_FindRow(
LPVUE lpvue,
LPSRestriction lpSRes,
BOOKMARK bkOrigin,
ULONG ulFlags )
{
SCODE sc;
HRESULT hr;
HRESULT hrDeferred = hrSuccess;
LPMAILUSER lpMailUser = NULL;
LPMAPIPROP lpMapiProp = NULL;
ULONG ulcProps = 0;
LPSPropValue lpPropArray = NULL;
LPSRowSet lpSRowSet = NULL;
LDAPMessage* lpResult = NULL;
LDAPMessage* lpEntry;
LDAP* pLDAP = NULL;
LPTSTR szDN;
ULONG ulResult;
ULONG ulcEntries;
ULONG ulIndex = 0;
LPTSTR szFilter = NULL;
LPTSTR szNTFilter = NULL;
LPTSTR szSimpleFilter = NULL;
LPTSTR szBase = NULL;
BOOL fInitDLL = FALSE;
BOOL fSimpleSearch = FALSE;
LPTSTR lpAdvFilter = NULL;
LPSRestriction lpres = NULL;
BOOL bIsNTDSEntry = FALSE;
BOOL bUnicode = lpvue->lptadParent->bMAPIUnicodeTable;
if (ulFlags & LDAP_USE_ADVANCED_FILTER)
{
lpAdvFilter = (LPTSTR) lpSRes;
ulFlags = ulFlags & ~LDAP_USE_ADVANCED_FILTER;
}
else
{
lpres = lpSRes;
}
#if !defined(NO_VALIDATION)
VALIDATE_OBJ(lpvue,LDAPVUE_,FindRow,lpVtbl);
/*
Validate_IMAPITable_FindRow(
lpvue,
lpres,
bkOrigin,
ulFlags );
*/
if ( FBadBookmark(lpvue,bkOrigin) )
{
DebugTrace(TEXT("LDAP_FindRow() - Bad parameter(s) passed\n") );
return HrSetLastErrorIds(lpvue, MAPI_E_INVALID_PARAMETER, 0);
}
#endif
LockObj(lpvue->lptadParent);
if(lpres)
{
// Convert the SRestriction into filters for ldap_search
hr = ParseSRestriction(lpres, &szFilter, &szSimpleFilter, &szNTFilter, FIRST_PASS, bUnicode);
if (hrSuccess != hr)
goto exit;
}
else
szFilter = lpAdvFilter;
// Set up the search base now so we only have to do it once.
hr = GetLDAPSearchBase(&szBase, (LPTSTR)lpvue->lpvDataSource);
if (hrSuccess != hr)
goto exit;
fSimpleSearch = bIsSimpleSearch((LPTSTR)lpvue->lpvDataSource);
if(fSimpleSearch && lpAdvFilter)
szSimpleFilter = lpAdvFilter;
// Load the client functions
if (! (fInitDLL = InitLDAPClientLib()))
{
hr = ResultFromScode(MAPI_E_UNCONFIGURED);
goto exit;
}
// Read the matching entries
ulResult = SearchWithCancel(&pLDAP,
(LPTSTR)szBase, LDAP_SCOPE_SUBTREE,
(LPTSTR) (fSimpleSearch ? szSimpleFilter : szFilter),
(LPTSTR)szNTFilter,
(LPTSTR*)g_rgszOpenEntryAttrs, // Get all the attributes the first time only //g_rgszFindRowAttrs,
0, &lpResult, (LPTSTR)lpvue->lpvDataSource,
FALSE, NULL, 0,
FALSE, NULL, NULL, FALSE, &bIsNTDSEntry,
TRUE); //unicode by default
// BUG 34201 If the search was unsuccessful because of an unknown attribute
// type, try a second search that does not use givenName. This fixes searches
// on ldap.itd.umich.edu, which do not recognize givenName.
if ( lpres &&
(LDAP_UNDEFINED_TYPE == ulResult || LDAP_UNWILLING_TO_PERFORM == ulResult) )
{
// close the connection since we will open a new one
if (pLDAP)
{
gpfnLDAPUnbind(pLDAP);
pLDAP = NULL;
}
if (!bIsNTDSEntry)
{
// Free the search filter memory
LocalFreeAndNull(&szFilter);
LocalFreeAndNull(&szNTFilter);
LocalFreeAndNull(&szSimpleFilter);
DebugTrace(TEXT("First try failed, trying umich semantics...\n"));
hr = ParseSRestriction(lpres, &szFilter, &szSimpleFilter, &szNTFilter, UMICH_PASS, bUnicode);
}
else
hr = hrSuccess;
if (hrSuccess == hr)
{
ulResult = SearchWithCancel(&pLDAP,
(LPTSTR)szBase, LDAP_SCOPE_SUBTREE,
(LPTSTR)(fSimpleSearch ? szSimpleFilter : szFilter),
NULL,
(LPTSTR*)g_rgszOpenEntryAttrs, //g_rgszFindRowAttrs,
0, &lpResult, (LPTSTR)lpvue->lpvDataSource,
FALSE, NULL, 0,
FALSE, NULL, NULL, FALSE, &bIsNTDSEntry,
TRUE); //unicode by default?
}
}
if (LDAP_SUCCESS != ulResult)
{
DebugTrace(TEXT("LDAP_FindRow: ldap_search returned %d.\n"), ulResult);
hr = HRFromLDAPError(ulResult, pLDAP, MAPI_E_NOT_FOUND);
// See if the result was the special value that tells us there were more
// entries than could be returned. If so, we need to check if we got
// some of the entries or none of the entries.
if ((ResultFromScode(MAPI_E_UNABLE_TO_COMPLETE) == hr) &&
(ulcEntries = gpfnLDAPCountEntries(pLDAP, lpResult)))
{
// We got some results back. Return MAPI_W_PARTIAL_COMPLETION
// instead of success.
hrDeferred = ResultFromScode(MAPI_W_PARTIAL_COMPLETION);
hr = hrSuccess;
}
else
{
goto exit;
}
}
else
ulcEntries = gpfnLDAPCountEntries(pLDAP, lpResult); // Count the entries.
if (0 == ulcEntries)
{
// 96/08/08 markdu BUG 35481 No error and no results means "not found"
hr = ResultFromScode(MAPI_E_NOT_FOUND);
goto exit;
}
// Allocate an SRowSet to hold the entries.
sc = MAPIAllocateBuffer(sizeof(SRowSet) + ulcEntries * sizeof(SRow), (LPVOID *)&lpSRowSet);
if (sc)
{
DebugTrace(TEXT("Allocation of SRowSet failed\n"));
hr = ResultFromScode(sc);
goto exit;
}
// Set the number of rows to zero in case we encounter an error and
// then try to free the row set before any rows were added.
lpSRowSet->cRows = 0;
// get the first entry in the search result
lpEntry = gpfnLDAPFirstEntry(pLDAP, lpResult);
if (NULL == lpEntry)
{
DebugTrace(TEXT("LDAP_FindRow: No entry found for %s.\n"), szFilter);
hr = HRFromLDAPError(LDAP_ERROR, pLDAP, MAPI_E_CORRUPT_DATA);
if (hrSuccess == hr)
{
// No error occurred according to LDAP, which in theory means that there
// were no more entries. However, this should not happen, so return error.
hr = ResultFromScode(MAPI_E_CORRUPT_DATA);
}
goto exit;
}
while (lpEntry)
{
hr = HrLDAPEntryToMAPIEntry( pLDAP, lpEntry,
(LPTSTR) lpvue->lpvDataSource,
0, // standard number of attributes
bIsNTDSEntry,
&ulcProps,
&lpPropArray);
if (hrSuccess != hr)
{
continue;
}
if(!bUnicode) // convert native UNICODE to ANSI if we need to ...
{
if(sc = ScConvertWPropsToA((LPALLOCATEMORE) (&MAPIAllocateMore), lpPropArray, ulcProps, 0))
goto exit;
}
// Put it in the RowSet
lpSRowSet->aRow[ulIndex].cValues = ulcProps; // number of properties
lpSRowSet->aRow[ulIndex].lpProps = lpPropArray; // LPSPropValue
// Get the next entry.
lpEntry = gpfnLDAPNextEntry(pLDAP, lpEntry);
ulIndex++;
}
// Free the search results memory
gpfnLDAPMsgFree(lpResult);
lpResult = NULL;
// Add the rows to the table
lpSRowSet->cRows = ulIndex;
hr = lpvue->lptadParent->lpVtbl->HrModifyRows(lpvue->lptadParent, 0, lpSRowSet);
if (hrSuccess != hr)
goto exit;
// Always reset the bookmark to the first row. This is done because the
// restriction passed in may not match any row in the table since the
// attributes returned from the LDAP search do not always include the
// attributes used to perform the search (eg country, organization)
lpvue->bkCurrent.uliRow = 0;
exit:
// free the search results memory
if (lpResult)
{
gpfnLDAPMsgFree(lpResult);
lpResult = NULL;
}
// close the connection
if (pLDAP)
{
gpfnLDAPUnbind(pLDAP);
pLDAP = NULL;
}
// Free the search filter memory
if(lpres)
{
LocalFreeAndNull(&szFilter);
LocalFreeAndNull(&szNTFilter);
LocalFreeAndNull(&szSimpleFilter);
}
LocalFreeAndNull(&szBase);
// Free the row set memory
FreeProws(lpSRowSet);
if (fInitDLL)
DeinitLDAPClientLib();
UnlockObj(lpvue->lptadParent);
// Check if we had a deferred error to return instead of success.
if (hrSuccess == hr)
{
hr = hrDeferred;
}
DebugTraceResult(LDAPCONT_ResolveNames, hr);
return hr;
}
//*******************************************************************
//
// FUNCTION: InitLDAPClientLib
//
// PURPOSE: Load the LDAP client libray and get the proc addrs.
//
// PARAMETERS: None.
//
// RETURNS: TRUE if successful, FALSE otherwise.
//
// HISTORY:
// 96/07/05 markdu Created.
//
//*******************************************************************
BOOL InitLDAPClientLib(void)
{
#ifdef WIN16
return FALSE;
#else // Disable until ldap16.dll is available.
// See if we already initialized.
if (NULL == ghLDAPDLLInst)
{
Assert(ulLDAPDLLRefCount == 0);
// open LDAP client library
// BUGBUG: Requires dll to be in system directory (neilbren)
ghLDAPDLLInst = LoadLibrary(cszLDAPClientDLL);
if (!ghLDAPDLLInst)
{
DebugTrace(TEXT("InitLDAPClientLib: Failed to LoadLibrary WLDAP32.DLL.\n"));
return FALSE;
}
// cycle through the API table and get proc addresses for all the APIs we
// need
if (!GetApiProcAddresses(ghLDAPDLLInst,LDAPAPIList,NUM_LDAPAPI_PROCS))
{
DebugTrace(TEXT("InitLDAPClientLib: Failed to load LDAP API.\n"));
// Unload the library we just loaded.
if (ghLDAPDLLInst)
{
FreeLibrary(ghLDAPDLLInst);
ghLDAPDLLInst = NULL;
}
return FALSE;
}
// Add an additional AddRef here so that this library stays loaded once
// it is loaded. This improves performance.
// The IAB_Neuter function will take care of calling the matching DeInit()
ulLDAPDLLRefCount++;
}
ulLDAPDLLRefCount++;
return TRUE;
#endif
}
//*******************************************************************
//
// FUNCTION: DeinitLDAPClientLib
//
// PURPOSE: decrement refcount on LDAP CLient library and
// release if 0.
//
// PARAMETERS: None.
//
// RETURNS: current refcount
//
// HISTORY:
// 96/07/12 BruceK Created.
//
//*******************************************************************
ULONG DeinitLDAPClientLib(void)
{
if (0 != ulLDAPDLLRefCount)
{
if (-- ulLDAPDLLRefCount == 0)
{
UINT nIndex;
// No clients using the LDAPCLI library. Release it.
if (ghLDAPDLLInst)
{
FreeLibrary(ghLDAPDLLInst);
ghLDAPDLLInst = NULL;
}
// cycle through the API table and NULL proc addresses for all the APIs
for (nIndex = 0; nIndex < NUM_LDAPAPI_PROCS; nIndex++)
{
*LDAPAPIList[nIndex].ppFcnPtr = NULL;
}
}
}
return(ulLDAPDLLRefCount);
}
//*******************************************************************
//
// FUNCTION: GetApiProcAddresses
//
// PURPOSE: Gets proc addresses for a table of functions
//
// PARAMETERS: hModDLL - dll from which to load the procs
// pApiProcList - array of proc names and pointers
// nApiProcs - number of procs in array
//
// RETURNS: TRUE if successful, FALSE if unable to retrieve
// any proc address in table
//
// HISTORY:
// 96/07/08 markdu Created.
//
//*******************************************************************
BOOL GetApiProcAddresses(
HMODULE hModDLL,
APIFCN * pApiProcList,
UINT nApiProcs)
{
UINT nIndex;
DebugTrace(TEXT("ldapcont.c::GetApiProcAddresses()\n"));
// cycle through the API table and get proc addresses for all the APIs we
// need
for (nIndex = 0;nIndex < nApiProcs;nIndex++)
{
if (!(*pApiProcList[nIndex].ppFcnPtr = (PVOID) GetProcAddress(hModDLL,
pApiProcList[nIndex].pszName)))
{
DebugTrace(TEXT("Unable to get address of function %s\n"),
pApiProcList[nIndex].pszName);
for (nIndex = 0;nIndex<nApiProcs;nIndex++)
*pApiProcList[nIndex].ppFcnPtr = NULL;
return FALSE;
}
}
return TRUE;
}
//*******************************************************************
//
// FUNCTION: MAPIPropToLDAPAttr
//
// PURPOSE: Get LDAP attribute equivalent to MAPI property.
//
// PARAMETERS: ulPropTag - MAPI property to map to LDAP attribute
//
// RETURNS: Pointer to string with LDAP attribute name if found,
// NULL otherwise.
//
// HISTORY:
// 96/06/28 markdu Created.
//
//*******************************************************************
LPTSTR MAPIPropToLDAPAttr(
const ULONG ulPropTag)
{
ULONG ulIndex;
for (ulIndex=0;ulIndex<NUM_ATTRMAP_ENTRIES;ulIndex++)
{
if (ulPropTag == gAttrMap[ulIndex].ulPropTag)
{
return (LPTSTR)gAttrMap[ulIndex].pszAttr;
}
}
// PR_WAB_CONF_SERVERS is not a constant but a named prop tag - hence its not
// part of the array above
if(ulPropTag == PR_WAB_CONF_SERVERS)
return (LPTSTR)cszAttr_conferenceInformation;
if(ulPropTag == PR_WAB_MANAGER)
return (LPTSTR)cszAttr_Manager;
if(ulPropTag == PR_WAB_REPORTS)
return (LPTSTR)cszAttr_Reports;
if(ulPropTag == PR_WAB_IPPHONE)
return (LPTSTR)cszAttr_IPPhone;
// property not found
return NULL;
}
//*******************************************************************
//
// FUNCTION: LDAPAttrToMAPIProp
//
// PURPOSE: Get MAPI property equivalent to LDAP attribute.
//
// PARAMETERS: szAttr - points to LDAP attribute name
//
// RETURNS: MAPI property tag if LDAP attribute name is found,
// PR_NULL otherwise.
//
// HISTORY:
// 96/06/28 markdu Created.
//
//*******************************************************************
ULONG LDAPAttrToMAPIProp(
const LPTSTR szAttr)
{
ULONG ulIndex;
for (ulIndex=0;ulIndex<NUM_ATTRMAP_ENTRIES;ulIndex++)
{
if (!lstrcmpi(szAttr, gAttrMap[ulIndex].pszAttr))
{
return gAttrMap[ulIndex].ulPropTag;
}
}
// PR_WAB_CONF_SERVERS is not a constant but a named prop tag - hence its not
// part of the array above
if(!lstrcmpi(szAttr, cszAttr_conferenceInformation))
return PR_WAB_CONF_SERVERS;
if(!lstrcmpi(szAttr, cszAttr_Manager))
return PR_WAB_MANAGER;
if(!lstrcmpi(szAttr, cszAttr_Reports))
return PR_WAB_REPORTS;
if(!lstrcmpi(szAttr, cszAttr_IPPhone))
return PR_WAB_IPPHONE;
// attribute not found
return PR_NULL;
}
//*******************************************************************
//
// FUNCTION: ParseSRestriction
//
// PURPOSE: Generate Search Base and Search Filter strings for
// LDAP search from MAPI SRestriction structure.
//
// PARAMETERS: lpRes - MAPI SRestriction structure to "parse"
// receive the search base string.
// lplpszFilter - receives buffer to hold search filter string.
// dwPass - which pass through this function (first pass
// is zero (0)). This allows
// reuse of this function with different behaviour each
// time (for example, to do a backup search with a
// different filter)
// bUnicode - TRUE if the Restriction contains UNICODE strings
// FALSE otherwise
//
// RETURNS: MAPI_E_INVALID_PARAMETER if the restriction cannot
// be converted in to filters, hrSuccess otherwise.
//
// HISTORY:
// 96/07/10 markdu Created.
// 96/08/05 markdu BUG 34023 Always start with a filter that causes
// the search to return only objects of class 'person'.
// If organization is supplied, add it to the base of the
// search instead of the filter to narrow the scope.
// 96/08/07 markdu BUG 34201 Added dwPass to allow backup searches.
// 96/10/18 markdu Removed base string, since it is now in registry.
//
//*******************************************************************
HRESULT ParseSRestriction(
LPSRestriction lpRes,
LPTSTR FAR * lplpszFilter,
LPTSTR * lplpszSimpleFilter,
LPTSTR * lplpszNTFilter,
DWORD dwPass,
BOOL bUnicode)
{
HRESULT hr = hrSuccess;
LPTSTR szTemp = NULL;
LPTSTR szEmailFilter = NULL;
LPTSTR szNameFilter = NULL;
LPTSTR szNTFilter = NULL;
LPTSTR szAltEmailFilter = NULL;
LPTSTR szSimpleFilter = NULL;
LPTSTR lpszInputCopy = NULL;
LPTSTR lpszInput;
ULONG ulcIllegalChars = 0;
ULONG ulcProps;
ULONG ulIndex;
ULONG ulcbFilter;
LPSRestriction lpResArray;
LPSPropertyRestriction lpPropRes;
LPTSTR lpsz = NULL;
ULONG ulPropTag = 0;
// Make sure we can write to lplpszFilter.
#ifdef PARAMETER_VALIDATION
if (IsBadReadPtr(lpRes, sizeof(SRestriction)))
{
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
if (IsBadWritePtr(lplpszFilter, sizeof(LPTSTR)))
{
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
#endif // PARAMETER_VALIDATION
// Currently we only support AND restrictions
if (RES_AND != lpRes->rt)
{
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
// We need to have at least one prop
ulcProps = lpRes->res.resAnd.cRes;
if (1 > ulcProps)
{
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
lpResArray = lpRes->res.resAnd.lpRes;
for (ulIndex=0;ulIndex<ulcProps;ulIndex++)
{
// Currently expect only SPropertyRestriction structures.
if (RES_PROPERTY != lpResArray[ulIndex].rt)
{
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
// Currently expect only EQ operations.
lpPropRes = &lpResArray[ulIndex].res.resProperty;
if (RELOP_EQ != lpPropRes->relop)
{
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
ulPropTag = lpPropRes->lpProp->ulPropTag;
if(bUnicode)
{
lpsz = lpPropRes->lpProp->Value.lpszW;
}
else // <note> assumes UNICODE defined
{
LocalFreeAndNull(&lpsz);
lpsz = ConvertAtoW(lpPropRes->lpProp->Value.lpszA);
if (NULL == lpsz)
{
hr = ResultFromScode(MAPI_E_NOT_ENOUGH_MEMORY);
goto exit;
}
if(PROP_TYPE(ulPropTag) == PT_STRING8)
ulPropTag = CHANGE_PROP_TYPE(ulPropTag, PT_UNICODE);
}
// 96/12/04 markdu BUG 11923 See if any characters in the input need to be escaped.
if (lpsz)
ulcIllegalChars = CountIllegalChars(lpsz);
if (0 == ulcIllegalChars)
{
lpszInput = lpsz;
}
else
{
ULONG cchSize = lstrlen(lpsz) + ulcIllegalChars*2 + 1;
// Allocate a copy of the input, large enough to replace the illegal chars
// with escaped versions - each escaped char needs 2 more spaces '\xx'.
lpszInputCopy = LocalAlloc( LMEM_ZEROINIT,
sizeof(TCHAR)*cchSize);
if (NULL == lpszInputCopy)
{
hr = ResultFromScode(MAPI_E_NOT_ENOUGH_MEMORY);
goto exit;
}
EscapeIllegalChars(lpsz, lpszInputCopy, cchSize);
lpszInput = lpszInputCopy;
}
// If this is the display name property,
// then we make the special filter for SimpleSearch
if (PR_DISPLAY_NAME == ulPropTag)
{
hr = BuildBasicFilter(
&szNTFilter,
(LPTSTR)cszAttr_anr,
lpszInput,
FALSE);
if (hrSuccess != hr)
{
return hr;
}
hr = CreateSimpleSearchFilter(
&szNameFilter,
&szAltEmailFilter,
&szSimpleFilter,
lpszInput,
dwPass);
if (hrSuccess != hr)
{
return hr;
}
}
if (PR_EMAIL_ADDRESS == ulPropTag)
{
// Only take the text up to the first space, comma, or tab.
szTemp = lpszInput;
while(*szTemp != '\0' && (! IsSpace(szTemp)) && *szTemp != '\t' && *szTemp != ',' )
{
szTemp = CharNext(szTemp);
}
*szTemp = '\0';
// Note UMich server does not allow wildcards in email search.
hr = BuildBasicFilter(
&szEmailFilter,
(LPTSTR)cszAttr_mail,
lpszInput,
(UMICH_PASS != dwPass));
if (hrSuccess != hr)
{
goto exit;
}
}
// We are done with lpszInputCopy.
LocalFreeAndNull(&lpszInputCopy);
} // for
// Put the simple filter togethor
if (szSimpleFilter)
{
if (szEmailFilter)
{
// Both fields were filled in, so AND them together
hr = BuildOpFilter(
lplpszSimpleFilter,
szEmailFilter,
szSimpleFilter,
FILTER_OP_AND);
if (hrSuccess != hr)
{
goto exit;
}
}
else if (szAltEmailFilter)
{
// No email field was given, so OR in the alternate email filter.
hr = BuildOpFilter( lplpszSimpleFilter,
szAltEmailFilter,
szSimpleFilter,
FILTER_OP_OR);
if (hrSuccess != hr)
{
goto exit;
}
}
else
{
DWORD cchSize = (lstrlen(szSimpleFilter) + 1);
*lplpszSimpleFilter = LocalAlloc(LMEM_ZEROINIT, sizeof(TCHAR) * cchSize);
if (NULL == *lplpszSimpleFilter)
{
hr = ResultFromScode(MAPI_E_NOT_ENOUGH_MEMORY);
goto exit;
}
StrCpyN(*lplpszSimpleFilter, szSimpleFilter, cchSize);
}
}
else if (szEmailFilter)
{
// Email was the only filter we got. This filter will not include common name.
// 96/10/02 markdu BUG 37426 If the filter does not include common name,
// add to the filter so that we only get entries that have a common name.
// Otherwise the entry will not be displayed.
hr = BuildOpFilter(
lplpszSimpleFilter,
(LPTSTR)cszCommonNamePresentFilter,
szEmailFilter,
FILTER_OP_AND);
if (hrSuccess != hr)
{
goto exit;
}
}
// Put the filter together.
if (szNameFilter)
{
if (szEmailFilter)
{
// Both fields were filled in, so AND them together
hr = BuildOpFilter(
lplpszFilter,
szEmailFilter,
szNameFilter,
FILTER_OP_AND);
if (hrSuccess != hr)
{
goto exit;
}
hr = BuildOpFilter(
lplpszNTFilter,
szEmailFilter,
szNTFilter,
FILTER_OP_AND);
if (hrSuccess != hr)
{
goto exit;
}
}
else if (szAltEmailFilter)
{
// No email field was given, so OR in the alternate email filter.
hr = BuildOpFilter(
lplpszFilter,
szAltEmailFilter,
szNameFilter,
FILTER_OP_OR);
if (hrSuccess != hr)
{
goto exit;
}
hr = BuildOpFilter(
lplpszNTFilter,
szAltEmailFilter,
szNTFilter,
FILTER_OP_OR);
if (hrSuccess != hr)
{
goto exit;
}
}
else
{
DWORD cchSize = (lstrlen(szNameFilter) + 1);
*lplpszFilter = LocalAlloc(LMEM_ZEROINIT, sizeof(TCHAR)*cchSize);
if (NULL == *lplpszFilter)
{
hr = ResultFromScode(MAPI_E_NOT_ENOUGH_MEMORY);
goto exit;
}
StrCpyN(*lplpszFilter, szNameFilter, cchSize);
cchSize = lstrlen(szNTFilter) + 1;
*lplpszNTFilter = LocalAlloc(LMEM_ZEROINIT, sizeof(TCHAR)*cchSize);
if (NULL == *lplpszNTFilter)
{
hr = ResultFromScode(MAPI_E_NOT_ENOUGH_MEMORY);
goto exit;
}
StrCpyN(*lplpszNTFilter, szNTFilter, cchSize);
}
}
else if (szEmailFilter)
{
// Email was the only filter we got. This filter will not include common name.
// 96/10/02 markdu BUG 37426 If the filter does not include common name,
// add to the filter so that we only get entries that have a common name.
// Otherwise the entry will not be displayed.
hr = BuildOpFilter(
lplpszFilter,
(LPTSTR)cszCommonNamePresentFilter,
szEmailFilter,
FILTER_OP_AND);
if (hrSuccess != hr)
{
goto exit;
}
}
else
{
// We did not generate a filter
hr = ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
exit:
// Free the temporary strings
LocalFreeAndNull(&szNameFilter);
LocalFreeAndNull(&szNTFilter);
LocalFreeAndNull(&szAltEmailFilter);
LocalFreeAndNull(&szEmailFilter);
LocalFreeAndNull(&szSimpleFilter);
if(!bUnicode)
LocalFreeAndNull(&lpsz);
if (hrSuccess != hr)
{
LocalFreeAndNull(lplpszFilter);
}
return hr;
}
//*******************************************************************
//
// FUNCTION: CreateSimpleSearchFilter
//
// PURPOSE: Generate Search Filter strings simple search.
//
// PARAMETERS: lplpszFilter - pointer to receive the search filter
// string buffer.
// lplpszAltFilter - pointer to receive the alternate
// filter string buffer.
// lpszInput - string to put into the filter
// dwPass - which pass through this function (first pass
// is zero (0)). This allows
// reuse of this function with different behaviour each
// time (for example, to do a backup search with a
// different filter)
//
// RETURNS: HRESULT
//
// HISTORY:
// 96/10/02 markdu Created to fix BUG 37424.
// 96/10/23 markdu Added UMICH_PASS.
//
//*******************************************************************
HRESULT CreateSimpleSearchFilter(
LPTSTR FAR * lplpszFilter,
LPTSTR FAR * lplpszAltFilter,
LPTSTR FAR * lplpszSimpleFilter,
LPTSTR lpszInput,
DWORD dwPass)
{
HRESULT hr = hrSuccess;
DWORD dwSizeOfFirst = 0;
LPTSTR szFirst = NULL;
LPTSTR szTemp = NULL;
LPTSTR szLast;
LPTSTR szCommonName = NULL;
LPTSTR szFirstSurname = NULL;
LPTSTR szLastSurname = NULL;
LPTSTR szGivenName = NULL;
LPTSTR szFirstLast = NULL;
LPTSTR szLastFirst = NULL;
// Prepare the (cn=Input*) filter
hr = BuildBasicFilter(
lplpszSimpleFilter,
(LPTSTR)cszAttr_cn,
lpszInput,
TRUE);
if (hrSuccess != hr)
{
goto exit;
}
// Make a copy of the input string
{
DWORD cchSize = (lstrlen(lpszInput) + 1);
szFirst = LocalAlloc(LMEM_ZEROINIT, sizeof(TCHAR)*cchSize);
if (NULL == szFirst)
{
hr = ResultFromScode(MAPI_E_NOT_ENOUGH_MEMORY);
goto exit;
}
StrCpyN(szFirst, lpszInput, cchSize);
}
// Attempt to break the input string into multiple strings.
// szFirst will be the string up to the first space, tab, or comma.
// szLast will be the rest of the string.
szLast = szFirst;
while(*szLast != '\0' && (! IsSpace(szLast)) && *szLast != '\t' && *szLast != ',' )
{
szLast = CharNext(szLast);
}
if(*szLast != '\0')
{
// Terminate szFirst at this delimiter
// 96/12/10 markdu BUG 12699 Call CharNext before setting char to null.
szTemp = szLast;
szLast = CharNext(szLast);
*szTemp = '\0';
// Set beginning of szLast to be first non-space/comma/tab
while(IsSpace(szLast) || *szLast == '\t' || *szLast == ',')
{
szLast = CharNext(szLast);
}
}
// Prepare the (sn=szFirst*) filter
hr = BuildBasicFilter(
&szFirstSurname,
(LPTSTR)cszAttr_sn,
szFirst,
TRUE);
if (hrSuccess != hr)
{
goto exit;
}
if (UMICH_PASS != dwPass)
{
// Prepare the (givenName=szFirst*) filter
hr = BuildBasicFilter(
&szGivenName,
(LPTSTR)cszAttr_givenName,
szFirst,
TRUE);
if (hrSuccess != hr)
{
goto exit;
}
}
if(*szLast == '\0')
{
// The strings was in just one piece
// Prepare the (cn=szFirst*) filter
hr = BuildBasicFilter(
&szCommonName,
(LPTSTR)cszAttr_cn,
szFirst,
TRUE);
if (hrSuccess != hr)
{
goto exit;
}
// The string is all in one piece. Stick it in the filter.
// Note that we use szFirst instead of szInput, since szFirst
// will have trailing spaces, commas, and tabs removed.
if (UMICH_PASS == dwPass)
{
// Final result should be:
// "(|(cn=szFirst*)(sn=szFirst*))"
// OR the common name and surname filters together
hr = BuildOpFilter(
lplpszFilter,
szCommonName,
szFirstSurname,
FILTER_OP_OR);
if (hrSuccess != hr)
{
goto exit;
}
}
else
{
// Final result should be:
// "(|(cn=szFirst*)(|(sn=szFirst*)(givenName=szFirst*)))"
// OR the given name and surname filters together
hr = BuildOpFilter(
&szFirstLast,
szFirstSurname,
szGivenName,
FILTER_OP_OR);
if (hrSuccess != hr)
{
goto exit;
}
// OR the common name and first-last filters together
hr = BuildOpFilter(
lplpszFilter,
szCommonName,
szFirstLast,
FILTER_OP_OR);
if (hrSuccess != hr)
{
goto exit;
}
}
// Generate the Alternate filter, which contains the email address.
// Note UMich server does not allow wildcards in email search.
hr = BuildBasicFilter(
lplpszAltFilter,
(LPTSTR)cszAttr_mail,
szFirst,
(UMICH_PASS != dwPass));
if (hrSuccess != hr)
{
goto exit;
}
}
else
{
// The string is in two pieces. Stick them in the filter.
// Prepare the (cn=lpszInput*) filter
hr = BuildBasicFilter(
&szCommonName,
(LPTSTR)cszAttr_cn,
lpszInput,
TRUE);
if (hrSuccess != hr)
{
goto exit;
}
// Prepare the (sn=szLast*) filter
hr = BuildBasicFilter(
&szLastSurname,
(LPTSTR)cszAttr_sn,
szLast,
TRUE);
if (hrSuccess != hr)
{
goto exit;
}
if (UMICH_PASS == dwPass)
{
// Final result should be:
// "(|(cn=szFirst*)(|(sn=szFirst*)(sn=szLast*)))"
// OR the first surname and last surname filters together
hr = BuildOpFilter(
&szFirstLast,
szFirstSurname,
szLastSurname,
FILTER_OP_OR);
if (hrSuccess != hr)
{
goto exit;
}
/*
// OR the common name and first-last filters together
hr = BuildOpFilter(
lplpszFilter,
szCommonName,
szFirstLast,
FILTER_OP_OR);
if (hrSuccess != hr)
{
goto exit;
}
*/
{
DWORD cchSize = (lstrlen(szCommonName)+1);
*lplpszFilter = LocalAlloc(LMEM_ZEROINIT, sizeof(TCHAR)*cchSize);
if(*lplpszFilter)
StrCpyN(*lplpszFilter, szCommonName, cchSize);
}
}
else
{
LPTSTR szFirstLastLastFirst;
// Final result should be:
// "(|(cn=lpszInput*)(|(&(sn=szFirst*)(givenName=szLast*))(&(givenName=szFirst*)(sn=szLast*))))"
// AND the first given name and last surname filters together
hr = BuildOpFilter(
&szLastFirst,
szGivenName,
szLastSurname,
FILTER_OP_AND);
if (hrSuccess != hr)
{
goto exit;
}
// Prepare the (givenName=szLast*) filter
LocalFreeAndNull(&szGivenName);
hr = BuildBasicFilter(
&szGivenName,
(LPTSTR)cszAttr_givenName,
szLast,
TRUE);
if (hrSuccess != hr)
{
goto exit;
}
// AND the last given name and first surname filters together
hr = BuildOpFilter(
&szFirstLast,
szFirstSurname,
szGivenName,
FILTER_OP_AND);
if (hrSuccess != hr)
{
goto exit;
}
// OR the first-last and last-first filters together
hr = BuildOpFilter(
&szFirstLastLastFirst,
szFirstLast,
szLastFirst,
FILTER_OP_OR);
if (hrSuccess != hr)
{
goto exit;
}
/****/
// OR the common name and first-last-last-first filters together
hr = BuildOpFilter(
lplpszFilter,
szCommonName,
szFirstLastLastFirst,
FILTER_OP_OR);
/****
*lplpszFilter = LocalAlloc(LMEM_ZEROINIT, sizeof(TCHAR)*(lstrlen(szCommonName)+1));
if(*lplpszFilter)
StrCpyN(*lplpszFilter, szCommonName);
****/
LocalFreeAndNull(&szFirstLastLastFirst);
if (hrSuccess != hr)
{
goto exit;
}
}
}
exit:
LocalFreeAndNull(&szFirst);
LocalFreeAndNull(&szCommonName);
LocalFreeAndNull(&szFirstSurname);
LocalFreeAndNull(&szLastSurname);
LocalFreeAndNull(&szGivenName);
LocalFreeAndNull(&szFirstLast);
LocalFreeAndNull(&szLastFirst);
return hr;
}
//*******************************************************************
//
// FUNCTION: GetLDAPServerName
//
// PURPOSE: Gets the server name property from the LDAP container
//
// PARAMETERS: lpLDAPCont -> LDAP container
// lppServer -> returned server name. Caller must
// MAPIFreeBuffer this string.
//
// RETURNS: HRESULT
//
// HISTORY:
// 96/07/11 brucek Created.
//
//*******************************************************************
HRESULT GetLDAPServerName(
LPLDAPCONT lpLDAPCont,
LPTSTR * lppServer)
{
HRESULT hResult;
SCODE sc;
ULONG cProps;
LPSPropValue lpProps = NULL;
LPTSTR lpServer = NULL;
if ((hResult = lpLDAPCont->lpVtbl->GetProps(lpLDAPCont,
(LPSPropTagArray)&ptaLDAPCont,
MAPI_UNICODE,
&cProps,
&lpProps))) {
DebugTraceResult( TEXT("LDAP Container GetProps"), hResult);
goto exit;
}
Assert(cProps == ildapcMax);
Assert(lpProps[ildapcPR_WAB_LDAP_SERVER].ulPropTag == PR_WAB_LDAP_SERVER);
{
DWORD cchSize = ((lstrlen(lpProps[ildapcPR_WAB_LDAP_SERVER].Value.LPSZ)) + 1);
if (sc = MAPIAllocateBuffer(sizeof(TCHAR)*cchSize, &lpServer))
{
hResult = ResultFromScode(sc);
goto exit;
}
StrCpyN(lpServer, lpProps[ildapcPR_WAB_LDAP_SERVER].Value.LPSZ, cchSize);
}
exit:
FreeBufferAndNull(&lpProps);
if (hResult) {
FreeBufferAndNull(&lpServer);
}
*lppServer = lpServer;
return(hResult);
}
//*******************************************************************
//
// FUNCTION: FixPropArray
//
// PURPOSE: Fixes the displayname in a proparray if it is
// equivalent to the email address.
// Adds a display name if there isnt one
//
// PARAMETERS: lpPropArray = MAPIAllocBuffer'ed property array
// ulcProps = number of props in lpPropArray
//
// RETURNS: TRUE if changes are made, FALSE if not.
//
// HISTORY:
// 96/07/12 brucek Created.
//
//*******************************************************************
BOOL FixPropArray(
LPSPropValue lpPropArray,
ULONG * lpulcProps)
{
ULONG ulcProps = *lpulcProps;
BOOL fChanged = FALSE;
signed int iSurname = NOT_FOUND, iGivenName = NOT_FOUND, iDisplayName = NOT_FOUND,
iEmailAddress = NOT_FOUND, iCompanyName = NOT_FOUND;
register signed int i;
for (i = 0; i < (signed int)ulcProps; i++) {
switch (lpPropArray[i].ulPropTag) {
case PR_SURNAME:
iSurname = i;
break;
case PR_GIVEN_NAME:
iGivenName = i;
break;
case PR_COMPANY_NAME:
iCompanyName = i;
break;
case PR_DISPLAY_NAME:
iDisplayName = i;
break;
case PR_EMAIL_ADDRESS:
iEmailAddress = i;
break;
}
}
if (((iSurname != NOT_FOUND && iGivenName != NOT_FOUND) || iCompanyName != NOT_FOUND) && iDisplayName != NOT_FOUND && iEmailAddress != NOT_FOUND) {
// PropArray contains all of the props.
// If PR_DISPLAY_NAME is same as PR_EMAIL_ADDRESS, regenerate the PR_DISPLAY_NAME from
// PR_SURNAME and PR_GIVEN_NAME or PR_COMPANY_NAME.
if (! lstrcmpi(lpPropArray[iDisplayName].Value.LPSZ, lpPropArray[iEmailAddress].Value.LPSZ)) {
fChanged = FixDisplayName(lpPropArray[iGivenName].Value.LPSZ,
NULL, //szEmpty, //For LDAP assume there is not middle name for now
lpPropArray[iSurname].Value.LPSZ,
iCompanyName == NOT_FOUND ? NULL : lpPropArray[iCompanyName].Value.LPSZ,
NULL, // NickName
(LPTSTR *) (&lpPropArray[iDisplayName].Value.LPSZ),
lpPropArray);
}
}
else if(iSurname != NOT_FOUND && iGivenName != NOT_FOUND && iDisplayName == NOT_FOUND)
{
//Exchange DS will sometimes not return a display name returning sn and givenName instead
iDisplayName = ulcProps; // This is safe as we allocated space for the display Name in the beginning
lpPropArray[iDisplayName].ulPropTag = PR_DISPLAY_NAME;
fChanged = FixDisplayName( lpPropArray[iGivenName].Value.LPSZ,
NULL, //szEmpty, //For LDAP assume there is not middle name for now
(lpPropArray[iSurname].Value.LPSZ),
(iCompanyName == NOT_FOUND ? NULL : lpPropArray[iCompanyName].Value.LPSZ),
NULL, // NickName
(LPTSTR *) (&lpPropArray[iDisplayName].Value.LPSZ),
(LPVOID) lpPropArray);
(*lpulcProps)++;
}
return(fChanged);
}
typedef HRESULT (* PFNHRCREATEACCOUNTMANAGER)(IImnAccountManager **);
//*******************************************************************
//
// FUNCTION: HrWrappedCreateAccountManager
//
// PURPOSE: Load account manager dll and create the object.
//
// PARAMETERS: lppAccountManager -> returned pointer to account manager
// object.
//
// RETURNS: HRESULT
//
//*******************************************************************
HRESULT HrWrappedCreateAccountManager(IImnAccountManager2 **lppAccountManager)
{
IImnAccountManager *pAccountManager;
LONG lRes;
DWORD dw;
HRESULT hResult;
TCHAR szPath[MAX_PATH],
szPathExpand[MAX_PATH],
szReg[MAX_PATH],
szGUID[MAX_PATH];
LPOLESTR lpszW= 0 ;
PFNHRCREATEACCOUNTMANAGER pfnHrCreateAccountManager = NULL;
LONG cb = MAX_PATH + 1;
DWORD dwType = 0;
HKEY hkey = NULL;
if (! lppAccountManager) {
return(ResultFromScode(E_INVALIDARG));
}
if (g_hInstImnAcct) {
return(ResultFromScode(ERROR_ALREADY_INITIALIZED));
}
*lppAccountManager = NULL;
if (HR_FAILED(hResult = StringFromCLSID(&CLSID_ImnAccountManager, &lpszW))) {
goto error;
}
StrCpyN(szGUID, lpszW, ARRAYSIZE(szGUID));
StrCpyN(szReg, TEXT("CLSID\\"), ARRAYSIZE(szReg));
StrCatBuff(szReg, szGUID, ARRAYSIZE(szReg));
StrCatBuff(szReg, TEXT("\\InprocServer32"), ARRAYSIZE(szReg));
lRes = RegOpenKeyEx(HKEY_CLASSES_ROOT, szReg, 0, KEY_QUERY_VALUE, &hkey);
if (lRes != ERROR_SUCCESS) {
hResult = ResultFromScode(CO_E_DLLNOTFOUND);
goto error;
}
cb = ARRAYSIZE(szPath);
lRes = RegQueryValueEx(hkey, NULL, NULL, &dwType, (LPBYTE)szPath, &cb);
if (REG_EXPAND_SZ == dwType)
{
// szPath is a REG_EXPAND_SZ type, so we need to expand
// environment strings
dw = ExpandEnvironmentStrings(szPath, szPathExpand, CharSizeOf(szPathExpand));
if (dw == 0) {
hResult = ResultFromScode(CO_E_DLLNOTFOUND);
goto error;
}
StrCpyN(szPath, szPathExpand, ARRAYSIZE(szPath));
}
if (lRes != ERROR_SUCCESS)
{
hResult = ResultFromScode(CO_E_DLLNOTFOUND);
goto error;
}
if (! (g_hInstImnAcct = LoadLibrary(szPath))) {
hResult = ResultFromScode(CO_E_DLLNOTFOUND);
goto error;
}
if (! (pfnHrCreateAccountManager = (PFNHRCREATEACCOUNTMANAGER)GetProcAddress(g_hInstImnAcct, "HrCreateAccountManager"))) {
hResult = ResultFromScode(TYPE_E_DLLFUNCTIONNOTFOUND);
goto error;
}
hResult = pfnHrCreateAccountManager(&pAccountManager);
if (SUCCEEDED(hResult))
{
hResult = pAccountManager->lpVtbl->QueryInterface(pAccountManager, &IID_IImnAccountManager2, (LPVOID *)lppAccountManager);
pAccountManager->lpVtbl->Release(pAccountManager);
}
goto exit;
error:
// Failed to init account manager the fast way. Try the S L O W OLE way...
if (CoInitialize(NULL) == S_FALSE) {
// Already initialized, undo the extra.
CoUninitialize();
} else {
fCoInitialize = TRUE;
}
if (HR_FAILED(hResult = CoCreateInstance(&CLSID_ImnAccountManager,
NULL,
CLSCTX_INPROC_SERVER,
&IID_IImnAccountManager2, (LPVOID *)lppAccountManager))) {
DebugTrace(TEXT("CoCreateInstance(IID_IImnAccountManager) -> %x\n"), GetScode(hResult));
}
exit:
// Clean up the OLE allocated memory
if (lpszW) {
LPMALLOC pMalloc = NULL;
CoGetMalloc(1, &pMalloc);
Assert(pMalloc);
if (pMalloc) {
pMalloc->lpVtbl->Free(pMalloc, lpszW);
pMalloc->lpVtbl->Release(pMalloc);
}
}
if (hkey != NULL)
RegCloseKey(hkey);
return(hResult);
}
//*******************************************************************
//
// FUNCTION: InitAccountManager
//
// PURPOSE: Load and initialize the account manager
//
// PARAMETERS: lppAccountManager -> returned pointer to account manager
// object.
//
// RETURNS: HRESULT
//
// COMMENTS: The first time through here, we will save the hResult.
// On subsequent calls, we will check this saved value
// and return it right away if there was an error, thus
// preventing repeated time consuming LoadLibrary calls.
//
// With Identity awareness (IE5.0 plus) .. we need to
// initiate the Account Manager on an identity basis ..
// We do this by passing it an appropriate regkey ..
// If this is a non-identity-aware app, then we always get
// info from the default identity ..
//
//*******************************************************************
HRESULT InitAccountManager(LPIAB lpIAB, IImnAccountManager2 ** lppAccountManager, GUID * pguidUser) {
static hResultSave = hrSuccess;
HRESULT hResult = hResultSave;
LPPTGDATA lpPTGData=GetThreadStoragePointer();
GUID guidNULL = {00000000-0000-0000-0000-000000000000};
if (! g_lpAccountManager && ! HR_FAILED(hResultSave)) {
#ifdef DEBUG
DWORD dwTickCount = GetTickCount();
DebugTrace(TEXT(">>>>> Initializing Account Manager...\n"));
#endif // DEBUG
if (hResult = HrWrappedCreateAccountManager(&g_lpAccountManager)) {
DebugTrace(TEXT("HrWrappedCreateAccountManager -> %x\n"), GetScode(hResult));
goto end;
}
Assert(g_lpAccountManager);
if(pt_bIsWABOpenExSession)
{
if (hResult = g_lpAccountManager->lpVtbl->InitEx( g_lpAccountManager,
NULL,
ACCT_INIT_OUTLOOK))
{
DebugTrace(TEXT("AccountManager->InitEx -> %x\n"), GetScode(hResult));
goto end;
}
}
else
{
// [PaulHi] 1/13/99 If a valid user guid pointer is passed in then
// use it to init the account manager
if (pguidUser)
{
g_lpAccountManager->lpVtbl->InitUser(g_lpAccountManager, NULL, pguidUser, 0);
}
else if (lpIAB &&
memcmp(&(lpIAB->guidCurrentUser), &guidNULL, sizeof(GUID)) )
{
// Try the existing user guid stored in the IAB
g_lpAccountManager->lpVtbl->InitUser(g_lpAccountManager, NULL, &(lpIAB->guidCurrentUser), 0);
}
else
{
// Default. WARNING If the account manager is not initialized at some point then
// it is susceptible to crash.
g_lpAccountManager->lpVtbl->InitUser(g_lpAccountManager, NULL, &UID_GIBC_DEFAULT_USER, 0);
}
}
#ifdef DEBUG
DebugTrace(TEXT(">>>>> Done Initializing Account Manager... %u milliseconds\n"), GetTickCount() - dwTickCount);
#endif // DEBUG
}
end:
if (HR_FAILED(hResult)) {
*lppAccountManager = NULL;
// Save the result
hResultSave = hResult;
} else {
*lppAccountManager = g_lpAccountManager;
}
return(hResult);
}
//*******************************************************************
//
// FUNCTION: UninitAccountManager
//
// PURPOSE: Release and unLoad the account manager
//
// PARAMETERS: none
//
// RETURNS: none
//
//*******************************************************************
void UninitAccountManager(void) {
if (g_lpAccountManager) {
#ifdef DEBUG
DWORD dwTickCount = GetTickCount();
DebugTrace(TEXT(">>>>> Uninitializing Account Manager...\n"));
#endif // DEBUG
g_lpAccountManager->lpVtbl->Release(g_lpAccountManager);
g_lpAccountManager = NULL;
// Unload the acct man dll
if (g_hInstImnAcct) {
FreeLibrary(g_hInstImnAcct);
g_hInstImnAcct=NULL;
}
if (fCoInitialize) {
CoUninitialize();
}
#ifdef DEBUG
DebugTrace(TEXT(">>>>> Done Uninitializing Account Manager... %u milliseconds\n"), GetTickCount() - dwTickCount);
#endif // DEBUG
}
}
//*******************************************************************
//
// FUNCTION: AddToServerList
//
// PURPOSE: Insert a server name in the server list
//
// PARAMETERS: lppServerNames -> ServerNames pointer
// szBuf = name of server
// dwOrder = insertion order of this server
//
// RETURNS: HRESULT
//
//*******************************************************************
HRESULT AddToServerList(UNALIGNED LPSERVER_NAME * lppServerNames, LPTSTR szBuf, DWORD dwOrder) {
HRESULT hResult = hrSuccess;
LPSERVER_NAME lpServerName = NULL, lpCurrent;
UNALIGNED LPSERVER_NAME * lppInsert;
DWORD cbSize = (lstrlen(szBuf) + 1);
// Create new node
if (! (lpServerName = LocalAlloc(LPTR, LcbAlignLcb(sizeof(SERVER_NAME))))) {
DebugTrace(TEXT("Can't allocate new server name structure\n"));
hResult = ResultFromScode(MAPI_E_NOT_ENOUGH_MEMORY);
goto exit;
}
if (! (lpServerName->lpszName = LocalAlloc(LPTR, LcbAlignLcb(sizeof(TCHAR)*cbSize)))) {
DebugTrace(TEXT("Can't allocate new server name\n"));
hResult = ResultFromScode(MAPI_E_NOT_ENOUGH_MEMORY);
goto exit;
}
StrCpyN(lpServerName->lpszName, szBuf, cbSize);
lpServerName->dwOrder = dwOrder;
// Insert in list in correct order
lppInsert = lppServerNames;
lpCurrent = *lppServerNames;
while (lpCurrent && (lpCurrent->dwOrder <= dwOrder)) {
lpCurrent = (*lppInsert)->lpNext;
lppInsert = &(*lppInsert)->lpNext;
}
lpServerName->lpNext = lpCurrent;
*lppInsert = lpServerName;
exit:
if (hResult && lpServerName) {
if (lpServerName->lpszName) {
LocalFree(lpServerName->lpszName);
}
LocalFree(lpServerName);
}
return(hResult);
}
//*******************************************************************
//
// FUNCTION: EnumerateLDAPtoServerList
//
// PURPOSE: Enumerate the LDAP servers to a server name linked list.
//
// PARAMETERS: lpAccountManager -> Account Manager
// lppServerNames -> returned ServerNames (must be NULL on
// entry.
// lpcServers -> returned number of servers. May be NULL
// if caller doesn't care.
//
// RETURNS: HRESULT
//
//*******************************************************************
HRESULT EnumerateLDAPtoServerList(IImnAccountManager2 * lpAccountManager,
LPSERVER_NAME * lppServerNames, LPULONG lpcServers) {
IImnEnumAccounts * lpEnumAccounts = NULL;
IImnAccount * lpAccount = NULL;
DWORD dwOrder;
LPSERVER_NAME lpNextServer;
char szBuf[MAX_UI_STR + 1];
HRESULT hResult;
Assert(lpAccountManager);
Assert(*lppServerNames == NULL);
if (lpcServers) {
*lpcServers = 0;
}
if (hResult = lpAccountManager->lpVtbl->Enumerate(lpAccountManager,
SRV_LDAP,
&lpEnumAccounts)) {
goto exit;
}
while (! lpEnumAccounts->lpVtbl->GetNext(lpEnumAccounts,
&lpAccount)) {
// Add this account's name to the list
if (! lpAccount->lpVtbl->GetPropSz(lpAccount,
AP_ACCOUNT_NAME,
szBuf,
sizeof(szBuf))) {
// What order in the list?
if (lpAccount->lpVtbl->GetPropDw(lpAccount,
AP_LDAP_SERVER_ID,
&dwOrder)) {
dwOrder = 0xFFFFFFFF; // last
}
// Add it to the linked list of names
{
LPTSTR lpServer = ConvertAtoW(szBuf);
if (lpServer)
{
if (hResult = AddToServerList(lppServerNames, lpServer, dwOrder)) {
goto exit;
}
LocalFreeAndNull(&lpServer);
}
else
{
hResult = E_OUTOFMEMORY;
goto exit;
}
}
if (lpcServers) {
(*lpcServers)++;
}
}
lpAccount->lpVtbl->Release(lpAccount);
lpAccount = NULL;
}
exit:
if (lpAccount) {
lpAccount->lpVtbl->Release(lpAccount);
}
if (lpEnumAccounts) {
lpEnumAccounts->lpVtbl->Release(lpEnumAccounts);
}
return(hResult);
}
//*******************************************************************
//
// FUNCTION: RegQueryValueExDWORD
//
// PURPOSE: Read a DWORD registry value, correcting for value type
//
// RETURNS: RegQueryValueEx error code
//
//*******************************************************************
DWORD RegQueryValueExDWORD(HKEY hKey, LPTSTR lpszValueName, LPDWORD lpdwValue)
{
DWORD dwType, dwErr;
DWORD cbData = sizeof(DWORD);
*lpdwValue = 0;
dwErr = RegQueryValueEx(hKey, lpszValueName, NULL, &dwType, (LPBYTE)lpdwValue, &cbData);
return(dwErr);
}
//*******************************************************************
//
// FUNCTION: GetLDAPNextServerID
//
// PURPOSE: To ensure that all server entries in the registry are
// unique, we will henceforth assign each new entry a
// unique serverID at the time of creation. This will help
// us make sure that all registry entries are unique.
// A running counter is stored in the registry and will
// give us the next available SeverID
//
// PARAMETERS: dwSet = input value to set the next id to. (Optional,
// ignored if zero.)
//
// RETURNS: The next available ID for use. Valid IDs range from 1 upwards.
// 0 is an invalid ID, as is -1.
//
// HISTORY:
// 96/10/09 vikramm Created
//*******************************************************************
DWORD GetLDAPNextServerID(DWORD dwSet) {
DWORD dwID = 0;
DWORD dwNextID = 0;
DWORD dwErr = 0;
HKEY hKeyWAB;
LPTSTR szLDAPNextAvailableServerID = TEXT("Server ID");
// Open the WAB's reg key
if (! (dwErr = RegOpenKeyEx(HKEY_CURRENT_USER, szWABKey, 0, KEY_ALL_ACCESS, &hKeyWAB)))
{
dwNextID = 0; // init in case registry gives < 4 bytes.
if (dwSet)
dwNextID = dwSet;
else
{
// Read the next available server id
if (dwErr = RegQueryValueExDWORD(hKeyWAB, (LPTSTR)szLDAPNextAvailableServerID, &dwNextID))
{
// The value wasn't found!!
// Create a new key, starting at 100
// (Start high so that we can be guaranteed to be past any
// pre-configured servers.)
dwNextID = 500;
}
}
dwID = dwNextID++;
// Update the ID in the registry
RegSetValueEx(hKeyWAB, (LPTSTR)szLDAPNextAvailableServerID, 0, REG_DWORD, (LPBYTE)&dwNextID, sizeof(dwNextID));
RegCloseKey(hKeyWAB);
}
return(dwID);
}
/*
-
- SetAccountStringAW
*
* Account manager returns ANSI/DBCS which we need to convert to Unicode as appropriate
*
*/
void SetAccountStringAW(LPTSTR * lppAcctStr, LPSTR lpszData)
{
*lppAcctStr = ConvertAtoW(lpszData);
}
//*******************************************************************
//
// FUNCTION: GetLDAPServerParams
//
// PURPOSE: Gets the per-server parameters for the given LDAP server.
// Parameters include limit on number of entries to retrieve in
// an LDAP search, the max number of seconds to spend on the
// server, the max number of seconds to wait at the client,
// and the type of authentication to use with this server.
//
// PARAMETERS: lpszServer - name of the server
// ServerParams - structure containing the per-server parameters
//
// RETURNS: TRUE if the lpszServer already existed else returns FALSE
// If the given server does not exist, still fills in the lspParams struct.
//
// HISTORY:
// 96/07/16 markdu Created.
// 96/10/09 vikramm Added Server Name and search base. Changed return value
// from void to BOOL
// 96/12/16 brucek Added URL.
//*******************************************************************
BOOL GetLDAPServerParams(LPTSTR lpszServer, LPLDAPSERVERPARAMS lspParams)
{
DWORD dwType;
HRESULT hResult = hrSuccess;
IImnAccountManager2 * lpAccountManager = NULL;
IImnAccount * lpAccount = NULL;
char szBuffer[513];
// Set defaults for each value
lspParams->dwSearchSizeLimit = LDAP_SEARCH_SIZE_LIMIT;
lspParams->dwSearchTimeLimit = LDAP_SEARCH_TIME_LIMIT;
lspParams->dwAuthMethod = LDAP_AUTH_METHOD_ANONYMOUS;
lspParams->lpszUserName = NULL;
lspParams->lpszPassword = NULL;
lspParams->lpszURL = NULL;
lspParams->lpszLogoPath = NULL;
lspParams->lpszBase = NULL;
lspParams->lpszName = NULL;
lspParams->fResolve = FALSE;
lspParams->dwID = 0;
lspParams->dwUseBindDN = 0;
lspParams->dwPort = LDAP_DEFAULT_PORT;
lspParams->fSimpleSearch = FALSE;
lspParams->lpszAdvancedSearchAttr = NULL;
#ifdef PAGED_RESULT_SUPPORT
lspParams->dwPagedResult = LDAP_PRESULT_UNKNOWN;
#endif //#ifdef PAGED_RESULT_SUPPORT
lspParams->dwIsNTDS = LDAP_NTDS_UNKNOWN;
if (hResult = InitAccountManager(NULL, &lpAccountManager, NULL)) {
goto exit;
}
SetAccountStringWA(szBuffer, lpszServer, CharSizeOf(szBuffer));
if (hResult = lpAccountManager->lpVtbl->FindAccount(lpAccountManager,
AP_ACCOUNT_NAME,
szBuffer,
&lpAccount)) {
//DebugTrace(TEXT("FindAccount(%s) -> %x\n"), lpszServer, GetScode(hResult));
goto exit;
}
// have account object, set its properties
Assert(lpAccount);
// Server Type: Is it LDAP?
if (HR_FAILED(hResult = lpAccount->lpVtbl->GetServerTypes(lpAccount,
&dwType))) {
DebugTrace(TEXT("GetServerTypes() -> %x\n"), GetScode(hResult));
goto exit;
}
if (! (dwType & SRV_LDAP)) {
DebugTrace(TEXT("Account manager gave us a non-LDAP server\n"));
hResult = ResultFromScode(MAPI_E_NOT_FOUND);
goto exit;
}
// LDAP Server address
if (HR_FAILED(hResult = lpAccount->lpVtbl->GetPropSz(lpAccount,
AP_LDAP_SERVER,
szBuffer,
sizeof(szBuffer)))) {
// This is a Required property, fail if not there.
DebugTrace(TEXT("GetPropSz(AP_LDAP_SERVER) -> %x\n"), GetScode(hResult));
goto exit;
}
SetAccountStringAW(&lspParams->lpszName, szBuffer);
// Username
if (! (HR_FAILED(hResult = lpAccount->lpVtbl->GetPropSz(lpAccount,
AP_LDAP_USERNAME,
szBuffer,
sizeof(szBuffer))))) {
SetAccountStringAW(&lspParams->lpszUserName, szBuffer);
}
// Password
if (! (HR_FAILED(hResult = lpAccount->lpVtbl->GetPropSz(lpAccount,
AP_LDAP_PASSWORD,
szBuffer,
sizeof(szBuffer))))) {
SetAccountStringAW(&lspParams->lpszPassword, szBuffer);
}
// Advanced Search Attributes
if (! (HR_FAILED(hResult = lpAccount->lpVtbl->GetPropSz(lpAccount,
AP_LDAP_ADVANCED_SEARCH_ATTR,
szBuffer,
sizeof(szBuffer))))) {
SetAccountStringAW(&lspParams->lpszAdvancedSearchAttr, szBuffer);
}
// Authentication method
if (HR_FAILED(hResult = lpAccount->lpVtbl->GetPropDw(lpAccount,
AP_LDAP_AUTHENTICATION,
&lspParams->dwAuthMethod))) {
// default to anonymous
lspParams->dwAuthMethod = LDAP_AUTH_METHOD_ANONYMOUS;
}
// LDAP Timeout
if (HR_FAILED(hResult = lpAccount->lpVtbl->GetPropDw(lpAccount,
AP_LDAP_TIMEOUT,
&lspParams->dwSearchTimeLimit))) {
// default to 60 seconds
lspParams->dwSearchTimeLimit = LDAP_SEARCH_TIME_LIMIT;
}
// LDAP Search Base
if (HR_FAILED(hResult = lpAccount->lpVtbl->GetPropSz(lpAccount,
AP_LDAP_SEARCH_BASE,
szBuffer,
sizeof(szBuffer)))) {
// Don't need to set the default search base here. GetLDAPSearchBase will
// calculate one if needed.
} else {
SetAccountStringAW(&lspParams->lpszBase, szBuffer);
}
// Search Limit
if (HR_FAILED(hResult = lpAccount->lpVtbl->GetPropDw(lpAccount,
AP_LDAP_SEARCH_RETURN,
&lspParams->dwSearchSizeLimit))) {
// default to 100
lspParams->dwSearchTimeLimit = LDAP_SEARCH_SIZE_LIMIT;
}
// Order
if (HR_FAILED(hResult = lpAccount->lpVtbl->GetPropDw(lpAccount,
AP_LDAP_SERVER_ID,
&lspParams->dwID))) {
lspParams->dwID = 0;
}
// Make sure we have a valid unique id
if (lspParams->dwID == 0 || lspParams->dwID == 0xFFFFFFFF) {
lspParams->dwID = GetLDAPNextServerID(0);
}
// Resolve flag
#ifndef WIN16
if (HR_FAILED(hResult = lpAccount->lpVtbl->GetPropDw(lpAccount,
AP_LDAP_RESOLVE_FLAG,
&lspParams->fResolve))) {
#else
if (HR_FAILED(hResult = lpAccount->lpVtbl->GetPropDw(lpAccount,
AP_LDAP_RESOLVE_FLAG,
(DWORD __RPC_FAR *)&lspParams->fResolve))) {
#endif
lspParams->fResolve = FALSE;
}
// Server URL
if (! (HR_FAILED(hResult = lpAccount->lpVtbl->GetPropSz(lpAccount,
AP_LDAP_URL,
szBuffer,
sizeof(szBuffer))))) {
SetAccountStringAW(&lspParams->lpszURL, szBuffer);
}
// Full Path to Logo bitmap
if (! (HR_FAILED(hResult = lpAccount->lpVtbl->GetPropSz(lpAccount,
AP_LDAP_LOGO,
szBuffer,
sizeof(szBuffer)))))
{
SetAccountStringAW(&lspParams->lpszLogoPath, szBuffer);
}
// Port
if (HR_FAILED(hResult = lpAccount->lpVtbl->GetPropDw(lpAccount,
AP_LDAP_PORT,
&lspParams->dwPort))) {
// default to 100
lspParams->dwPort = LDAP_DEFAULT_PORT;
}
// Use Bind DN
if (HR_FAILED(hResult = lpAccount->lpVtbl->GetPropDw(lpAccount,
AP_LDAP_USE_BIND_DN,
&lspParams->dwUseBindDN))) {
lspParams->dwUseBindDN = 0;
}
// Use SSL
if (HR_FAILED(hResult = lpAccount->lpVtbl->GetPropDw(lpAccount,
AP_LDAP_SSL,
&lspParams->dwUseSSL))) {
lspParams->dwUseSSL = 0;
}
// Do Simple Search
if (HR_FAILED(hResult = lpAccount->lpVtbl->GetPropDw(lpAccount,
AP_LDAP_SIMPLE_SEARCH,
&lspParams->fSimpleSearch))) {
lspParams->fSimpleSearch = FALSE;
}
#ifdef PAGED_RESULT_SUPPORT
// Paged Result Support
if (HR_FAILED(hResult = lpAccount->lpVtbl->GetPropDw(lpAccount,
AP_LDAP_PAGED_RESULTS,
&lspParams->dwPagedResult))) {
lspParams->dwPagedResult = LDAP_PRESULT_UNKNOWN;
}
#endif //#ifdef PAGED_RESULT_SUPPORT
// Is this an NTDS account
if (HR_FAILED(hResult = lpAccount->lpVtbl->GetPropDw(lpAccount,
AP_LDAP_NTDS,
&lspParams->dwIsNTDS))) {
lspParams->dwIsNTDS = LDAP_NTDS_UNKNOWN;
}
exit:
if (lpAccount) {
lpAccount->lpVtbl->Release(lpAccount);
}
return(!HR_FAILED(hResult));
}
/*
-
- SetAccountStringWA
*
* Account manager needs ANSI/DBCS so if we have UNICODE data we need to convert to ANSI
*
* lpStrA should be a big enough buffer to get the ANSI data
* cb = CharSizeOf(szStrA)
*/
void SetAccountStringWA(LPSTR szStrA, LPTSTR lpszData, int cbsz)
{
LPSTR lpBufA = NULL;
Assert(szStrA);
szStrA[0] = '\0';
// If the source string pointer is NULL then just return
if (lpszData == NULL)
return;
lpBufA = ConvertWtoA(lpszData);
if (lpBufA)
{
StrCpyNA(szStrA, (LPCSTR)lpBufA, cbsz);
LocalFreeAndNull((LPVOID*)&lpBufA);
}
}
//*******************************************************************
//
// FUNCTION: SetLDAPServerParams
//
// PURPOSE: Sets the per-server parameters for the given LDAP server.
// Parameters include limit on number of entries to retrieve in
// an LDAP search, the max number of seconds to spend on the
// server, the max number of seconds to wait at the client,
// and the type of authentication to use with this server.
//
// PARAMETERS: lpszServer - name of the server
// ServerParams - structure containing the per-server parameters
// Note: if this parameter is NULL, the key with name lpszServer
// will be deleted if it exists.
// Note: lpszUserName and lpszPassword are only stored if
// dwAuthenticationMethod is LDAP_AUTH_METHOD_SIMPLE. Otherwise,
// these parameters are ignored. To clear one of the strings,
// set it to a NULL string (ie ""). Setting the parameter to
// NULL will result in ERROR_INVALID_PARAMETER.
//
// RETURNS: HRESULT
//
// HISTORY:
// 96/07/26 markdu Created.
// 97/01/19 brucek Port to account manager
//
//*******************************************************************
HRESULT SetLDAPServerParams(
LPTSTR lpszServer,
LPLDAPSERVERPARAMS lspParams)
{
HRESULT hResult = hrSuccess;
IImnAccountManager2 * lpAccountManager = NULL;
IImnAccount * lpAccount = NULL;
DWORD dwType;
char szBuf[513];
if (hResult = InitAccountManager(NULL, &lpAccountManager, NULL)) {
goto exit;
}
SetAccountStringWA(szBuf, lpszServer, CharSizeOf(szBuf));
if (hResult = lpAccountManager->lpVtbl->FindAccount(lpAccountManager,
AP_ACCOUNT_NAME,
szBuf,
&lpAccount)) {
DebugTrace(TEXT("Creating account %s\n"), lpszServer);
if (hResult = lpAccountManager->lpVtbl->CreateAccountObject(lpAccountManager,
ACCT_DIR_SERV,
&lpAccount)) {
DebugTrace(TEXT("CreateAccountObject -> %x\n"), GetScode(hResult));
goto exit;
}
} else {
// Found an account. Is it LDAP?
if (HR_FAILED(hResult = lpAccount->lpVtbl->GetServerTypes(lpAccount,
&dwType))) {
DebugTrace(TEXT("GetServerTypes() -> %x\n"), GetScode(hResult));
goto exit;
}
if (! (dwType & SRV_LDAP)) {
DebugTrace(TEXT("%s is already a non-LDAP server name\n"), lpszServer);
hResult = ResultFromScode(MAPI_E_COLLISION);
goto exit;
}
// Yes, at this point, we know that the existing server is LDAP.
if (NULL == lspParams) {
// Are there other account types on this account?
if (dwType == SRV_LDAP) {
lpAccount->lpVtbl->Delete(lpAccount);
} else {
// BUGBUG: If AcctManager ever supports more than one type per account, we
// should add code here to remove LDAP from the type.
}
// Jump past the property settings
goto exit;
}
}
// have account object, set its properties
Assert(lpAccount);
// Account Name
SetAccountStringWA(szBuf, lpszServer, CharSizeOf(szBuf));
if (HR_FAILED(hResult = lpAccount->lpVtbl->SetPropSz(lpAccount,
AP_ACCOUNT_NAME,
szBuf))) { // account name = server name
DebugTrace(TEXT("SetPropSz(AP_ACCOUNT_NAME, %s) -> %x\n"), lpszServer, GetScode(hResult));
goto exit;
}
// LDAP Server address
SetAccountStringWA(szBuf,
(!lspParams->lpszName || !lstrlen(lspParams->lpszName)) ? szNULLString : lspParams->lpszName,
CharSizeOf(szBuf));
if (HR_FAILED(hResult = lpAccount->lpVtbl->SetPropSz(lpAccount,
AP_LDAP_SERVER,
szBuf)))
{
DebugTrace(TEXT("SetPropSz(AP_LDAP_SERVER, %s) -> %x\n"), lspParams->lpszName ? lspParams->lpszName : TEXT("<NULL>"), GetScode(hResult));
goto exit;
}
// Username
SetAccountStringWA(szBuf, lspParams->lpszUserName, CharSizeOf(szBuf));
if (HR_FAILED(hResult = lpAccount->lpVtbl->SetPropSz(lpAccount,
AP_LDAP_USERNAME,
szBuf))) {
DebugTrace(TEXT("SetPropSz(AP_LDAP_USERNAME, %s) -> %x\n"), lspParams->lpszUserName ? lspParams->lpszUserName : TEXT("<NULL>"), GetScode(hResult));
goto exit;
}
// Password
SetAccountStringWA(szBuf, lspParams->lpszPassword, CharSizeOf(szBuf));
if (HR_FAILED(hResult = lpAccount->lpVtbl->SetPropSz(lpAccount,
AP_LDAP_PASSWORD,
szBuf))) {
DebugTrace(TEXT("SetPropSz(AP_LDAP_PASSWORD, %s) -> %x\n"), lspParams->lpszPassword ? lspParams->lpszPassword : TEXT("<NULL>"), GetScode(hResult));
goto exit;
}
// Advanced Search Attributes
SetAccountStringWA(szBuf, lspParams->lpszAdvancedSearchAttr, CharSizeOf(szBuf));
if (HR_FAILED(hResult = lpAccount->lpVtbl->SetPropSz(lpAccount,
AP_LDAP_ADVANCED_SEARCH_ATTR,
szBuf))) {
DebugTrace(TEXT("SetPropSz(AP_LDAP_ADVANCED_SEARCH_ATTR, %s) -> %x\n"), lspParams->lpszAdvancedSearchAttr ? lspParams->lpszAdvancedSearchAttr : TEXT("<NULL>"), GetScode(hResult));
goto exit;
}
// Authentication method
if (HR_FAILED(hResult = lpAccount->lpVtbl->SetPropDw(lpAccount,
AP_LDAP_AUTHENTICATION,
lspParams->dwAuthMethod))) {
DebugTrace(TEXT("SetPropDw(AP_LDAP_AUTHENTICATION, %u) -> %x\n"), lspParams->dwAuthMethod, GetScode(hResult));
goto exit;
}
// LDAP Timeout
if (HR_FAILED(hResult = lpAccount->lpVtbl->SetPropDw(lpAccount,
AP_LDAP_TIMEOUT,
lspParams->dwSearchTimeLimit))) { // account name = server name
DebugTrace(TEXT("SetPropDw(AP_LDAP_TIMEOUT, %y) -> %x\n"), lspParams->dwSearchTimeLimit, GetScode(hResult));
goto exit;
}
// LDAP Search Base
SetAccountStringWA(szBuf, lspParams->lpszBase, CharSizeOf(szBuf));
if (HR_FAILED(hResult = lpAccount->lpVtbl->SetPropSz(lpAccount,
AP_LDAP_SEARCH_BASE,
szBuf))) {
DebugTrace(TEXT("SetPropSz(AP_LDAP_SEARCH_BASE, %s) -> %x\n"), lspParams->lpszBase ? lspParams->lpszBase : TEXT("<NULL>"), GetScode(hResult));
goto exit;
}
// Search Limit
if (HR_FAILED(hResult = lpAccount->lpVtbl->SetPropDw(lpAccount,
AP_LDAP_SEARCH_RETURN,
lspParams->dwSearchSizeLimit))) {
DebugTrace(TEXT("SetPropDw(AP_LDAP_SEARCH_RETURN, %u) -> %x\n"), lspParams->dwSearchSizeLimit, GetScode(hResult));
goto exit;
}
// Order
if (HR_FAILED(hResult = lpAccount->lpVtbl->SetPropDw(lpAccount,
AP_LDAP_SERVER_ID,
lspParams->dwID))) {
DebugTrace(TEXT("SetPropDw(AP_LDAP_SERVER_ID, %u) -> %x\n"), lspParams->dwID, GetScode(hResult));
goto exit;
}
// Resolve flag
if (HR_FAILED(hResult = lpAccount->lpVtbl->SetPropDw(lpAccount,
AP_LDAP_RESOLVE_FLAG,
lspParams->fResolve))) {
DebugTrace(TEXT("SetPropDw(AP_LDAP_RESOLVE_FLAG) -> %x\n"), GetScode(hResult));
}
// Server URL
SetAccountStringWA(szBuf, lspParams->lpszURL, CharSizeOf(szBuf));
if (HR_FAILED(hResult = lpAccount->lpVtbl->SetPropSz(lpAccount,
AP_LDAP_URL,
szBuf))) {
DebugTrace(TEXT("SetPropSz(AP_LDAP_URL, %s) -> %x\n"), lspParams->lpszURL ? lspParams->lpszURL : TEXT("<NULL>"), GetScode(hResult));
}
// Server URL
SetAccountStringWA(szBuf, lspParams->lpszLogoPath, CharSizeOf(szBuf));
if (HR_FAILED(hResult = lpAccount->lpVtbl->SetPropSz(lpAccount,
AP_LDAP_LOGO,
szBuf))) {
DebugTrace(TEXT("SetPropSz(AP_LDAP_URL, %s) -> %x\n"), lspParams->lpszLogoPath ? lspParams->lpszLogoPath : TEXT("<NULL>"), GetScode(hResult));
}
// Port
if (HR_FAILED(hResult = lpAccount->lpVtbl->SetPropDw(lpAccount,
AP_LDAP_PORT,
lspParams->dwPort))) {
DebugTrace(TEXT("SetPropDw(AP_LDAP_PORT, %u) -> %x\n"), lspParams->dwPort, GetScode(hResult));
goto exit;
}
// Bind DN
if (HR_FAILED(hResult = lpAccount->lpVtbl->SetPropDw(lpAccount,
AP_LDAP_USE_BIND_DN,
lspParams->dwUseBindDN))) {
DebugTrace(TEXT("SetPropDw(AP_LDAP_USE_BIND_DN, %u) -> %x\n"), lspParams->dwUseBindDN, GetScode(hResult));
goto exit;
}
// Use SSL
if (HR_FAILED(hResult = lpAccount->lpVtbl->SetPropDw(lpAccount,
AP_LDAP_SSL,
lspParams->dwUseSSL))) {
DebugTrace(TEXT("SetPropDw(AP_LDAP_SSL, %u) -> %x\n"), lspParams->dwUseSSL, GetScode(hResult));
goto exit;
}
// Simple Search
if (HR_FAILED(hResult = lpAccount->lpVtbl->SetPropDw(lpAccount,
AP_LDAP_SIMPLE_SEARCH,
lspParams->fSimpleSearch))) {
DebugTrace(TEXT("SetPropDw(AP_LDAP_SIMPLE_SEARCH, %u) -> %x\n"), lspParams->fSimpleSearch, GetScode(hResult));
goto exit;
}
#ifdef PAGED_RESULT_SUPPORT
// Paged Result Support
if (HR_FAILED(hResult = lpAccount->lpVtbl->SetPropDw(lpAccount,
AP_LDAP_PAGED_RESULTS,
lspParams->dwPagedResult))) {
DebugTrace(TEXT("SetPropDw(AP_LDAP_PAGED_RESULTS, %u) -> %x\n"), lspParams->dwPagedResult, GetScode(hResult));
goto exit;
}
#endif //#ifdef PAGED_RESULT_SUPPORT
if (HR_FAILED(hResult = lpAccount->lpVtbl->SetPropDw(lpAccount,
AP_LDAP_NTDS,
lspParams->dwIsNTDS))) {
DebugTrace(TEXT("SetPropDw(AP_LDAP_NTDS, %u) -> %x\n"), lspParams->dwIsNTDS, GetScode(hResult));
goto exit;
}
// Save the changes to this account
if (HR_FAILED(hResult = lpAccount->lpVtbl->SaveChanges(lpAccount))) {
DebugTrace(TEXT("Account->SaveChanges -> %x\n"), GetScode(hResult));
goto exit;
}
// AP_LAST_UPDATED
// AP_RAS_CONNECTION_TYPE
// AP_RAS_CONNECTOID
// AP_RAS_CONNECTION_FLAGS
// AP_RAS_CONNECTED
exit:
if (lpAccount) {
lpAccount->lpVtbl->Release(lpAccount);
}
return(hResult);
}
//*******************************************************************
//
// FUNCTION: LDAPResolveName
//
// PURPOSE: Resolves against all the LDAP servers. Maintains
// list of ambiguous resolves for UI.
//
// PARAMETERS: lpAdrBook = IADDRBOOK object
// lpAdrList -> ADRLIST to resolve
// lpFlagList -> FlagList
// lpAmbiguousTables -> list of ambiguous match tables [in/out]
// lpulResolved -> Resolved count [in/out]
// lpulAmbiguous -> Ambiguous count [in/out]
// lpulUnresolved -> Unresolved count [in/out]
//
// RETURNS: HRESULT
//
// HISTORY:
// 96/07/15 brucek Created.
//
//*******************************************************************
HRESULT LDAPResolveName(
LPADRBOOK lpAddrBook,
LPADRLIST lpAdrList,
LPFlagList lpFlagList,
LPAMBIGUOUS_TABLES lpAmbiguousTables,
ULONG ulFlags)
{
SCODE sc;
HRESULT hResult;
LPMAPITABLE lpRootTable = NULL;
LPMAPITABLE lpAmbiguousTable = NULL;
LPABCONT lpRoot = NULL;
ULONG ulObjType;
ULONG i;
LPABCONT lpLDAPContainer = NULL;
LPFlagList lpFlagListOld = NULL;
LPSRowSet lpRow = NULL;
ULONG ulResolved, ulUnresolved, ulAmbiguous;
BOOL bUnicode = (ulFlags & WAB_RESOLVE_UNICODE);
// Open Root container
if (! (hResult = lpAddrBook->lpVtbl->OpenEntry(lpAddrBook,
0,
NULL,
NULL,
0,
&ulObjType,
(LPUNKNOWN *)&lpRoot))) {
if (! (hResult = lpRoot->lpVtbl->GetContentsTable(lpRoot,
MAPI_UNICODE,
&lpRootTable))) {
SRestriction resAnd[2]; // 0 = LDAP, 1 = ResolveFlag
SRestriction resLDAPResolve;
SPropValue ResolveFlag;
ULONG cRows;
// Set the columns
lpRootTable->lpVtbl->SetColumns(lpRootTable,
(LPSPropTagArray)&irnColumns,
0);
// Restrict: Only show LDAP containers with Resolve TRUE
resAnd[0].rt = RES_EXIST;
resAnd[0].res.resExist.ulReserved1 = 0;
resAnd[0].res.resExist.ulReserved2 = 0;
resAnd[0].res.resExist.ulPropTag = PR_WAB_LDAP_SERVER;
ResolveFlag.ulPropTag = PR_WAB_RESOLVE_FLAG;
ResolveFlag.Value.b = TRUE;
resAnd[1].rt = RES_PROPERTY;
resAnd[1].res.resProperty.relop = RELOP_EQ;
resAnd[1].res.resProperty.ulPropTag = PR_WAB_RESOLVE_FLAG;
resAnd[1].res.resProperty.lpProp = &ResolveFlag;
resLDAPResolve.rt = RES_AND;
resLDAPResolve.res.resAnd.cRes = 2;
resLDAPResolve.res.resAnd.lpRes = resAnd;
if (HR_FAILED(hResult = lpRootTable->lpVtbl->Restrict(lpRootTable,
&resLDAPResolve,
0))) {
DebugTraceResult( TEXT("RootTable: Restrict"), hResult);
goto exit;
}
CountFlags(lpFlagList, &ulResolved, &ulAmbiguous, &ulUnresolved);
cRows = 1;
while (cRows && ulUnresolved) {
if (hResult = lpRootTable->lpVtbl->QueryRows(lpRootTable,
1, // one row at a time
0, // ulFlags
&lpRow)) {
DebugTraceResult( TEXT("ResolveName:QueryRows"), hResult);
} else if (lpRow) {
if (cRows = lpRow->cRows) { // Yes, single '='
// Open the container
if (! (hResult = lpAddrBook->lpVtbl->OpenEntry(lpAddrBook,
lpRow->aRow[0].lpProps[irnPR_ENTRYID].Value.bin.cb,
(LPENTRYID)lpRow->aRow[0].lpProps[irnPR_ENTRYID].Value.bin.lpb,
NULL,
0,
&ulObjType,
(LPUNKNOWN *)&lpLDAPContainer))) {
ULONG ulAmbiguousOld = ulAmbiguous;
__UPV * lpv;
//
// Create a copy of the current flag list
//
// Allocate the lpFlagList first and zero fill it.
if (sc = MAPIAllocateBuffer((UINT)CbNewSPropTagArray(lpAdrList->cEntries),
&lpFlagListOld)) {
hResult = ResultFromScode(sc);
goto exit;
}
MAPISetBufferName(lpFlagListOld, TEXT("WAB: lpFlagListOld in IAB_ResolveNames"));
lpFlagListOld->cFlags = lpAdrList->cEntries;
for (i = 0; i < lpFlagListOld->cFlags; i++) {
lpFlagListOld->ulFlag[i] = lpFlagList->ulFlag[i];
}
// Resolve against the LDAP container
if (! HR_FAILED(hResult = lpLDAPContainer->lpVtbl->ResolveNames(lpLDAPContainer,
NULL, // tag set
(bUnicode ? MAPI_UNICODE : 0), // ulFlags
lpAdrList,
lpFlagList))) {
// Ignore warnings
hResult = hrSuccess;
}
// Did this container report Ambiguous on any entries?
CountFlags(lpFlagList, &ulResolved, &ulAmbiguous, &ulUnresolved);
if (ulAmbiguousOld != ulAmbiguous) {
// Find which entries were reported as ambiguous and
// create a table to return.
for (i = 0; i < lpFlagList->cFlags; i++) {
if (lpFlagList->ulFlag[i] == MAPI_AMBIGUOUS &&
lpFlagListOld->ulFlag[i] != MAPI_AMBIGUOUS) {
// The search got an ambiguous result. Deal with it!
if (hResult = lpLDAPContainer->lpVtbl->GetContentsTable(lpLDAPContainer,
(bUnicode ? MAPI_UNICODE : 0),
&lpAmbiguousTable)) {
DebugTraceResult( TEXT("LDAPResolveName:GetContentsTable"), hResult);
// goto exit; // is this fatal?
hResult = hrSuccess;
} else {
// populate the table
SRestriction resAnd[1]; // 0 = DisplayName
SRestriction resLDAPFind;
SPropValue DisplayName;
ULONG ulPropTag = ( bUnicode ? PR_DISPLAY_NAME : // <note> assumes UNICODE defined
CHANGE_PROP_TYPE(PR_DISPLAY_NAME, PT_STRING8) );
if (lpv = FindAdrEntryProp(lpAdrList, i, ulPropTag))
{
DisplayName.ulPropTag = ulPropTag;
if(bUnicode)
DisplayName.Value.lpszW = lpv->lpszW;
else
DisplayName.Value.lpszA = lpv->lpszA;
resAnd[0].rt = RES_PROPERTY;
resAnd[0].res.resProperty.relop = RELOP_EQ;
resAnd[0].res.resProperty.ulPropTag = ulPropTag;
resAnd[0].res.resProperty.lpProp = &DisplayName;
resLDAPFind.rt = RES_AND;
resLDAPFind.res.resAnd.cRes = 1;
resLDAPFind.res.resAnd.lpRes = resAnd;
if (hResult = lpAmbiguousTable->lpVtbl->FindRow(lpAmbiguousTable,
&resLDAPFind,
BOOKMARK_BEGINNING,
0)) {
DebugTraceResult( TEXT("LDAPResolveName:GetContentsTable"), hResult);
// goto exit; // is this fatal?
hResult = hrSuccess;
UlRelease(lpAmbiguousTable);
lpAmbiguousTable = NULL;
} else {
// Got a contents table; put it in the
// ambiguity tables list.
Assert(i < lpAmbiguousTables->cEntries);
lpAmbiguousTables->lpTable[i] = lpAmbiguousTable;
}
}
}
}
}
}
FreeBufferAndNull(&lpFlagListOld);
UlRelease(lpLDAPContainer);
lpLDAPContainer = NULL;
}
}
FreeProws(lpRow);
lpRow = NULL;
}
}
UlRelease(lpRootTable);
lpRootTable = NULL;
}
UlRelease(lpRoot);
lpRoot = NULL;
}
exit:
UlRelease(lpLDAPContainer);
UlRelease(lpRootTable);
UlRelease(lpRoot);
if (lpRow) {
FreeProws(lpRow);
}
FreeBufferAndNull(&lpFlagListOld);
return(hResult);
}
//*******************************************************************
//
// FUNCTION: HRFromLDAPError
//
// PURPOSE: Convert LDAP error code into an HRESULT.
//
// PARAMETERS: ulErr - error code returned by LDAP, or LDAP_ERROR if the
// LDAP function does not directly return an error code.
// pLDAP - contains ld_errno member that holds the error
// if nErr is LDAP_ERROR.
// scDefault - SCODE for error to default to. If this is
// NULL, default is MAPI_E_CALL_FAILED.
//
// RETURNS: HRESULT that is closest match for the LDAP error.
//
// HISTORY:
// 96/07/22 markdu Created.
//
//*******************************************************************
HRESULT HRFromLDAPError(
ULONG ulErr,
LDAP* pLDAP,
SCODE scDefault)
{
HRESULT hr;
DebugPrintError(( TEXT("LDAP error 0x%.2x: %s\n"), ulErr, gpfnLDAPErr2String(ulErr)));
hr = ResultFromScode(MAPI_E_CALL_FAILED);
if ((LDAP_ERROR == ulErr) && pLDAP)
{
// Get the error code from the LDAP structure.
ulErr = pLDAP->ld_errno;
}
// Translate the error.
switch(ulErr)
{
case LDAP_SUCCESS:
hr = hrSuccess;
break;
case LDAP_ADMIN_LIMIT_EXCEEDED:
case LDAP_TIMELIMIT_EXCEEDED:
case LDAP_SIZELIMIT_EXCEEDED:
case LDAP_RESULTS_TOO_LARGE:
// With these error messages it is still possible to get back some
// valid data. If there is valid data, then the error should be
// MAPI_W_PARTIAL_COMPLETION instead of MAPI_E_UNABLE_TO_COMPLETE.
// It is the responibility of the caller to make this change.
hr = ResultFromScode(MAPI_E_UNABLE_TO_COMPLETE);
break;
case LDAP_NO_SUCH_OBJECT:
hr = ResultFromScode(MAPI_E_NOT_FOUND);
break;
case LDAP_AUTH_METHOD_NOT_SUPPORTED:
case LDAP_STRONG_AUTH_REQUIRED:
case LDAP_INAPPROPRIATE_AUTH:
case LDAP_INVALID_CREDENTIALS:
case LDAP_INSUFFICIENT_RIGHTS:
hr = ResultFromScode(MAPI_E_NO_ACCESS);
break;
case LDAP_SERVER_DOWN:
hr = ResultFromScode(MAPI_E_NETWORK_ERROR);
break;
case LDAP_TIMEOUT:
hr = ResultFromScode(MAPI_E_TIMEOUT);
break;
case LDAP_USER_CANCELLED:
hr = ResultFromScode(MAPI_E_USER_CANCEL);
break;
default:
if (scDefault)
{
hr = ResultFromScode(scDefault);
}
break;
}
return hr;
}
//*******************************************************************
//
// DNtoLDAPURL
//
// Converts a DN into an LDAP URL
//
//
//
//*******************************************************************
static const LPTSTR lpLDAPPrefix = TEXT("ldap://");
void DNtoLDAPURL(LPTSTR lpServer, LPTSTR lpDN, LPTSTR szURL, ULONG cchURL)
{
if(!lpServer || !lpDN || !szURL)
return;
StrCpyN(szURL, lpLDAPPrefix, cchURL);
StrCatBuff(szURL, lpServer, cchURL);
StrCatBuff(szURL, TEXT("/"), cchURL);
StrCatBuff(szURL, lpDN, cchURL);
return;
}
//*******************************************************************
//
// FUNCTION: TranslateAttrs
//
// PURPOSE: Cycle through the attributes in the entry, convert
// them into MAPI properties and return them.
//
// PARAMETERS: pLDAP - LDAP structure for this session
// lpEntry - Entry whose attributes to translate
// pulcProps - buffer to hold number of properties returned
// lpPropArray - buffer to hold returned properties
//
// RETURNS: HRESULT error code.
//
// HISTORY:
// 96/07/22 markdu Created.
//
//*******************************************************************
typedef enum
{
e_pager = 0, // highest priority
e_otherPager,
e_OfficePager, // lowest
e_pagerMax
};
HRESULT TranslateAttrs(
LDAP* pLDAP,
LDAPMessage* lpEntry,
LPTSTR lpServer,
ULONG* pulcProps,
LPSPropValue lpPropArray)
{
HRESULT hr = hrSuccess;
ULONG ulcProps = 0;
ULONG ulPropTag;
ULONG ulPrimaryEmailIndex = MAX_ULONG;
ULONG ulContactAddressesIndex = MAX_ULONG;
ULONG ulContactAddrTypesIndex = MAX_ULONG;
ULONG ulContactDefAddrIndexIndex = MAX_ULONG;
ULONG cbValue;
ULONG i, j;
SCODE sc;
LPTSTR szAttr;
BerElement* ptr;
LPTSTR* aszValues;
LPTSTR atszPagerAttr[e_pagerMax] = {0}; // [PaulHi] 3/17/99 Raid 73733 Choose between three pager attributes
// 0 - "pager", 1 - "otherpager", 2 - "officepager" in this order
#ifdef PARAMETER_VALIDATION
if (IsBadReadPtr(pLDAP, sizeof(LDAP)))
{
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
if (IsBadReadPtr(lpEntry, sizeof(LDAPMessage)))
{
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
if (IsBadWritePtr(pulcProps, sizeof(ULONG)))
{
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
if (IsBadReadPtr(lpPropArray, sizeof(SPropValue)))
{
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
#endif // PARAMETER_VALIDATION
szAttr = gpfnLDAPFirstAttr(pLDAP, lpEntry, &ptr);
while (szAttr)
{
//DebugTrace(TEXT("%s / "),szAttr);
ulPropTag = LDAPAttrToMAPIProp(szAttr);
// [PaulHi] 3/17/99 Raid 73733 Save up to e_pagerMax pager attribute types and skip. Later
// we need to choose a pager property by order of prioriy, in case there is more than one.
if (ulPropTag == PR_PAGER_TELEPHONE_NUMBER)
{
aszValues = gpfnLDAPGetValues(pLDAP, lpEntry, szAttr);
if (aszValues && aszValues[0])
{
ULONG cchSize = lstrlen(aszValues[0])+1;
LPTSTR lptszTemp = LocalAlloc(LMEM_ZEROINIT, cchSize * sizeof(TCHAR));
if (!lptszTemp) goto error;
StrCpyN(lptszTemp, aszValues[0], cchSize);
gpfnLDAPValueFree(aszValues);
if (!lstrcmpi(szAttr, cszAttr_pager))
{
LocalFreeAndNull(&(atszPagerAttr[0]));
atszPagerAttr[e_pager] = lptszTemp;
}
else if (!lstrcmpi(szAttr, cszAttr_otherPager))
{
LocalFreeAndNull(&(atszPagerAttr[1]));
atszPagerAttr[e_otherPager] = lptszTemp;
}
else
{
// If this Assert fires it must mean that the gAttrMap mapping table has changed
// to include yet another LDAP attribute to be associated with PR_PAGER_TELEPHONE_NUMBER.
// Increase the atszPagerAttr[] array to include this too.
Assert(lstrcmpi(szAttr, cszAttr_OfficePager) == 0);
LocalFreeAndNull(&(atszPagerAttr[2]));
atszPagerAttr[e_OfficePager] = lptszTemp;
}
}
goto endloop;
}
switch (PROP_TYPE(ulPropTag))
{
// BUGBUG currently only works for PT_MV_BINARY, PT_TSTRING or PT_MV_TSTRING properties
case PT_TSTRING:
{
// Get the value for this attribute
aszValues = gpfnLDAPGetValues(pLDAP, lpEntry, szAttr);
if (aszValues)
{
// BUGBUG for now just use first value (aszValues[0] )
if (aszValues[0] && (cbValue = lstrlen(aszValues[0])))
{
ULONG cbExtra = 0;
#ifdef DEBUG
if(!lstrcmpi(szAttr, TEXT("cn")))
{
DebugTrace(TEXT("cn=%s\n"),aszValues[0]);
}
#endif
lpPropArray[ulcProps].ulPropTag = ulPropTag;
lpPropArray[ulcProps].dwAlignPad = 0;
// If this is a postalAddress attribute, we need to replace $'s
// with \r\n. Add one byte for each $ in the string.
if ((PR_STREET_ADDRESS == ulPropTag) ||
(PR_HOME_ADDRESS_STREET == ulPropTag))
{
cbExtra = CountDollars(aszValues[0]);
}
if (PR_WAB_MANAGER == ulPropTag && lpServer)
{
cbExtra = lstrlen(lpLDAPPrefix) + lstrlen(lpServer) + 1;
}
// Allocate more space for the data
sc = MAPIAllocateMore(sizeof(TCHAR)*(cbValue + cbExtra + 1), lpPropArray,
(LPVOID *)&(lpPropArray[ulcProps].Value.LPSZ));
if (sc)
{
goto error;
}
// Copy the data, replacing $'s if necessary.
if ((0 != cbExtra) &&
((PR_STREET_ADDRESS == ulPropTag) ||
(PR_HOME_ADDRESS_STREET == ulPropTag)))
{
DollarsToLFs(aszValues[0], lpPropArray[ulcProps].Value.LPSZ, cbValue + cbExtra + 1);
}
else if(PR_WAB_MANAGER == ulPropTag && lpServer)
{
DNtoLDAPURL(lpServer, aszValues[0], lpPropArray[ulcProps].Value.LPSZ, cbValue + cbExtra + 1);
}
else
{
StrCpyN(lpPropArray[ulcProps].Value.LPSZ, aszValues[0], cbValue + cbExtra + 1);
}
// If this is PR_EMAIL_ADDRESS, create a PR_ADDRTYPE entry as well
if (PR_EMAIL_ADDRESS == ulPropTag)
{
// Remember where the email value was, so we can add it to
// PR_CONTACT_EMAIL_ADDRESSES later
ulPrimaryEmailIndex = ulcProps;
ulcProps++;
lpPropArray[ulcProps].ulPropTag = PR_ADDRTYPE;
lpPropArray[ulcProps].dwAlignPad = 0;
lpPropArray[ulcProps].Value.LPSZ = (LPTSTR)szSMTP;
}
ulcProps++;
}
gpfnLDAPValueFree(aszValues);
} // if aszValues
break;
}
case PT_MV_TSTRING:
if(ulPropTag == PR_WAB_SECONDARY_EMAIL_ADDRESSES)
{
ULONG ulcValues;
ULONG ulcSMTP = 0;
ULONG ulProp = 0;
UNALIGNED LPTSTR FAR *lppszAddrs;
UNALIGNED LPTSTR FAR *lppszTypes;
// Only property of this type that we know how to handle is
// PR_WAB_SECONDARY_EMAIL_ADDRESSES
Assert(PR_WAB_SECONDARY_EMAIL_ADDRESSES == ulPropTag);
// Get the value for this attribute
aszValues = gpfnLDAPGetValues(pLDAP, lpEntry, szAttr);
if (aszValues)
{
// Cycle through the addresses and count the number that are SMTP
ulcValues = gpfnLDAPCountValues(aszValues);
for (i=0;i<ulcValues;i++)
{
if (TRUE == IsSMTPAddress(aszValues[i], NULL))
ulcSMTP++;
}
// We are done if there were no SMTP addresses.
if (0 == ulcSMTP)
break;
// Set the default address to be the first one for now.
lpPropArray[ulcProps].ulPropTag = PR_CONTACT_DEFAULT_ADDRESS_INDEX;
lpPropArray[ulcProps].Value.l = 0;
ulContactDefAddrIndexIndex = ulcProps;
ulcProps++;
// Create the PR_CONTACT_EMAIL_ADDRESSES entry and allocate space for the array.
// Include space for an extra entry so we can add the PR_EMAIL_ADDRESS later.
lpPropArray[ulcProps].ulPropTag = PR_CONTACT_EMAIL_ADDRESSES;
lpPropArray[ulcProps].Value.MVSZ.cValues = ulcSMTP;
sc = MAPIAllocateMore((ulcSMTP + 1) * sizeof(LPTSTR), lpPropArray,
(LPVOID *)&(lpPropArray[ulcProps].Value.MVSZ.LPPSZ));
if (sc)
goto error;
lppszAddrs = lpPropArray[ulcProps].Value.MVSZ.LPPSZ;
ZeroMemory((LPVOID)lppszAddrs, (ulcSMTP + 1) * sizeof(LPTSTR));
// Create the PR_CONTACT_ADDRTYPES entry and allocate space for the array.
// Include space for an extra entry so we can add the PR_EMAIL_ADDRESS later.
lpPropArray[ulcProps + 1].ulPropTag = PR_CONTACT_ADDRTYPES;
lpPropArray[ulcProps + 1].Value.MVSZ.cValues = ulcSMTP;
sc = MAPIAllocateMore((ulcSMTP + 1) * sizeof(LPTSTR), lpPropArray,
(LPVOID *)&(lpPropArray[ulcProps + 1].Value.MVSZ.LPPSZ));
if (sc)
goto error;
lppszTypes = lpPropArray[ulcProps + 1].Value.MVSZ.LPPSZ;
ZeroMemory((LPVOID)lppszTypes, (ulcSMTP + 1) * sizeof(LPTSTR));
// Add the SMTP addresses to the list
for (i=0;i<ulcValues;i++)
{
LPTSTR lptszEmailName = NULL;
if (TRUE == IsSMTPAddress(aszValues[i], &lptszEmailName))
{
// Allocate more space for the email address and copy it.
ULONG cchSize = lstrlen(lptszEmailName) + 1;
sc = MAPIAllocateMore(sizeof(TCHAR)*cchSize, lpPropArray,
(LPVOID *)&(lppszAddrs[ulProp]));
if (sc)
goto error;
StrCpyN(lppszAddrs[ulProp], lptszEmailName, cchSize);
// Fill in the address type.
lppszTypes[ulProp] = (LPTSTR)szSMTP;
// Go on to the next one. Skip the rest if we know we have done all SMTP.
ulProp++;
if (ulProp >= ulcSMTP)
break;
}
}
// Remember where the PR_CONTACT_EMAIL_ADDRESSES value was, so we can
// add PR_EMAIL_ADDRESS to it later
ulContactAddressesIndex = ulcProps;
ulContactAddrTypesIndex = ulcProps + 1;
ulcProps += 2;
gpfnLDAPValueFree(aszValues);
} // if aszValues
}
else if(ulPropTag == PR_WAB_CONF_SERVERS)
{
// Even though this is MV_TSTRING prop, the ldap server
// will only really return 1 single item which is of the format
// server/conf-email
// All we need to do is put it in the prop with a callto:// prefix ...
//
ULONG ulcValues;
ULONG ulProp = 0;
ULONG ulPrefixLen;
UNALIGNED LPTSTR FAR *lppszServers;
// Get the value for this attribute
aszValues = gpfnLDAPGetValues(pLDAP, lpEntry, szAttr);
if (aszValues)
{
ULONG cchSize;
lpPropArray[ulcProps].ulPropTag = PR_WAB_CONF_SERVERS;
lpPropArray[ulcProps].Value.MVSZ.cValues = 1;
sc = MAPIAllocateMore(sizeof(LPTSTR), lpPropArray,
(LPVOID *)&(lpPropArray[ulcProps].Value.MVSZ.LPPSZ));
if (sc)
{
goto error;
}
lppszServers = lpPropArray[ulcProps].Value.MVSZ.LPPSZ;
ulPrefixLen = lstrlen(szCallto) + 1;
// Allocate more space for the email address and copy it.
cchSize = lstrlen(aszValues[0]) + ulPrefixLen + 1;
sc = MAPIAllocateMore(sizeof(TCHAR)*cchSize, lpPropArray,
(LPVOID *)&(lppszServers[0]));
if (sc)
{
goto error;
}
StrCpyN(lppszServers[0], szCallto, cchSize);
StrCatBuff(lppszServers[0], (LPTSTR) aszValues[0], cchSize);
ulcProps++;
gpfnLDAPValueFree(aszValues);
} // if aszValues
}
else if(ulPropTag == PR_WAB_REPORTS && lpServer)
{
ULONG ulcValues = 0;
ULONG ulLen = 0;
UNALIGNED LPTSTR FAR *lppszServers = NULL;
// Get the value for this attribute
aszValues = gpfnLDAPGetValues(pLDAP, lpEntry, szAttr);
if (aszValues)
{
ulcValues = gpfnLDAPCountValues(aszValues);
lpPropArray[ulcProps].ulPropTag = PR_WAB_REPORTS;
lpPropArray[ulcProps].Value.MVSZ.cValues = ulcValues;
sc = MAPIAllocateMore((ulcValues+1)*sizeof(LPTSTR), lpPropArray,
(LPVOID *)&(lpPropArray[ulcProps].Value.MVSZ.LPPSZ));
if (sc)
goto error;
lppszServers = lpPropArray[ulcProps].Value.MVSZ.LPPSZ;
for(i=0;i<ulcValues;i++)
{
ulLen = (lstrlen(lpLDAPPrefix) + lstrlen(lpServer) + 1 + lstrlen(aszValues[i]) + 1);
// Allocate more space for the email address and copy it.
sc = MAPIAllocateMore(sizeof(TCHAR)*ulLen, lpPropArray,
(LPVOID *)&(lppszServers[i]));
if (sc)
goto error;
DNtoLDAPURL(lpServer, aszValues[i], lppszServers[i], ulLen);
}
ulcProps++;
gpfnLDAPValueFree(aszValues);
} // if aszValues
}
break;
case PT_MV_BINARY:
{
ULONG ulcValues;
struct berval** ppberval;
BOOL bSMIME = FALSE;
// Only property of this type that we know how to handle is
// PR_USER_X509_CERTIFICATE
Assert(PR_USER_X509_CERTIFICATE == ulPropTag);
DebugTrace(TEXT("%s\n"),szAttr);
if(!lstrcmpi(szAttr, cszAttr_userSMIMECertificate) || !lstrcmpi(szAttr, cszAttr_userSMIMECertificatebinary))
bSMIME = TRUE;
// Get the value for this attribute
ppberval = gpfnLDAPGetValuesLen(pLDAP, lpEntry, szAttr);
if (ppberval && (*ppberval) && (*ppberval)->bv_len)
{
ulcValues = gpfnLDAPCountValuesLen(ppberval);
if (0 != ulcValues)
{
ULONG cbNew = 0,k=0;
/* We dont want to translate the LDAP Cert to a MAPI Cert just yet
For now we will put the raw cert data into PR_WAB_LDAP_RAWCERT and
do the conversion when the user calls OpenEntry on this LDAP Contact
*/
lpPropArray[ulcProps].ulPropTag = bSMIME ? PR_WAB_LDAP_RAWCERTSMIME: PR_WAB_LDAP_RAWCERT;
lpPropArray[ulcProps].dwAlignPad = 0;
lpPropArray[ulcProps].Value.MVbin.cValues = ulcValues;
if(!FAILED(sc = MAPIAllocateMore(sizeof(SBinary)*ulcValues,lpPropArray,(LPVOID)&(lpPropArray[ulcProps].Value.MVbin.lpbin))))
{
for(k=0;k<ulcValues;k++)
{
cbNew = lpPropArray[ulcProps].Value.MVbin.lpbin[k].cb = (DWORD)((ppberval[k])->bv_len);
if (FAILED(sc = MAPIAllocateMore(cbNew, lpPropArray, (LPVOID)&(lpPropArray[ulcProps].Value.MVbin.lpbin[k].lpb))))
{
//hr = ResultFromScode(sc);
ulcProps--;
goto endloop;
}
CopyMemory(lpPropArray[ulcProps].Value.MVbin.lpbin[k].lpb, (PBYTE)((ppberval[k])->bv_val), cbNew);
}
}
ulcProps++;
}
gpfnLDAPValueFreeLen(ppberval);
} // if ppberval
}
break;
case PT_NULL:
break;
default:
Assert((PROP_TYPE(ulPropTag) == PT_TSTRING) ||
(PROP_TYPE(ulPropTag) == PT_MV_TSTRING));
break;
} // switch
endloop:
// Get the next attribute
szAttr = gpfnLDAPNextAttr(pLDAP, lpEntry, ptr);
} // while szAttr
// [PaulHi] 3/17/99 Raid 73733 Add the pager property here, if any. These
// will have been added in order of priority so just grab the first valid one.
{
for (i=0; i<e_pagerMax; i++)
{
if (atszPagerAttr[i])
{
lpPropArray[ulcProps].ulPropTag = PR_PAGER_TELEPHONE_NUMBER;
lpPropArray[ulcProps].dwAlignPad = 0;
cbValue = lstrlen(atszPagerAttr[i]);
sc = MAPIAllocateMore(sizeof(TCHAR)*(cbValue + 1), lpPropArray,
(LPVOID *)&(lpPropArray[ulcProps].Value.LPSZ));
if (sc)
goto error;
StrCpyN(lpPropArray[ulcProps].Value.LPSZ, atszPagerAttr[i], cbValue+1);
++ulcProps;
break;
}
}
// Clean up
for (i=0; i<e_pagerMax; i++)
LocalFreeAndNull(&(atszPagerAttr[i]));
}
if (ulcProps)
{
// Remove duplicates.
for (i=0;i<ulcProps - 1;i++)
{
// If there are any entries in the array that have the same
// type as this one, replace them with PR_NULL.
ulPropTag = lpPropArray[i].ulPropTag;
if (PR_NULL != ulPropTag)
{
for (j=i+1;j<ulcProps;j++)
{
if (ulPropTag == lpPropArray[j].ulPropTag)
{
lpPropArray[j].ulPropTag = PR_NULL;
}
}
}
}
// Fix up the email address properties
if ((MAX_ULONG == ulPrimaryEmailIndex) && (ulContactAddressesIndex < ulcProps))
{
LPTSTR lpszDefault;
ULONG cchSize;
// We got only secondary email addressess. Copy one to the primary address.
// Take the first one, since it is already set as default anyway.
lpPropArray[ulcProps].ulPropTag = PR_EMAIL_ADDRESS;
lpPropArray[ulcProps].dwAlignPad = 0;
// Allocate more space for the email address and copy it.
lpszDefault = lpPropArray[ulContactAddressesIndex].Value.MVSZ.LPPSZ[0];
cchSize = lstrlen(lpszDefault) + 1;
sc = MAPIAllocateMore(sizeof(TCHAR)*cchSize, lpPropArray,
(LPVOID *)&(lpPropArray[ulcProps].Value.LPSZ));
if (sc)
{
goto error;
}
StrCpyN(lpPropArray[ulcProps].Value.LPSZ, lpszDefault, cchSize);
ulcProps++;
// Create the PR_ADDRTYPE property as well.
lpPropArray[ulcProps].ulPropTag = PR_ADDRTYPE;
lpPropArray[ulcProps].dwAlignPad = 0;
lpPropArray[ulcProps].Value.LPSZ = (LPTSTR)szSMTP;
ulcProps++;
// Delete the PR_CONTACT_ properties if that was the only one,
if (1 == lpPropArray[ulContactAddressesIndex].Value.MVSZ.cValues)
{
// We don't need the PR_CONTACT_ properties
lpPropArray[ulContactAddressesIndex].ulPropTag = PR_NULL;
lpPropArray[ulContactAddrTypesIndex].ulPropTag = PR_NULL;
lpPropArray[ulContactDefAddrIndexIndex].ulPropTag = PR_NULL;
}
}
else if ((ulPrimaryEmailIndex < ulcProps) && (ulContactAddressesIndex < ulcProps))
{
ULONG ulcEntries;
LPTSTR lpszDefault;
// We need to add the primary address to PR_CONTACT_EMAIL_ADDRESSES
// and set it as the default
Assert((ulContactAddrTypesIndex < ulcProps) && (ulContactDefAddrIndexIndex < ulcProps));
// Before adding, see if it is already in the list.
lpszDefault = lpPropArray[ulPrimaryEmailIndex].Value.LPSZ;
ulcEntries = lpPropArray[ulContactAddressesIndex].Value.MVSZ.cValues;
for (i=0;i<ulcEntries;i++)
{
if (!lstrcmpi(lpPropArray[ulContactAddressesIndex].Value.MVSZ.LPPSZ[i], lpszDefault))
{
// Found a match.
break;
}
}
if (i < ulcEntries)
{
// The default is already in the list at index i
lpPropArray[ulContactDefAddrIndexIndex].Value.l = i;
}
else
{
ULONG cchSize;
// Add the default address to the end of the list.
lpPropArray[ulContactDefAddrIndexIndex].Value.l = ulcEntries;
// Allocate more space for the email address and copy it.
cchSize = lstrlen(lpszDefault) + 1;
sc = MAPIAllocateMore(sizeof(TCHAR)*cchSize, lpPropArray,
(LPVOID *)&(lpPropArray[ulContactAddressesIndex].Value.MVSZ.LPPSZ[ulcEntries]));
if (sc)
{
goto error;
}
lpPropArray[ulContactAddressesIndex].Value.MVSZ.cValues++;
StrCpyN(lpPropArray[ulContactAddressesIndex].Value.MVSZ.LPPSZ[ulcEntries], lpszDefault, cchSize);
// Fill in the address type.
lpPropArray[ulContactAddrTypesIndex].Value.MVSZ.LPPSZ[ulcEntries] = (LPTSTR)szSMTP;
lpPropArray[ulContactAddrTypesIndex].Value.MVSZ.cValues++;
}
}
}
if (pulcProps)
{
*pulcProps = ulcProps;
}
else
{
hr = ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
return hr;
error:
gpfnLDAPValueFree(aszValues);
// [PaulHi] clean up
for (i=0; i<e_pagerMax; i++)
LocalFreeAndNull(&(atszPagerAttr[i]));
return ResultFromScode(sc);
}
//*******************************************************************
//
// FUNCTION: CountDollars
//
// PURPOSE: Count the number of $ characters in the string.
//
// PARAMETERS: lpszStr - string to count.
//
// RETURNS: Number of dollar signs.
//
// HISTORY:
// 96/11/21 markdu Created.
//
//*******************************************************************
ULONG CountDollars(LPTSTR lpszStr)
{
ULONG ulcDollars = 0;
while (*lpszStr)
{
if ('$' == *lpszStr)
{
ulcDollars++;
}
lpszStr = CharNext(lpszStr);
}
return ulcDollars;
}
//*******************************************************************
//
// FUNCTION: DollarsToLFs
//
// PURPOSE: Convert all $ characters in the input string to line
// feeds in the output string. The rest of the output
// string is just a copy of the input string.
//
// PARAMETERS: lpszSrcStr - string to copy.
// lpszDestStr - output string, previously allocated large
// enough to hold the input string with $'s replaced.
//
// RETURNS: None.
//
// HISTORY:
// 96/11/21 markdu Created.
//
//*******************************************************************
void DollarsToLFs(
LPTSTR lpszSrcStr,
LPTSTR lpszDestStr,
DWORD cchDestStr)
{
DWORD dw=0;
while (*lpszSrcStr && dw < cchDestStr)
{
if ('$' == *lpszSrcStr)
{
*lpszDestStr++ = '\r';
dw++;
if (dw < cchDestStr)
{
*lpszDestStr++ = '\n';
dw++;
}
// Eat all whitespace characters that were after the $
// Also get rid of any more $'s so we don't stick to many LF's
while(IsSpace(lpszSrcStr) || *lpszSrcStr == '\t' || *lpszSrcStr == '$')
{
lpszSrcStr++;
}
}
else
{
// we're now natively unicode
//if (IsDBCSLeadByte((BYTE)*lpszSrcStr))
//{
// *lpszDestStr++ = *lpszSrcStr++;
//}
*lpszDestStr++ = *lpszSrcStr++;
dw++;
}
}
*lpszDestStr = '\0';
return;
}
//*******************************************************************
//
// FUNCTION: CountIllegalChars
//
// PURPOSE: Count the number of "illegal" characters in the string.
// This consists of the characters that should be escaped
// according to RFC1558: '*', '(', ')'.
//
// PARAMETERS: lpszStr - string to count.
//
// RETURNS: Number of illegal characters.
//
// HISTORY:
// 96/12/04 markdu Created.
//
//*******************************************************************
ULONG CountIllegalChars(LPTSTR lpszStr)
{
ULONG ulcIllegalChars = 0;
while (*lpszStr)
{
if (('*' == *lpszStr) || ('(' == *lpszStr) || (')' == *lpszStr))
{
ulcIllegalChars++;
}
lpszStr = CharNext(lpszStr);
}
return ulcIllegalChars;
}
//*******************************************************************
//
// FUNCTION: EscapeIllegalChars
//
// PURPOSE: Escape all illegal characters in the input string by
// replacing them with '\xx' where xx is the hex value
// representing the char. The rest of the output
// string is just a copy of the input string.
// Illegal characters are those that should be escaped
// according to RFC1558: '*', '(', ')'.
//
// PARAMETERS: lpszSrcStr - string to copy.
// lpszDestStr - output string, previously allocated large
// enough to hold the input string with illegal chars escaped..
//
// RETURNS: None.
//
// HISTORY:
// 96/12/04 markdu Created.
//
//*******************************************************************
static const LPTSTR szStar = TEXT("\\2a"); // '*'
static const LPTSTR szOBracket = TEXT("\\28"); // '('
static const LPTSTR szCBracket = TEXT("\\29"); // ')'
void EscapeIllegalChars(
LPTSTR lpszSrcStr,
LPTSTR lpszDestStr,
ULONG cchDestStr)
{
ULONG i=0;
lpszDestStr[0] = 0;
while (*lpszSrcStr && i < (cchDestStr-1))
{
if ('*' == *lpszSrcStr)
{
StrCatBuff(lpszDestStr, szStar, cchDestStr);
i += lstrlen(szStar);
lpszSrcStr++;
}
else if ('(' == *lpszSrcStr)
{
StrCatBuff(lpszDestStr, szOBracket, cchDestStr);
i += lstrlen(szOBracket);
lpszSrcStr++;
}
else if (')' == *lpszSrcStr)
{
StrCatBuff(lpszDestStr, szCBracket, cchDestStr);
i += lstrlen(szCBracket);
lpszSrcStr++;
}
else
{
// we're now natively unicode
//if (IsDBCSLeadByte((BYTE)*lpszSrcStr))
//{
// *lpszDestStr++ = *lpszSrcStr++;
//}
lpszDestStr[i++] = *lpszSrcStr++;
}
}
lpszDestStr[i] = '\0';
return;
}
//*******************************************************************
//
// FUNCTION: IsSMTPAddress
//
// PURPOSE: Checks to see if a given string is an SMTP email address
// according to draft-ietf-asid-ldapv3-attributes-01.txt
// section 6.9. For this to be the case, the string must
// begin with the characters "SMTP$".
// NOTE: The remainder of the
// string is not checked to see if it is a valid SMTP email
// address, so this is not a general-purpose function for
// determining whether an arbitrary string is SMTP.
//
// [PaulHi] Added [out] LPTSTR pointer that points to
// the beginning of the actual address name.
//
// PARAMETERS: lpszStr - string to check.
// [out] lpptszName, returned pointer in lpszStr for the
// actual email name part of the string.
//
// RETURNS: TRUE if the string is SMTP, FALSE otherwise.
//
// HISTORY:
// 96/11/27 markdu Created.
// 99/2/5 paulhi Modified.
//
//*******************************************************************
const TCHAR szsmtp[] = TEXT("smtp");
BOOL IsSMTPAddress(LPTSTR lpszStr, LPTSTR * lpptszName)
{
LPTSTR lpszSMTP = (LPTSTR)szSMTP;
LPTSTR lpszsmtp = (LPTSTR)szsmtp;
if (lpptszName)
(*lpptszName) = NULL;
while (*lpszSMTP && *lpszsmtp && *lpszStr)
{
if (*lpszSMTP != *lpszStr && *lpszsmtp != *lpszStr)
return FALSE;
lpszSMTP++;
lpszStr++;
lpszsmtp++;
}
if ('$' != *lpszStr)
return FALSE;
// If requested, return pointer to email name
if (lpptszName)
(*lpptszName) = lpszStr + 1; // Account for the '$' delimiter
return TRUE;
}
/*
-
- GetLDAPConnectionTimeout
* The default wldap32.dll timeout for connecting is 30-60 secs .. if the server is hung
* the user thinks they are hung .. so the WAB would downsize this timeout to 10 seconds ..
* However people using the RAS have a problem that 10 is too short .. so we add a reg setting
* that can be customized .. this customization is global for all services so it's at the
* user's own risk. Default, if no reg setting, is 10 seconds
* Bug 2409 - IE4.0x QFE RAID
*/
#define LDAP_CONNECTION_TIMEOUT 10 //seconds
DWORD GetLDAPConnectionTimeout()
{
DWORD dwErr = 0, dwTimeout = 0;
HKEY hKeyWAB;
LPTSTR szLDAPConnectionTimeout = TEXT("LDAP Connection Timeout");
// Open the WAB's reg key
if(!(dwErr=RegOpenKeyEx(HKEY_CURRENT_USER, szWABKey, 0, KEY_ALL_ACCESS, &hKeyWAB)))
{
// Read the next available server id
if (dwErr = RegQueryValueExDWORD(hKeyWAB, (LPTSTR)szLDAPConnectionTimeout, &dwTimeout))
{
// The value wasn't found!! .. Create a new key
dwTimeout = LDAP_CONNECTION_TIMEOUT;
RegSetValueEx(hKeyWAB, (LPTSTR)szLDAPConnectionTimeout, 0, REG_DWORD, (LPBYTE)&dwTimeout, sizeof(dwTimeout));
}
RegCloseKey(hKeyWAB);
}
return dwTimeout;
}
//*******************************************************************
//
// FUNCTION: OpenConnection
//
// PURPOSE: Open a connection to the LDAP server, and start an
// asynchronous bind with the correct authentication method.
//
// PARAMETERS: ppLDAP - receives LDAP structure for this session
// lpszServer - name of LDAP server to open
// pulTimeout - buffer to hold timeout value for search
// pulMsgID - message id returned by the bind call
// pfSyncBind - upon return, this will be set to TRUE if a
// synchronous bind was used, FALSE otherwise. Not used on input.
// lpszBindDN - the name with which to bind - most probably passed in
// through an LDAP URL. Overrides any other setting
//
// RETURNS: LDAP error code.
//
// HISTORY:
// 96/07/26 markdu Created.
// 96/11/02 markdu Made asynchronous.
// 96/12/14 markdu Added pfSyncBind.
//
//*******************************************************************
ULONG OpenConnection(
LPTSTR lpszServer,
LDAP** ppLDAP,
ULONG* pulTimeout,
ULONG* pulMsgID,
BOOL* pfSyncBind,
ULONG ulLdapType,
LPTSTR lpszBindDN,
DWORD dwAuthType)
{
LDAPSERVERPARAMS Params = {0};
LDAP* pLDAP = NULL;
LDAP* pLDAPSSL = NULL;
LPTSTR szDN;
LPTSTR szCred;
ULONG method;
ULONG ulResult = LDAP_SUCCESS;
BOOL fUseSynchronousBind = *pfSyncBind; //FALSE;
ULONG ulValue = LDAP_VERSION2;
ZeroMemory(&Params, sizeof(Params));
// initialize search control parameters
GetLDAPServerParams(lpszServer, &Params);
// The LDAP server name can be "NULL" or "" or "xxxx" ..
// The first 2 cases mean that pass a NULL to wldap32.dll for the server name .. if will go out and
// find the "closest" possible server - though I think this only works on NT 5 ..
//
if(!Params.lpszName ||
!lstrlen(Params.lpszName))
{
// Chances are that if we are here in OpenConnection and the
// name is NULL, then we are trying to open a LDAP server
// So quietly fill in the Params.lpszName with the server name
// <TBD> - fill a flag somewhere so we know the above assumption
// is try
if(lpszServer && lstrlen(lpszServer))
{
ULONG cchSize = lstrlen(lpszServer)+1;
Params.lpszName = LocalAlloc(LMEM_ZEROINIT, sizeof(TCHAR)*cchSize);
if(!Params.lpszName)
goto exit;
StrCpyN(Params.lpszName, lpszServer, cchSize);
}
else
Params.lpszName = szEmpty;
}
else if(!lstrcmpi(Params.lpszName, szNULLString))
{
// The search base is specified as a "NULL" which means use a NULL
LocalFree(Params.lpszName);
Params.lpszName = szEmpty;
}
if(Params.dwUseSSL)
{
pLDAPSSL = gpfnLDAPSSLInit( (Params.lpszName && lstrlen(Params.lpszName))?Params.lpszName:NULL,
Params.dwPort, TRUE);
if(NULL == pLDAPSSL)
{
DebugTrace(TEXT("ldap_ssl_init failed for this server\n"));
ulResult = LDAP_AUTH_METHOD_NOT_SUPPORTED;
goto exit;
}
}
// Open a connection
// The wldap32.dll has a 30-60 second timeout for the ldapopen call if the call cannot
// go through. To most users, it looks like the app is hung. WAB wants a lower timeout
// maybe close to 10 seconds and we can do that by calling ldap_init and then ldap_connect
// instead of ldapopen
#ifndef SMALLER_TIMEOUT
{
LDAP_TIMEVAL timeout;
if(!pLDAPSSL)
pLDAP = gpfnLDAPInit((Params.lpszName && lstrlen(Params.lpszName))?Params.lpszName:NULL,
Params.dwPort);
else
pLDAP = pLDAPSSL;
timeout.tv_sec = GetLDAPConnectionTimeout();
timeout.tv_usec = 0;
ulResult = gpfnLDAPConnect( pLDAP, &timeout );
if(ulResult != LDAP_SUCCESS)
goto exit;
}
#else
pLDAP = gpfnLDAPOpen(Params.lpszName, Params.dwPort);
if (NULL == pLDAP)
{
DebugTrace(TEXT("ldap_open failed for server %s.\n"), lpszServer);
// We could not open the server, so we assume that we could not find it
ulResult = LDAP_SERVER_DOWN;
goto exit;
}
#endif
// To do LDAP over SSL we can do:
// 1. Call ldap_sslinit before calling ldap_open which will always use port 636
// 2. To use any port number, set the SSL option using the ldap_set_option method
/*
// Hmm...option 2 doesnt seem to work ...
if(Params.dwUseSSL)
{
ULONG ulSecure = (ULONG) LDAP_OPT_ON;
if(gpfnLDAPSetOption( pLDAP, LDAP_OPT_SSL, &ulSecure) != LDAP_SUCCESS)
{
DebugTrace(TEXT("ldap_set_option failed to set SSL option"));
//ulResult = LDAP_AUTH_METHOD_NOT_SUPPORTED;
//goto exit;
}
}
*/
pLDAP->ld_sizelimit = (ULONG)Params.dwSearchSizeLimit;
pLDAP->ld_timelimit = (ULONG)Params.dwSearchTimeLimit;
pLDAP->ld_deref = LDAP_DEREF_ALWAYS;
// Convert timeout from seconds to milliseconds
Assert(pulTimeout);
*pulTimeout = (ULONG)Params.dwSearchTimeLimit * 1000;
// Set authentication parameters.
if(lpszBindDN && lstrlen(lpszBindDN))
{
szDN = lpszBindDN;
szCred = NULL;
method = LDAP_AUTH_SIMPLE;
}
else if (dwAuthType == LDAP_AUTH_SICILY || dwAuthType == LDAP_AUTH_NEGOTIATE || LDAP_AUTH_METHOD_SICILY == Params.dwAuthMethod)
{
// Use Sicily authentication. We need to do a synchronous bind in this case.
szDN = NULL;
szCred = NULL;
method = LDAP_AUTH_NEGOTIATE;
fUseSynchronousBind = TRUE;
}
else
if (LDAP_AUTH_METHOD_SIMPLE == Params.dwAuthMethod)
{
// Use LDAP simple authentication
szDN = Params.lpszUserName;
szCred = Params.lpszPassword;
method = LDAP_AUTH_SIMPLE;
}
else
{
// authenticate anonymously
if(Params.dwUseBindDN)
{
szDN = (LPTSTR) szBindDNMSFTUser;
szCred = (LPTSTR) szBindCredMSFTPass;
}
else
{
szDN = NULL;
szCred = NULL;
}
method = LDAP_AUTH_SIMPLE;
}
// We should try to bind as LDAP v3 client .. only if that fails
// with an LDAP_OPERATIONS_ERROR should we try to bind as an LDAP 2
// client
if(ulLdapType == use_ldap_v3)
ulValue = LDAP_VERSION3;
tryLDAPv2:
gpfnLDAPSetOption(pLDAP, LDAP_OPT_VERSION, &ulValue );
if (TRUE == fUseSynchronousBind)
{
ulResult = gpfnLDAPBindS(pLDAP, szDN, szCred, method);
// BUGBUG 96/12/09 markdu BUG 10537 Temporary work-around for wldap32.dll returning
// the wrong error code for invalid password on sicily bind.
// This should be removed later (BUG 12608).
// 96/12/19 markdu BUG 12608 Commented out temporary work-around.
//if ((LDAP_LOCAL_ERROR == ulResult) &&
// (LDAP_AUTH_SICILY == method))
//{
// ulResult = LDAP_INVALID_CREDENTIALS;
//}
}
else
{
// Start the asynchronous bind
*pulMsgID = gpfnLDAPBind(pLDAP, szDN, szCred, method);
/*
ulResult = pLDAP->ld_errno;
if(ulResult == LDAP_SUCCESS)
{
// make sure its really a success - some of the directory servers are
// sending a LDAP_PROTOCOL error after some time which is screwing up
// searching against those servers ...
LDAPMessage *lpResult = NULL;
struct l_timeval PollTimeout;
// Poll the server for results
ZeroMemory(&PollTimeout, sizeof(struct l_timeval));
PollTimeout.tv_sec = 2;
PollTimeout.tv_usec = 0;
ulResult = gpfnLDAPResult( pLDAP,
*pulMsgID,
LDAP_MSG_ALL, //Get all results before returning
&PollTimeout, // Timeout immediately (poll)
&lpResult);
ulResult = gpfnLDAPResult2Error(pLDAP, lpResult, FALSE);
// 96/12/09 markdu BUG 10537 If the bind returned one of these error
// messages, it probably means that the account name (DN) passed to the
// bind was incorrect or in the wrong format. Map these to an error code
// that will result in a better error message than "entry not found".
if ((LDAP_NAMING_VIOLATION == ulResult) || (LDAP_UNWILLING_TO_PERFORM == ulResult))
ulResult = LDAP_INVALID_CREDENTIALS;
// free the search results memory
if (lpResult)
gpfnLDAPMsgFree(lpResult);
}
*/
}
if(ulValue == LDAP_VERSION3 && (ulResult == LDAP_OPERATIONS_ERROR || ulResult == LDAP_PROTOCOL_ERROR))
{
gpfnLDAPAbandon(*ppLDAP, *pulMsgID);
// [PaulHi] 1/7/99 Since we try a new bind we need to relinquish the old binding,
// otherwise the server will support two connections until the original V3 attempt
// times out.
gpfnLDAPUnbind(*ppLDAP);
ulValue = LDAP_VERSION2;
goto tryLDAPv2;
}
exit:
if (LDAP_SUCCESS == ulResult)
{
*ppLDAP = pLDAP;
*pfSyncBind = fUseSynchronousBind;
}
FreeLDAPServerParams(Params);
return ulResult;
}
//*******************************************************************
//
// FUNCTION: EncryptDecryptText
//
// PURPOSE: Perform simple encryption on text so we can store it
// in the registry. The algorithm is reflexive, so it
// can also be used to decrypt text that it encrypted.
//
// PARAMETERS: lpb - text to encrypt/decrypt.
// dwSize - number of bytes to encrypt
//
// RETURNS: None.
//
// HISTORY:
// 96/07/29 markdu Created.
//
//*******************************************************************
void EncryptDecryptText(
LPBYTE lpb,
DWORD dwSize)
{
DWORD dw;
for (dw=0;dw<dwSize;dw++)
{
// Simple encryption -- just xor with 'w'
lpb[dw] ^= 'w';
}
}
//*******************************************************************
//
// FUNCTION: FreeLDAPServerParams
//
// PURPOSE: Frees allocated strings in the LDAPServerParams struct
//
// HISTORY:
// 96/10/10 vikram Created
//
//*******************************************************************
void FreeLDAPServerParams(LDAPSERVERPARAMS Params)
{
LocalFreeAndNull(&Params.lpszUserName);
LocalFreeAndNull(&Params.lpszPassword);
LocalFreeAndNull(&Params.lpszURL);
if(Params.lpszName && lstrlen(Params.lpszName))
LocalFreeAndNull(&Params.lpszName);
LocalFreeAndNull(&Params.lpszBase);
LocalFreeAndNull(&Params.lpszLogoPath);
LocalFreeAndNull(&Params.lpszAdvancedSearchAttr);
return;
}
//*******************************************************************
//
// FUNCTION: GetLDAPSearchBase
//
// PURPOSE: Generate Search Base string for LDAP search for the
// given server.
//
// PARAMETERS: lplpszBase - pointer to receive the search base string buffer.
// lpszServer - name of LDAP server whose base string to get.
//
// RETURNS: HRESULT
//
// HISTORY:
// 96/10/18 markdu Created.
//
//*******************************************************************
HRESULT GetLDAPSearchBase(
LPTSTR FAR * lplpszBase,
LPTSTR lpszServer)
{
LDAPSERVERPARAMS Params;
HRESULT hr = hrSuccess;
BOOL fRet;
TCHAR szCountry[COUNTRY_STR_LEN + 1];
LPTSTR lpszCountry;
// Make sure we can write to lplpszBase.
#ifdef PARAMETER_VALIDATION
if (IsBadWritePtr(lplpszBase, sizeof(LPTSTR)))
{
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
if (IsBadReadPtr(lpszServer, sizeof(CHAR)))
{
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
#endif // PARAMETER_VALIDATION
LocalFreeAndNull(lplpszBase);
GetLDAPServerParams((LPTSTR)lpszServer, &Params);
if(NULL == Params.lpszBase)
{
// Generate a default base.
// Read the default country for the search base from the registry.
DWORD cchSize = (lstrlen(cszBaseFilter) + lstrlen(cszAttr_c) + lstrlen(cszDefaultCountry) + 1);
*lplpszBase = (LPTSTR) LocalAlloc(LMEM_ZEROINIT, sizeof(TCHAR) * cchSize);
if (NULL == *lplpszBase)
{
hr = ResultFromScode(MAPI_E_NOT_ENOUGH_MEMORY);
goto exit;
}
*szCountry = '\0';
fRet = ReadRegistryLDAPDefaultCountry(NULL, 0, szCountry, ARRAYSIZE(szCountry));
if ((fRet) && (COUNTRY_STR_LEN == lstrlen(szCountry)))
{
lpszCountry = szCountry;
}
else
{
lpszCountry = (LPTSTR)cszDefaultCountry;
}
wnsprintf(*lplpszBase, cchSize, cszBaseFilter, cszAttr_c, lpszCountry);
}
else if(!lstrcmpi(Params.lpszBase, szNULLString))
{
// we've explicitly set this search base to NULL which means
// dont pass in an empty search base
*lplpszBase = LocalAlloc(LMEM_ZEROINIT, sizeof(TCHAR)*(lstrlen(szEmpty)+1));
if (NULL == *lplpszBase)
{
hr = ResultFromScode(MAPI_E_NOT_ENOUGH_MEMORY);
goto exit;
}
(*lplpszBase)[0] = TEXT('\0');
}
else
{
ULONG cchSize = lstrlen(Params.lpszBase)+1;
// The search base is configured for this server.
*lplpszBase = LocalAlloc(LMEM_ZEROINIT, sizeof(TCHAR)*cchSize);
if (NULL == *lplpszBase)
{
hr = ResultFromScode(MAPI_E_NOT_ENOUGH_MEMORY);
goto exit;
}
StrCpyN(*lplpszBase, Params.lpszBase, cchSize);
}
exit:
FreeLDAPServerParams(Params);
return hr;
}
//*******************************************************************
//
// FUNCTION: SearchWithCancel
//
// PURPOSE: Initiates a synchronous of asynchronous LDAP search
// Asynchronous may have a cancel dialog.
//
// PARAMETERS: ppLDAP - receives the LDAP connection handle
// szBase - The dn of the entry at which to start the search
// ulScope - The scope of the search
// szFilter - The search filter
// szNTFilter - NTDS-specific filter (can be NULL)
// ppszAttrs - A NULL-terminated array of strings indicating
// which attributes to return for each matching entry.
// Passing NULL for this entry causes all available attributes
// to be retrieved.
// ulAttrsonly - A boolean value that should be zero if both
// attribute types and values are to be returned, non-zero
// if only types are wanted
// pTimeout - The local search timeout value
// lplpResult - recieves the result parameter containing the entire
// search results
// lpszServer - name of LDAP server on which the search is to be performed
// fShowAnim - If true, show an animation in the cancel dialog
//
// RETURNS: Result of ldap_search call.
//
// HISTORY:
// 96/10/24 markdu Created.
//
//*******************************************************************
ULONG SearchWithCancel(
LDAP** ppLDAP,
LPTSTR szBase,
ULONG ulScope,
LPTSTR szFilter,
LPTSTR szNTFilter,
LPTSTR* ppszAttrs,
ULONG ulAttrsonly,
LDAPMessage** lplpResult,
LPTSTR lpszServer,
BOOL fShowAnim,
LPTSTR lpszBindDN,
DWORD dwAuthType,// to override or set the Authentication type if not 0
BOOL fResolveMultiple,
LPADRLIST lpAdrList,
LPFlagList lpFlagList,
BOOL fUseSynchronousBind,
BOOL * lpbIsNTDSEntry,
BOOL bUnicode)
{
ULONG ulMsgID;
ULONG ulResult;
HWND hDlg;
MSG msg;
LDAPSEARCHPARAMS LDAPSearchParams;
LPPTGDATA lpPTGData=GetThreadStoragePointer();
// Stuff the parameters into the structure to be passed to the dlg proc
ZeroMemory(&LDAPSearchParams, sizeof(LDAPSEARCHPARAMS));
LDAPSearchParams.ppLDAP = ppLDAP;
LDAPSearchParams.szBase = szBase;
LDAPSearchParams.ulScope = ulScope;
LDAPSearchParams.ulError = LDAP_SUCCESS;
LDAPSearchParams.szFilter = szFilter;
LDAPSearchParams.szNTFilter = szNTFilter;
LDAPSearchParams.ppszAttrs = ppszAttrs;
LDAPSearchParams.ulAttrsonly = ulAttrsonly;
LDAPSearchParams.lplpResult = lplpResult;
LDAPSearchParams.lpszServer = lpszServer;
LDAPSearchParams.lpszBindDN = lpszBindDN;
LDAPSearchParams.dwAuthType = dwAuthType;
LDAPSearchParams.lpAdrList = lpAdrList;
LDAPSearchParams.lpFlagList = lpFlagList;
LDAPSearchParams.bUnicode = bUnicode;
if(fShowAnim)
LDAPSearchParams.ulFlags |= LSP_ShowAnim;
if(fResolveMultiple)
LDAPSearchParams.ulFlags |= LSP_ResolveMultiple;
if(fUseSynchronousBind)
LDAPSearchParams.ulFlags |= LSP_UseSynchronousBind;
if(!pt_hWndFind) // no UI
{
DoSyncLDAPSearch(&LDAPSearchParams);
}
else
{
LDAPSearchParams.hDlgCancel = CreateDialogParam(hinstMapiX,
MAKEINTRESOURCE(IDD_DIALOG_LDAPCANCEL),
pt_hWndFind,
DisplayLDAPCancelDlgProc,
(LPARAM) &LDAPSearchParams);
// if called from the find dialog, the find dialog needs to be able
// to cancel the modeless dialog
pt_hDlgCancel = LDAPSearchParams.hDlgCancel;
while (LDAPSearchParams.hDlgCancel && GetMessage(&msg, NULL, 0, 0))
{
if (!IsDialogMessage(LDAPSearchParams.hDlgCancel, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
// If an error occurred in ldap_result, return the error code
if (LDAP_SUCCESS != LDAPSearchParams.ulError)
{
ulResult = LDAPSearchParams.ulError;
goto exit;
}
#ifdef PAGED_RESULT_SUPPORT
if(bSupportsLDAPPagedResults(&LDAPSearchParams))
ProcessLDAPPagedResultCookie(&LDAPSearchParams);
#endif //#ifdef PAGED_RESULT_SUPPORT
if(lpbIsNTDSEntry)
*lpbIsNTDSEntry = (LDAPSearchParams.ulFlags & LSP_IsNTDS) ? TRUE : FALSE;
ulResult = CheckErrorResult(&LDAPSearchParams, LDAP_RES_SEARCH_RESULT);
exit:
return ulResult;
}
//*******************************************************************
//
// FUNCTION: DisplayLDAPCancelDlgProc
//
// PURPOSE: Display a cancel dialog while waiting for results from
// multiple LDAP searches for ResolveNames
//
// PARAMETERS: lParam - pointer to structure containing all the
// search parameters.
//
// RETURNS: Returns TRUE if we successfully processed the message,
// FALSE otherwise.
//
// HISTORY:
// 96/10/24 markdu Created.
// 96/10/31 markdu Enhanced to allow multiple searches.
//
//*******************************************************************
INT_PTR CALLBACK DisplayLDAPCancelDlgProc(
HWND hDlg,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
switch (uMsg)
{
case WM_INITDIALOG:
{
PLDAPSEARCHPARAMS pLDAPSearchParams;
TCHAR szBuf[MAX_UI_STR];
LPTSTR lpszMsg = NULL;
HWND hWndAnim;
{
LPPTGDATA lpPTGData=GetThreadStoragePointer();
HWND hWndParent = GetParent(hDlg);
if(hWndParent && !pt_bDontShowCancel) // Find dlg may request not to see the cancel dlg
EnableWindow(hWndParent, FALSE); // Dont want to disable the find dialog in that event
}
// lParam contains pointer to LDAPSEARCHPARAMS struct, set it
// in window data
Assert(lParam);
SetWindowLongPtr(hDlg,DWLP_USER,lParam);
pLDAPSearchParams = (PLDAPSEARCHPARAMS) lParam;
if(InitLDAPClientLib())
pLDAPSearchParams->ulFlags |= LSP_InitDll;
// Put the dialog in the center of the parent window
CenterWindow(hDlg, GetParent(hDlg));
// Put the server name on the dialog.
LoadString(hinstMapiX, idsLDAPCancelMessage, szBuf, CharSizeOf(szBuf));
if (FormatMessage(FORMAT_MESSAGE_FROM_STRING |
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_ARGUMENT_ARRAY,
szBuf,
0, // stringid
0, // dwLanguageId
(LPTSTR)&lpszMsg, // output buffer
0, // MAX_UI_STR
(va_list *)&pLDAPSearchParams->lpszServer))
{
SetDlgItemText(hDlg, IDC_LDAPCANCEL_STATIC_PLEASEWAIT, lpszMsg);
IF_WIN32(LocalFreeAndNull(&lpszMsg);)
IF_WIN16(FormatMessageFreeMem(lpszMsg);)
}
if(bIsSimpleSearch(pLDAPSearchParams->lpszServer))
pLDAPSearchParams->ulFlags |= LSP_SimpleSearch;
if (!(pLDAPSearchParams->ulFlags & LSP_ShowAnim)) // This means search came from the Search dialog
{
// While the bind is going on, there is no visual feedback to the user
// We turn on the hidden static on the Search dialog that says "Connecting..."
HWND hWndParent = GetParent(hDlg);
if( hWndParent &&
GetDlgItem(hWndParent, IDC_TAB_FIND) &&
GetDlgItem(hWndParent, IDC_FIND_ANIMATE1))
{
// Make sure that the parent is the find dialog and nothing else
TCHAR sz[MAX_PATH];
LoadString(hinstMapiX, idsFindConnecting, sz, CharSizeOf(sz));
SetWindowText(hWndParent, sz);
UpdateWindow(hWndParent);
}
}
// Perform the bind operation.
Assert(pLDAPSearchParams->lpszServer);
{
BOOL fUseSynchronousBind = (pLDAPSearchParams->ulFlags & LSP_UseSynchronousBind);
pLDAPSearchParams->ulLDAPValue = use_ldap_v3;
pLDAPSearchParams->ulError = OpenConnection( pLDAPSearchParams->lpszServer,
pLDAPSearchParams->ppLDAP,
&pLDAPSearchParams->ulTimeout,
&pLDAPSearchParams->ulMsgID,
&fUseSynchronousBind,
pLDAPSearchParams->ulLDAPValue,
pLDAPSearchParams->lpszBindDN,
pLDAPSearchParams->dwAuthType);
if(fUseSynchronousBind)
pLDAPSearchParams->ulFlags |= LSP_UseSynchronousBind;
else
pLDAPSearchParams->ulFlags &= ~LSP_UseSynchronousBind;
}
if (!(pLDAPSearchParams->ulFlags & LSP_ShowAnim)) // This means search came from the Search dialog
{
// We turn off the hidden static on the Search dialog that says "Connecting..."
HWND hWndParent = GetParent(hDlg);
if( hWndParent &&
GetDlgItem(hWndParent, IDC_TAB_FIND) &&
GetDlgItem(hWndParent, IDC_FIND_ANIMATE1))
{
// Make sure that the parent is the find dialog and nothing else
TCHAR sz[MAX_PATH];
LoadString(hinstMapiX, idsSearchDialogTitle, sz, CharSizeOf(sz));
SetWindowText(hWndParent, sz);
UpdateWindow(hWndParent);
}
}
if (LDAP_SUCCESS != pLDAPSearchParams->ulError)
{
SendMessage(hDlg, WM_CLOSE, 0, 0);
return TRUE;
}
if (pLDAPSearchParams->ulFlags & LSP_UseSynchronousBind)
{
BOOL fRet;
// The actions that need to be performed after the bind are done
// in BindProcessResults, so we call this even though there really are
// no results to process in the synchronous case.
fRet = BindProcessResults(pLDAPSearchParams, hDlg, NULL);
if (FALSE == fRet)
{
SendMessage(hDlg, WM_CLOSE, 0, 0);
return TRUE;
}
}
else
{
// Start a timer for the server polling.
if (LDAP_BIND_TIMER_ID != SetTimer( hDlg,LDAP_BIND_TIMER_ID,LDAP_SEARCH_TIMER_DELAY,NULL))
{
// Cancel the bind if we couldn't start the timer.
gpfnLDAPAbandon(*pLDAPSearchParams->ppLDAP,pLDAPSearchParams->ulMsgID);
pLDAPSearchParams->ulError = LDAP_LOCAL_ERROR;
SendMessage(hDlg, WM_CLOSE, 0, 0);
return TRUE;
}
pLDAPSearchParams->unTimerID = LDAP_BIND_TIMER_ID;
}
// Load the AVI
hWndAnim = GetDlgItem(hDlg, IDC_LDAPCANCEL_ANIMATE);
Animate_Open(hWndAnim, MAKEINTRESOURCE(IDR_AVI_WABFIND));
Animate_Play(hWndAnim, 0, 1, 0);
Animate_Stop(hWndAnim);
// Play it only if this is a resolve operation
if ((pLDAPSearchParams->ulFlags & LSP_ShowAnim))
{
Animate_Play(hWndAnim, 0, -1, -1);
}
EnableWindow(hDlg, FALSE);
return TRUE;
}
case WM_TIMER:
{
struct l_timeval PollTimeout;
PLDAPSEARCHPARAMS pLDAPSearchParams;
Assert ((wParam == LDAP_SEARCH_TIMER_ID) || (wParam == LDAP_BIND_TIMER_ID));
// get data pointer from window data
pLDAPSearchParams =
(PLDAPSEARCHPARAMS) GetWindowLongPtr(hDlg,DWLP_USER);
Assert(pLDAPSearchParams);
if(pLDAPSearchParams->unTimerID == wParam)
{
// Poll the server for results
ZeroMemory(&PollTimeout, sizeof(struct l_timeval));
pLDAPSearchParams->ulResult = gpfnLDAPResult(
*pLDAPSearchParams->ppLDAP,
pLDAPSearchParams->ulMsgID,
LDAP_MSG_ALL, //LDAP_MSG_RECEIVED, //LDAP_MSG_ALL, // Get all results before returning
&PollTimeout, // Timeout immediately (poll)
pLDAPSearchParams->lplpResult);
// If the return value was zero, the call timed out
if (0 == pLDAPSearchParams->ulResult)
{
// See if the timeout has expired.
pLDAPSearchParams->ulTimeElapsed += LDAP_SEARCH_TIMER_DELAY;
if (pLDAPSearchParams->ulTimeElapsed >= pLDAPSearchParams->ulTimeout)
{
pLDAPSearchParams->ulError = LDAP_TIMEOUT;
}
else
{
// Timeout has not expired, and no results were returned.
// See if the dialog is supposed to be displayed at this point.
if (pLDAPSearchParams->ulTimeElapsed >= SEARCH_CANCEL_DIALOG_DELAY)
{
LPPTGDATA lpPTGData=GetThreadStoragePointer();
if(pt_hWndFind && !pt_bDontShowCancel) // Find dlg may request not to see the cancel dlg
{
ShowWindow(hDlg, SW_SHOW);
EnableWindow(hDlg, TRUE);
}
}
return TRUE;
}
}
// If the return value was anything but zero, we either have
// results or an error ocurred
else
{
// See if this is the bind timer or the search timer
if (LDAP_SEARCH_TIMER_ID == pLDAPSearchParams->unTimerID)
{
// Process the results
KillTimer(hDlg, LDAP_SEARCH_TIMER_ID);
if (pLDAPSearchParams->ulFlags & LSP_ResolveMultiple)
{
if(ResolveProcessResults(pLDAPSearchParams, hDlg))
return TRUE; // We have more searches to do
}
else if(LDAP_ERROR == pLDAPSearchParams->ulResult)
{
pLDAPSearchParams->ulError = (*pLDAPSearchParams->ppLDAP)->ld_errno;
}
}
else if (LDAP_BIND_TIMER_ID == pLDAPSearchParams->unTimerID)
{
BOOL fRet;
BOOL bKillTimer = TRUE;
fRet = BindProcessResults(pLDAPSearchParams, hDlg, &bKillTimer);
if(bKillTimer)
KillTimer(hDlg, LDAP_BIND_TIMER_ID);
if (TRUE == fRet)
return TRUE; // We have more searches to do
}
else
{
// Not our timer. Shouldn't happen.
return FALSE;
}
}
}
else
KillTimer(hDlg, wParam);
//Stop the animation if it is running
if (pLDAPSearchParams->ulFlags & LSP_ShowAnim)
Animate_Stop(GetDlgItem(hDlg, IDC_LDAPCANCEL_ANIMATE));
SendMessage(hDlg, WM_CLOSE, 0, 0);
return TRUE;
}
case WM_CLOSE:
{
PLDAPSEARCHPARAMS pLDAPSearchParams;
// get data pointer from window data
pLDAPSearchParams = (PLDAPSEARCHPARAMS) GetWindowLongPtr(hDlg,DWLP_USER);
Assert(pLDAPSearchParams);
KillTimer(hDlg, pLDAPSearchParams->unTimerID);
//Stop the animation if it is running
if (pLDAPSearchParams->ulFlags & LSP_ShowAnim)
Animate_Stop(GetDlgItem(hDlg, IDC_LDAPCANCEL_ANIMATE));
if(pLDAPSearchParams->ulFlags & LSP_AbandonSearch)
{
// Abandon the search and set the error code to note the cancel
gpfnLDAPAbandon(*pLDAPSearchParams->ppLDAP,pLDAPSearchParams->ulMsgID);
pLDAPSearchParams->ulError = LDAP_USER_CANCELLED;
}
// Must set hDlgCancel to NULL in order to exit the message loop.
pLDAPSearchParams->hDlgCancel = NULL;
if(pLDAPSearchParams->ulFlags & LSP_InitDll)
DeinitLDAPClientLib();
{
LPPTGDATA lpPTGData=GetThreadStoragePointer();
HWND hWndParent = GetParent(hDlg);
if(hWndParent)
EnableWindow(hWndParent, TRUE);
pt_hDlgCancel = NULL;
}
DestroyWindow(hDlg);
return TRUE;
}
case WM_COMMAND:
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case IDCANCEL:
{
PLDAPSEARCHPARAMS pLDAPSearchParams = (PLDAPSEARCHPARAMS) GetWindowLongPtr(hDlg,DWLP_USER);
pLDAPSearchParams->ulFlags |= LSP_AbandonSearch;
SendMessage(hDlg, WM_CLOSE, 0, 0);
return TRUE;
}
}
break;
}
return FALSE;
}
//*******************************************************************
//
// FUNCTION: CenterWindow
//
// PURPOSE: Center one window over another.
//
// PARAMETERS: hwndChild - window to center
// hwndParent - window to use as center reference
//
// RETURNS: Returns result of SetWindowPos
//
// HISTORY:
// 96/10/28 markdu Created.
//
//*******************************************************************
BOOL CenterWindow (
HWND hwndChild,
HWND hwndParent)
{
RECT rChild, rParent;
int wChild, hChild, wParent, hParent;
int wScreen, hScreen, xNew, yNew;
HDC hdc;
Assert(hwndChild);
// Get the Height and Width of the child window
GetWindowRect (hwndChild, &rChild);
wChild = rChild.right - rChild.left;
hChild = rChild.bottom - rChild.top;
// If there is no parent, put it in the center of the screen
if ((NULL == hwndParent) || !IsWindow(hwndParent))
{
return SetWindowPos(hwndChild, NULL,
((GetSystemMetrics(SM_CXSCREEN) - wChild) / 2),
((GetSystemMetrics(SM_CYSCREEN) - hChild) / 2),
0, 0, SWP_NOSIZE | SWP_NOACTIVATE);
}
// Get the Height and Width of the parent window
GetWindowRect (hwndParent, &rParent);
wParent = rParent.right - rParent.left;
hParent = rParent.bottom - rParent.top;
// Get the display limits
hdc = GetDC (hwndChild);
wScreen = GetDeviceCaps (hdc, HORZRES);
hScreen = GetDeviceCaps (hdc, VERTRES);
ReleaseDC (hwndChild, hdc);
// Calculate new X position, then adjust for screen
xNew = rParent.left + ((wParent - wChild) /2);
if (xNew < 0) {
xNew = 0;
} else if ((xNew+wChild) > wScreen) {
xNew = wScreen - wChild;
}
// Calculate new Y position, then adjust for screen
yNew = rParent.top + ((hParent - hChild) /2);
if (yNew < 0) {
yNew = 0;
} else if ((yNew+hChild) > hScreen) {
yNew = hScreen - hChild;
}
// Set it, and return
return SetWindowPos (hwndChild, NULL,
xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
}
/*
-
- StartLDAPSearch
-
* Starts the LDAP Search
*
*/
BOOL StartLDAPSearch(HWND hDlg, PLDAPSEARCHPARAMS pLDAPSearchParams, LPTSTR lpFilter)
{
BOOL fRet = FALSE;
LPTSTR szFilterT, szFilter = NULL;
if (lpFilter)
szFilterT = lpFilter;
else
{
if ((pLDAPSearchParams->ulFlags & LSP_IsNTDS) && pLDAPSearchParams->szNTFilter)
szFilterT = pLDAPSearchParams->szNTFilter;
else
szFilterT = pLDAPSearchParams->szFilter;
}
Assert(szFilterT);
if (pLDAPSearchParams->ulFlags & LSP_IsNTDS)
{
// [PaulHi] 4/20/99 Raid 73205 Allow NTDS group AND people searches.
LPTSTR tszFilterGP = NULL;
BOOL bFilterSucceeded = FALSE;
// Put together person and group categories
// [PaulHi] 6/21/99 Put together simpler search string
// ( & ( | (mail=chuck*) (anr=chuck) ) (|(objectcategory=person) (objectcategory=group) ) )
if (BuildOpFilter(&tszFilterGP, (LPTSTR)cszAllPersonFilter, (LPTSTR)cszAllGroupFilter, FILTER_OP_OR) == hrSuccess)
{
// Add to existing filter
bFilterSucceeded = (BuildOpFilter(&szFilter, szFilterT, tszFilterGP, FILTER_OP_AND) == hrSuccess);
LocalFreeAndNull(&tszFilterGP);
}
if (!bFilterSucceeded)
goto out;
}
else
szFilter = szFilterT;
DebugTrace(TEXT("Starting search for%s\n"),szFilter);
if (pLDAPSearchParams->ulFlags & LSP_UseSynchronousSearch)
{
pLDAPSearchParams->ulError = gpfnLDAPSearchS(*pLDAPSearchParams->ppLDAP, pLDAPSearchParams->szBase, pLDAPSearchParams->ulScope,
szFilter,
pLDAPSearchParams->ppszAttrs, pLDAPSearchParams->ulAttrsonly, pLDAPSearchParams->lplpResult);
if(LDAP_SUCCESS != pLDAPSearchParams->ulError)
{
DebugTrace(TEXT("LDAP Error: 0x%.2x %s\n"),(*(pLDAPSearchParams->ppLDAP))->ld_errno, gpfnLDAPErr2String((*(pLDAPSearchParams->ppLDAP))->ld_errno));
goto out;
}
}
else
{
#ifdef PAGED_RESULT_SUPPORT
// WAB's synchronous search calls never need to deal with paged results
// so for now (11/5/98) we don't do paged results stuff for synchronous calls.
// Instead we only do that stuff for Async since the UI driven LDAP calls are
// all Async
if(bSupportsLDAPPagedResults(pLDAPSearchParams))
InitLDAPPagedSearch(FALSE, pLDAPSearchParams, lpFilter);
else
#endif //#ifdef PAGED_RESULT_SUPPORT
{
pLDAPSearchParams->ulMsgID = gpfnLDAPSearch(*pLDAPSearchParams->ppLDAP, pLDAPSearchParams->szBase, pLDAPSearchParams->ulScope,
szFilter,
pLDAPSearchParams->ppszAttrs, pLDAPSearchParams->ulAttrsonly);
}
if(LDAP_ERROR == pLDAPSearchParams->ulMsgID)
{
DebugTrace(TEXT("LDAP Error: 0x%.2x %s\n"),(*(pLDAPSearchParams->ppLDAP))->ld_errno, gpfnLDAPErr2String((*(pLDAPSearchParams->ppLDAP))->ld_errno));
goto out;
}
}
if(!(pLDAPSearchParams->ulFlags & LSP_UseSynchronousSearch))
{
// Start a timer for the server polling.
if (LDAP_SEARCH_TIMER_ID != SetTimer(hDlg, LDAP_SEARCH_TIMER_ID, LDAP_SEARCH_TIMER_DELAY, NULL))
{
// Cancel the search if we couldn't start the timer.
gpfnLDAPAbandon( *pLDAPSearchParams->ppLDAP, pLDAPSearchParams->ulMsgID);
pLDAPSearchParams->ulError = LDAP_LOCAL_ERROR;
goto out;
}
pLDAPSearchParams->unTimerID = LDAP_SEARCH_TIMER_ID;
}
fRet = TRUE;
out:
if (szFilter != szFilterT)
LocalFreeAndNull(&szFilter);
return fRet;
}
//*******************************************************************
//
// FUNCTION: ResolveDoNextSearch
//
// PURPOSE: Start an asynchronous search for the next entry in
// the resolve adrlist.
//
// PARAMETERS: pLDAPSearchParams - search information
// hDlg - cancel dialog window handle
//
// RETURNS: Returns TRUE if there is a new search in progress.
// Returns FALSE if there is no more work left to do.
//
// HISTORY:
// 96/10/31 markdu Created.
//
//*******************************************************************
BOOL ResolveDoNextSearch(
PLDAPSEARCHPARAMS pLDAPSearchParams,
HWND hDlg,
BOOL bSecondPass)
{
LPADRENTRY lpAdrEntry;
ULONG ulAttrIndex;
ULONG ulEntryIndex;
ULONG ulcbFilter;
HRESULT hr = hrSuccess;
LPTSTR szFilter = NULL;
LPTSTR szNameFilter = NULL;
LPTSTR szEmailFilter = NULL;
LPTSTR szSimpleFilter = NULL;
LPTSTR lpFilter = NULL;
BOOL bUnicode = pLDAPSearchParams->bUnicode;
LPTSTR lpszInput = NULL;
BOOL bRet = FALSE;
ULONG cchSize;
// search for each name in the lpAdrList
ulEntryIndex = pLDAPSearchParams->ulEntryIndex;
while (ulEntryIndex < pLDAPSearchParams->lpAdrList->cEntries)
{
// Make sure we don't resolve an entry which is already resolved.
if (pLDAPSearchParams->lpFlagList->ulFlag[ulEntryIndex] != MAPI_RESOLVED)
{
// Search for this address
lpAdrEntry = &(pLDAPSearchParams->lpAdrList->aEntries[ulEntryIndex]);
// Look through the ADRENTRY for a PR_DISPLAY_NAME
for (ulAttrIndex = 0; ulAttrIndex < lpAdrEntry->cValues; ulAttrIndex++)
{
ULONG ulPropTag = lpAdrEntry->rgPropVals[ulAttrIndex].ulPropTag;
if(!bUnicode && PROP_TYPE(ulPropTag)==PT_STRING8)
ulPropTag = CHANGE_PROP_TYPE(ulPropTag, PT_UNICODE);
if ( ulPropTag == PR_DISPLAY_NAME || ulPropTag == PR_EMAIL_ADDRESS)
{
LPTSTR lpszInputCopy = NULL;
ULONG ulcIllegalChars = 0;
LPTSTR lpFilter = NULL;
BOOL bEmail = (ulPropTag == PR_EMAIL_ADDRESS);
if(!bUnicode)
LocalFreeAndNull(&lpszInput);
else
lpszInput = NULL;
lpszInput = bUnicode ? // <note> assumes UNICODE defined
lpAdrEntry->rgPropVals[ulAttrIndex].Value.lpszW :
ConvertAtoW(lpAdrEntry->rgPropVals[ulAttrIndex].Value.lpszA);
if (lpszInput)
ulcIllegalChars = CountIllegalChars(lpszInput);
if (ulcIllegalChars)
{
// Allocate a copy of the input, large enough to replace the illegal chars
// with escaped versions .. each escaped char is replaced by '\xx'
cchSize = lstrlen(lpszInput) + ulcIllegalChars*2 + 1;
lpszInputCopy = LocalAlloc(LMEM_ZEROINIT, sizeof(TCHAR)*cchSize);
if (NULL == lpszInputCopy)
{
hr = ResultFromScode(MAPI_E_NOT_ENOUGH_MEMORY);
break;
}
EscapeIllegalChars(lpszInput, lpszInputCopy, cchSize);
lpszInput = lpszInputCopy;
}
// We should have figured this out by now
Assert(pLDAPSearchParams->ulFlags & (LSP_IsNTDS | LSP_IsNotNTDS));
// Set up the search filter.
if (pLDAPSearchParams->ulFlags & LSP_IsNTDS)
{
hr = CreateSimpleSearchFilter(&szNameFilter, &szEmailFilter, &szSimpleFilter, lpszInput, FIRST_PASS);
if ((hrSuccess == hr) && !bSecondPass)
{
LocalFreeAndNull(&szNameFilter);
hr = BuildBasicFilter(&szNameFilter, (LPTSTR)cszAttr_anr, lpszInput, FALSE);
if (hrSuccess != hr)
{
LocalFreeAndNull(&szEmailFilter);
LocalFreeAndNull(&szSimpleFilter);
}
else
lpFilter = szNameFilter;
}
}
else
hr = CreateSimpleSearchFilter( &szNameFilter, &szEmailFilter, &szSimpleFilter, lpszInput, (bSecondPass ? UMICH_PASS : FIRST_PASS) );
if(lpszInputCopy)
LocalFree(lpszInputCopy);
if (hrSuccess != hr)
{
continue;
}
if (!lpFilter)
lpFilter = (pLDAPSearchParams->ulFlags & LSP_SimpleSearch) ? szSimpleFilter : szNameFilter;
if (szEmailFilter)
{
if (bEmail)
{
cchSize = lstrlen(szEmailFilter) + 1;
if(szFilter = LocalAlloc(LMEM_ZEROINIT, sizeof(TCHAR)*cchSize))
StrCpyN(szFilter, szEmailFilter, cchSize);
}
else
{
// No email field was given, so OR in the alternate email filter.
hr = BuildOpFilter( &szFilter, szEmailFilter, lpFilter, FILTER_OP_OR);
}
if (hrSuccess != hr || !szFilter)
{
LocalFreeAndNull(&szNameFilter);
LocalFreeAndNull(&szEmailFilter);
LocalFreeAndNull(&szSimpleFilter);
continue;
}
}
else
{
cchSize = lstrlen(lpFilter) + 1;
szFilter = LocalAlloc(LMEM_ZEROINIT, sizeof(TCHAR)*cchSize);
if (NULL == szFilter)
{
LocalFreeAndNull(&szNameFilter);
LocalFreeAndNull(&szEmailFilter);
LocalFreeAndNull(&szSimpleFilter);
continue;
}
StrCpyN(szFilter, lpFilter, cchSize);
}
LocalFreeAndNull(&szNameFilter);
LocalFreeAndNull(&szEmailFilter);
LocalFreeAndNull(&szSimpleFilter);
if(StartLDAPSearch(hDlg, pLDAPSearchParams, szFilter))
{
// If no error occurred in ldap_search, return with this as
// our search. Otherwise, go on to the next entry.
pLDAPSearchParams->ulEntryIndex = ulEntryIndex;
// Free the search filter memory
LocalFreeAndNull(&szFilter);
bRet = TRUE;
goto out;
}
// Free the search filter memory
LocalFreeAndNull(&szFilter);
} // if value is PR_DISPLAY_NAME
} // for each value
} // if already resolved
// Go on to the next entry.
ulEntryIndex++;
}
out:
if(!bUnicode)
LocalFreeAndNull(&lpszInput);
return bRet;
}
//*******************************************************************
//
// FUNCTION: ResolveProcessResults
//
// PURPOSE: Process the results of the last search and put them
// in the resolve adrlist.
//
// PARAMETERS: pLDAPSearchParams - search information
// hDlg - cancel dialog window handle
//
// RETURNS: Returns TRUE if there is a new search in progress.
// Returns FALSE if there is no more work left to do.
//
// HISTORY:
// 96/10/31 markdu Created.
//
//*******************************************************************
BOOL ResolveProcessResults(
PLDAPSEARCHPARAMS pLDAPSearchParams,
HWND hDlg)
{
LPADRENTRY lpAdrEntry;
SCODE sc;
ULONG ulEntryIndex;
LPSPropValue lpPropArray = NULL;
LPSPropValue lpPropArrayNew = NULL;
ULONG ulcPropsNew;
ULONG ulcProps = 0;
HRESULT hr = hrSuccess;
LDAP* pLDAP = NULL;
LDAPMessage* lpResult = NULL;
LDAPMessage* lpEntry;
LPTSTR szDN;
ULONG ulResult = LDAP_SUCCESS;
ULONG ulcEntries;
ULONG ulcAttrs = 0;
LPTSTR* ppszAttrs;
BOOL bUnicode = pLDAPSearchParams->bUnicode;
// Set up local variables for frequently-accessed structure members
pLDAP = *pLDAPSearchParams->ppLDAP;
lpResult = *pLDAPSearchParams->lplpResult;
ulEntryIndex = pLDAPSearchParams->ulEntryIndex;
ulResult = CheckErrorResult(pLDAPSearchParams, LDAP_RES_SEARCH_RESULT);
if (LDAP_SUCCESS != ulResult)
{
DebugTrace(TEXT("LDAPCONT_ResolveNames: ldap_search returned %d.\n"), ulResult);
if (LDAP_UNDEFINED_TYPE == ulResult)
{
// the search failed we need to search again with a simplified filter ..
// This is true mostly for umich and we need to work against them ..
// free the search results memory
if (lpResult)
{
gpfnLDAPMsgFree(lpResult);
*pLDAPSearchParams->lplpResult = NULL;
}
return ResolveDoNextSearch(pLDAPSearchParams, hDlg, TRUE);
}
// If this entry was not found continue without error
if (LDAP_NO_SUCH_OBJECT != ulResult)
{
hr = HRFromLDAPError(ulResult, pLDAP, MAPI_E_NOT_FOUND);
// See if the result was the special value that tells us there were more
// entries than could be returned. If so, then there must be more than one
// entry, so we might just return ambiguous for this one.
if (ResultFromScode(MAPI_E_UNABLE_TO_COMPLETE) == hr)
{
// 96/09/28 markdu BUG 36766
// If we mark this as ambiguous, the check names dialog will come up.
// We only want to do this is we actually got some results, otherwise
// the list will be empty.
ulcEntries = gpfnLDAPCountEntries(pLDAP, lpResult);
if (0 == ulcEntries)
{
// We got back no results, so mark the entry as resolved so we
// don't display the check names dialog.
DebugTrace(TEXT("ResolveNames found more than 1 match but got no results back\n"));
pLDAPSearchParams->lpFlagList->ulFlag[ulEntryIndex] = MAPI_UNRESOLVED;
}
else
{
// We got back multiple entries, so mark this as ambiguous
DebugTrace(TEXT("ResolveNames found more than 1 match... MAPI_AMBIGUOUS\n"));
pLDAPSearchParams->lpFlagList->ulFlag[ulEntryIndex] = MAPI_AMBIGUOUS;
}
}
}
goto exit;
}
// Count the entries.
ulcEntries = gpfnLDAPCountEntries(pLDAP, lpResult);
if (1 < ulcEntries)
{
DebugTrace(TEXT("ResolveNames found more than 1 match... MAPI_AMBIGUOUS\n"));
pLDAPSearchParams->lpFlagList->ulFlag[ulEntryIndex] = MAPI_AMBIGUOUS;
}
else if (1 == ulcEntries)
{
// get the first entry in the search result
lpAdrEntry = &(pLDAPSearchParams->lpAdrList->aEntries[ulEntryIndex]);
lpEntry = gpfnLDAPFirstEntry(pLDAP, lpResult);
if (NULL == lpEntry)
{
goto exit;
}
// Allocate a new buffer for the MAPI property array.
ppszAttrs = pLDAPSearchParams->ppszAttrs;
while (NULL != *ppszAttrs)
{
ppszAttrs++;
ulcAttrs++;
}
hr = HrLDAPEntryToMAPIEntry( pLDAP, lpEntry,
(LPTSTR) pLDAPSearchParams->lpszServer,
ulcAttrs, // standard number of attributes
(pLDAPSearchParams->ulFlags & LSP_IsNTDS),
&ulcProps,
&lpPropArray);
if (hrSuccess != hr)
{
goto exit;
}
if(!bUnicode) // convert native UNICODE to ANSI if we need to ...
{
if(sc = ScConvertWPropsToA((LPALLOCATEMORE) (&MAPIAllocateMore), lpPropArray, ulcProps, 0))
goto exit;
}
// Merge the new props with the ADRENTRY props
sc = ScMergePropValues(lpAdrEntry->cValues,
lpAdrEntry->rgPropVals, // source1
ulcProps,
lpPropArray, // source2
&ulcPropsNew,
&lpPropArrayNew); // dest
if (sc)
{
goto exit;
}
// Free the original prop value array
FreeBufferAndNull((LPVOID *) (&(lpAdrEntry->rgPropVals)));
lpAdrEntry->cValues = ulcPropsNew;
lpAdrEntry->rgPropVals = lpPropArrayNew;
// Free the temp prop value array
FreeBufferAndNull(&lpPropArray);
// Mark this entry as found.
pLDAPSearchParams->lpFlagList->ulFlag[ulEntryIndex] = MAPI_RESOLVED;
}
else
{
// 96/08/08 markdu BUG 35481 No error and no results means "not found"
// If this entry was not found continue without error
}
exit:
// free the search results memory
if (lpResult)
{
gpfnLDAPMsgFree(lpResult);
*pLDAPSearchParams->lplpResult = NULL;
}
// Free the temp prop value array
FreeBufferAndNull(&lpPropArray);
// Initiate the next search.
pLDAPSearchParams->ulEntryIndex++;
return ResolveDoNextSearch(pLDAPSearchParams, hDlg, FALSE);
}
//*******************************************************************
//
// FUNCTION: BindProcessResults
//
// PURPOSE: Process the results of the bind operation. If successful,
// launch a search.
//
// PARAMETERS: pLDAPSearchParams - search information
// hDlg - cancel dialog window handle
//
// RETURNS: Returns TRUE if the bind was successful and we should
// go ahead with the searches.
// Returns FALSE if the bind failed.
//
// HISTORY:
// 96/11/01 markdu Created.
//
//*******************************************************************
BOOL BindProcessResults(PLDAPSEARCHPARAMS pLDAPSearchParams,
HWND hDlg,
BOOL * lpbNoMoreSearching)
{
LDAPMessage* lpResult = NULL;
LDAP* pLDAP = NULL;
ULONG ulResult = LDAP_SUCCESS;
// Set up local variables for frequently-accessed structure members
lpResult = *pLDAPSearchParams->lplpResult;
pLDAP = *pLDAPSearchParams->ppLDAP;
// Check for error results here if bind was asynchronous. If sync, we have
// already dealt with this.
if (!(pLDAPSearchParams->ulFlags & LSP_UseSynchronousBind))
{
// If an error occurred in ldap_result, return the error code
if (LDAP_ERROR == pLDAPSearchParams->ulResult)
{
ulResult = pLDAP->ld_errno;
}
// Check the result for errors
else if (NULL != lpResult)
{
ulResult = gpfnLDAPResult2Error(pLDAP,lpResult,FALSE);
}
ulResult = CheckErrorResult(pLDAPSearchParams, LDAP_RES_BIND);
// free the search results memory
if (lpResult)
{
gpfnLDAPMsgFree(lpResult);
*pLDAPSearchParams->lplpResult = NULL;
}
if (LDAP_SUCCESS != ulResult)
{
if(ulResult == LDAP_PROTOCOL_ERROR && pLDAPSearchParams->ulLDAPValue == use_ldap_v3)
{
// This means the server failed the v3 connection
// abort and try again
BOOL fUseSynchronousBind = (pLDAPSearchParams->ulFlags & LSP_UseSynchronousBind);
gpfnLDAPAbandon(*pLDAPSearchParams->ppLDAP, pLDAPSearchParams->ulMsgID);
gpfnLDAPAbandon(*pLDAPSearchParams->ppLDAP, pLDAPSearchParams->ulMsgID);
// [PaulHi] 1/7/99 Since we try a new bind we need to relinquish the old binding,
// otherwise the server will support two connections until the original V3 attempt
// times out.
gpfnLDAPUnbind(*pLDAPSearchParams->ppLDAP);
pLDAPSearchParams->ulLDAPValue = use_ldap_v2;
pLDAPSearchParams->ulError = OpenConnection( pLDAPSearchParams->lpszServer,
pLDAPSearchParams->ppLDAP,
&pLDAPSearchParams->ulTimeout,
&pLDAPSearchParams->ulMsgID,
&fUseSynchronousBind,
pLDAPSearchParams->ulLDAPValue,
pLDAPSearchParams->lpszBindDN,
pLDAPSearchParams->dwAuthType);
if(lpbNoMoreSearching)
*lpbNoMoreSearching = FALSE;
return TRUE;
}
// 96/12/09 markdu BUG 10537 If the bind returned one of these error
// messages, it probably means that the account name (DN) passed to the
// bind was incorrect or in the wrong format. Map these to an error code
// that will result in a better error message than "entry not found".
if ((LDAP_NAMING_VIOLATION == ulResult) || (LDAP_UNWILLING_TO_PERFORM == ulResult))
{
ulResult = LDAP_INVALID_CREDENTIALS;
}
// Bind was unsuccessful.
pLDAPSearchParams->ulError = ulResult;
return FALSE;
}
} // if (FALSE == pLDAPSearchParams->fUseSynchronousBind)
// we need to determine if a particular server is NTDS or not .. this is as good
// a place as any to make the check ...
bCheckIfNTDS(pLDAPSearchParams);
// See if we need to do the single search or if we need
// to launch the multiple searches
if (pLDAPSearchParams->ulFlags & LSP_ResolveMultiple)
{
// Initiate the first search.
return ResolveDoNextSearch(pLDAPSearchParams, hDlg, FALSE);
}
else
{
if(!StartLDAPSearch(hDlg, pLDAPSearchParams, NULL))
return FALSE;
}
return TRUE;
}
//*******************************************************************
//
// FUNCTION: BuildBasicFilter
//
// PURPOSE: Build an RFC1558 compliant filter of the form
// (A=B*) where A is an attribute, B is a value, and
// the * is optional. The buffer for the filter is
// allocated here, and must be freed by the caller.
//
// PARAMETERS: lplpszFilter - recieves buffer containing the filter
// lpszA - part A of (A=B*)
// lpszB - part B of (A=B*)
// fStartsWith - if TRUE, append the * so the filter
// will be a "starts with" filter
//
// RETURNS: HRESULT
//
// HISTORY:
// 96/12/22 markdu Created.
//
//*******************************************************************
HRESULT BuildBasicFilter(
LPTSTR FAR* lplpszFilter,
LPTSTR lpszA,
LPTSTR lpszB,
BOOL fStartsWith)
{
HRESULT hr = hrSuccess;
ULONG cchFilter;
// Allocate enough space for the filter string
cchFilter =
(FILTER_EXTRA_BASIC + // includes space for the *
lstrlen(lpszA) +
lstrlen(lpszB) + 1);
*lplpszFilter = LocalAlloc(LMEM_ZEROINIT, cchFilter*sizeof(TCHAR));
if (NULL == *lplpszFilter)
{
hr = ResultFromScode(MAPI_E_NOT_ENOUGH_MEMORY);
goto exit;
}
StrCatBuff(*lplpszFilter, cszOpenParen, cchFilter);
StrCatBuff(*lplpszFilter, lpszA, cchFilter);
StrCatBuff(*lplpszFilter, cszEqualSign, cchFilter);
StrCatBuff(*lplpszFilter, lpszB, cchFilter);
if (TRUE == fStartsWith)
{
StrCatBuff(*lplpszFilter, cszStar, cchFilter);
}
StrCatBuff(*lplpszFilter, cszCloseParen, cchFilter);
exit:
return hr;
}
//*******************************************************************
//
// FUNCTION: BuildOpFilter
//
// PURPOSE: Build an RFC1558 compliant filter of the form
// (xAB) where A is an attribute, B is a value, and
// x is either & or |. The buffer for the filter is
// allocated here, and must be freed by the caller.
//
// PARAMETERS: lplpszFilter - recieves buffer containing the filter
// lpszA - part A of (A=B*)
// lpszB - part B of (A=B*)
// dwOp - if FILTER_OP_AND, x is &, if FILTER_OP_OR, x is |
//
// RETURNS: HRESULT
//
// HISTORY:
// 96/12/22 markdu Created.
//
//*******************************************************************
HRESULT BuildOpFilter(
LPTSTR FAR* lplpszFilter,
LPTSTR lpszA,
LPTSTR lpszB,
DWORD dwOp)
{
HRESULT hr = hrSuccess;
ULONG cchFilter;
LPTSTR szOp;
// Allocate enough space for the filter string
cchFilter =
FILTER_EXTRA_OP +
lstrlen(lpszA) +
lstrlen(lpszB) + 1;
*lplpszFilter = LocalAlloc(LMEM_ZEROINIT, sizeof(TCHAR)*cchFilter);
if (NULL == *lplpszFilter)
{
hr = ResultFromScode(MAPI_E_NOT_ENOUGH_MEMORY);
goto exit;
}
switch (dwOp)
{
case FILTER_OP_AND:
szOp = (LPTSTR)cszAnd;
break;
case FILTER_OP_OR:
szOp = (LPTSTR)cszOr;
break;
default:
hr = ResultFromScode(MAPI_E_INVALID_PARAMETER);
goto exit;
}
StrCatBuff(*lplpszFilter, cszOpenParen, cchFilter);
StrCatBuff(*lplpszFilter, szOp, cchFilter);
StrCatBuff(*lplpszFilter, lpszA, cchFilter);
StrCatBuff(*lplpszFilter, lpszB, cchFilter);
StrCatBuff(*lplpszFilter, cszCloseParen, cchFilter);
exit:
return hr;
}
/***************************************************************************
Name : StrICmpN
Purpose : Compare strings, ignore case, stop at N characters
Parameters: szString1 = first string
szString2 = second string
N = number of characters to compare
bCmpI - compare insensitive if TRUE, sensitive if false
Returns : 0 if first N characters of strings are equivalent.
Comment :
***************************************************************************/
int StrICmpN(LPTSTR szString1, LPTSTR szString2, ULONG N, BOOL bCmpI) {
int Result = 0;
if (szString1 && szString2) {
if(bCmpI)
{
szString1 = CharUpper(szString1);
szString2 = CharUpper(szString2);
}
while (*szString1 && *szString2 && N)
{
N--;
if (*szString1 != *szString2)
{
Result = 1;
break;
}
szString1=CharNext(szString1);
szString2=CharNext(szString2);
}
} else {
Result = -1; // arbitrarily non-equal result
}
return(Result);
}
//$$*************************************************
/*
* FreeLDAPURl - frees the LDAPURL struct
*
*
*///*************************************************
void FreeLDAPUrl(LPLDAPURL lplu)
{
if(lplu->lpszServer && lstrlen(lplu->lpszServer))
LocalFreeAndNull(&(lplu->lpszServer));
LocalFreeAndNull(&(lplu->lpszBase));
LocalFreeAndNull(&(lplu->lpszFilter));
if(lplu->ppszAttrib)
{
ULONG i;
for(i=0;i<lplu->ulAttribCount;i++)
{
if(lplu->ppszAttrib[i])
LocalFree(lplu->ppszAttrib[i]);
}
LocalFree(lplu->ppszAttrib);
}
return;
}
//$$//////////////////////////////////////////////////////////////
//
// Parse the LDAP URL into an LDAPURL struct
//
// if the URL has only the server specified, we need to show only the
// search dialog with the server name filled in .. however since we
// tend to fill in default values for the items we are not provided,
// we need a seperate flag to track that only the server existed in the
// given URL
//////////////////////////////////////////////////////////////////
HRESULT ParseLDAPUrl(LPTSTR szLDAPUrl,
LPLDAPURL lplu)
{
HRESULT hr = E_FAIL;
TCHAR szLDAP[] = TEXT("ldap://");
TCHAR szScopeBase[] = TEXT("base");
TCHAR szScopeOne[] = TEXT("one");
TCHAR szScopeSub[] = TEXT("sub");
TCHAR szDefBase[32];
TCHAR szBindName[] = TEXT("bindname=");
LPTSTR lpsz = NULL;
LPTSTR lpszTmp = NULL, lpszMem = NULL;
ULONG cchSize;
DWORD dwCharCount;
// Form of the LDAP URL is
//
// ldap://[<servername>:<port>][/<dn>[?[<attrib>[?[<scope>[?[<filter>[?[<extension>]]]]]]]]]
//
// Translation of above to our parameters is
//
// lpszServer = ServerName
// szBase = dn default = "c=US"
// ppszAttrib = attrib default = all
// ulScope = scope default = base
// szfilter = filter default = (objectclass=*)
// szExtension = extension default = none
//
if(!lplu || !szLDAPUrl)
goto exit;
lplu->bServerOnly = FALSE;
{
// Fill in the default base as c=DefCountry
LPTSTR lpszBase = TEXT("c=%s");
TCHAR szCode[4];
ReadRegistryLDAPDefaultCountry(NULL, 0, szCode, ARRAYSIZE(szCode));
wnsprintf(szDefBase, ARRAYSIZE(szDefBase), lpszBase, szCode);
}
// Make a copy of our URL
// [PaulHi] 3/24/99 Leave room for InternetCanonicalizeUrlW adjustment
{
dwCharCount = 3 * lstrlen(szLDAPUrl);
lpsz = LocalAlloc(LMEM_FIXED, sizeof(WCHAR)*(dwCharCount+1));
if(!lpsz)
goto exit;
StrCpyN(lpsz, szLDAPUrl, dwCharCount+1);
// Since this is most likely a URL on an HTML page, we need to translate its escape
// characters to proper characters .. e.g. %20 becomes ' ' ..
//
// [PaulHi] 3/24/99 !!InternetCanonicalizeUrlW takes buffer size in CHARS and not
// BYTES as the documentation claims. Also doesn't account for the NULL terminator!!
// DWORD dw = sizeof(TCHAR)*(lstrlen(szLDAPUrl));
if ( !InternetCanonicalizeUrlW(szLDAPUrl, lpsz, &dwCharCount, ICU_DECODE | ICU_NO_ENCODE) )
{
DebugTrace(TEXT("ERROR: ParseLDAPUrl, InternetCanonicalizeUrlW failed.\n"));
Assert(0);
}
}
lpszMem = lpszTmp = lpsz;
// Check if this is an LDAP url
if(StrICmpN(lpsz, szLDAP, CharSizeOf(szLDAP)-1, TRUE))
goto exit;
lpszTmp += CharSizeOf(szLDAP)-1;
StrCpyN(lpsz,lpszTmp, dwCharCount+1);
lpszTmp = lpsz;
// If there is no server name, bail ..
// If the next character after the ldap:// is a '/',
// we know there is no server name ..
lplu->bServerOnly = TRUE; // we turn this on for the server and
// then turn it off if we find a filter or dn
if(*lpsz == '/')
{
// null server name .. which is valid
lplu->lpszServer = szEmpty; //NULL?
}
else
{
while( *lpszTmp &&
*lpszTmp != '/')
{
lpszTmp = CharNext(lpszTmp);
}
if(*lpszTmp)
{
LPTSTR lp = lpszTmp;
lpszTmp = CharNext(lpszTmp);
*lp = '\0';
}
cchSize = lstrlen(lpsz)+1;
lplu->lpszServer = LocalAlloc(LMEM_ZEROINIT, sizeof(TCHAR)*cchSize);
if(!lplu->lpszServer)
goto exit;
StrCpyN(lplu->lpszServer, lpsz, cchSize);
lpsz = lpszTmp;
}
// The next item in the filter is the <dn> which is out szBase
// If the next char is a \0 or a '?', then we didnt get a <dn>
if(!*lpsz || *lpsz == '?')
{
cchSize = lstrlen(szDefBase)+1;
lplu->lpszBase = LocalAlloc(LMEM_ZEROINIT, sizeof(TCHAR)*cchSize);
if(!lplu->lpszBase)
goto exit;
StrCpyN(lplu->lpszBase, szDefBase, cchSize);
lpsz = lpszTmp = CharNext(lpsz);
}
else
{
lplu->bServerOnly = FALSE; // if we found a dn, we have something to search for
while( *lpszTmp &&
*lpszTmp != '?')
{
lpszTmp = CharNext(lpszTmp);
}
if(*lpszTmp)
{
LPTSTR lp = lpszTmp;
lpszTmp = CharNext(lpszTmp);
*lp = '\0';
}
cchSize = lstrlen(lpsz)+1;
lplu->lpszBase = LocalAlloc(LMEM_ZEROINIT, sizeof(TCHAR)*cchSize);
if(!lplu->lpszBase)
goto exit;
StrCpyN(lplu->lpszBase, lpsz, cchSize);
lpsz = lpszTmp;
}
// NExt on line is the attributes for this search ...
// There are no attributes if this is the end of the string
// or the current character is a ?
if(!*lpsz || *lpsz == '?')
{
lplu->ppszAttrib = NULL;
lpsz = lpszTmp = CharNext(lpsz);
}
else
{
while( *lpszTmp &&
*lpszTmp != '?')
{
lpszTmp = CharNext(lpszTmp);
}
if(*lpszTmp)
{
LPTSTR lp = lpszTmp;
lpszTmp = CharNext(lpszTmp);
*lp = '\0';
}
{
//Count the commas in the attrib string
LPTSTR lp = lpsz;
ULONG i;
lplu->ulAttribCount = 0;
while(*lp)
{
if(*lp == ',')
lplu->ulAttribCount++;
lp=CharNext(lp);
}
lplu->ulAttribCount++; // one more attribute than commas
lplu->ulAttribCount++; // we must get a display name no matter whether its asked for or not
// otherwise we will fault on NT .. so add a display name param
lplu->ulAttribCount++; // for terminating NULL
lplu->ppszAttrib = LocalAlloc(LMEM_ZEROINIT, sizeof(LPTSTR) * lplu->ulAttribCount);
if(!lplu->ppszAttrib)
goto exit;
lp = lpsz;
for(i=0;i<lplu->ulAttribCount - 2;i++)
{
LPTSTR lp3 = lp;
while(*lp && *lp!= ',')
lp = CharNext(lp);
lp3=CharNext(lp);
*lp = '\0';
lp = lp3;
cchSize = lstrlen(lpsz)+1;
lplu->ppszAttrib[i] = LocalAlloc(LMEM_ZEROINIT, sizeof(TCHAR)*cchSize);
if(!lplu->ppszAttrib[i])
goto exit;
StrCpyN(lplu->ppszAttrib[i], lpsz, cchSize);
lpsz = lp;
}
cchSize = lstrlen(cszAttr_cn)+1;
lplu->ppszAttrib[i] = LocalAlloc(LMEM_ZEROINIT, sizeof(TCHAR)*cchSize);
if(!lplu->ppszAttrib[i])
goto exit;
StrCpyN(lplu->ppszAttrib[i], cszAttr_cn, cchSize);
i++;
lplu->ppszAttrib[i] = NULL;
}
lpsz = lpszTmp;
}
// Next is the scope which can be one of 3 values
if(!*lpsz || *lpsz == '?')
{
lplu->ulScope = LDAP_SCOPE_BASE;
lpsz = lpszTmp = CharNext(lpsz);
}
else
{
if(!StrICmpN(lpsz, szScopeOne, CharSizeOf(szScopeOne)-1, TRUE))
{
lplu->ulScope = LDAP_SCOPE_ONELEVEL;
lpszTmp += CharSizeOf(szScopeOne);
}
else if(!StrICmpN(lpsz, szScopeSub, CharSizeOf(szScopeSub)-1, TRUE))
{
lplu->ulScope = LDAP_SCOPE_SUBTREE;
lpszTmp += CharSizeOf(szScopeSub);
}
else
if(!StrICmpN(lpsz, szScopeBase, CharSizeOf(szScopeBase)-1, TRUE))
{
lplu->ulScope = LDAP_SCOPE_BASE;
lpszTmp += CharSizeOf(szScopeBase);
}
lpsz = lpszTmp;
}
// Finally the filter
if(!*lpsz)
{
// No filter
lpsz = (LPTSTR)cszAllEntriesFilter;// TBD this should be c=DefaultCountry
}
else
lplu->bServerOnly = FALSE; // we have something to search for ..
cchSize = lstrlen(lpsz)+1;
lplu->lpszFilter = LocalAlloc(LMEM_ZEROINIT, sizeof(TCHAR)*cchSize);
if(!lplu->lpszFilter)
goto exit;
// Dont set the lpszFilter to NULL at any point of time or we will leak the above memory
StrCpyN(lplu->lpszFilter, lpsz, cchSize);
// There may be extensions in the filter itself .. so we check for such extensions by
// looking for a '?' .. the extensions are a list of comma-seperated type-value pairs
// e.g bindname=xxx,!sometype=yyy etc ...
//
// Extensions may be of 2 types - critical (which start with '!') and non-critical which dont
// If a handler can't handle the critical extension, then they should decline from handling
// the complete LDAP URL ..
// If they can handle the critical extension then they must handle it
// If the extension is non critical, then its up to us what we do with it ..
{
LPTSTR lp = lplu->lpszFilter;
while(lp && *lp && *lp!='?')
lp++;
if(*lp == '?')
{
lplu->lpszExtension = lp+1; // point this to the extension part
*lp = '\0'; // null terminate the middle to isolate the filter from the extension
}
// only known extension specified in RFC2255 is the bindname extension which
// is the name that one should bind with - this is critical only if !bindname=xxx is specified
if(lplu->lpszExtension)
{
LPTSTR lpTemp = lplu->lpszExtension;
lp = lplu->lpszExtension;
// walk this list looking at subcomponent extensions
// if there is more than 1 that is critical and we dont know how to handle it
// we can bail
while(lpTemp && *lpTemp)
{
BOOL bFoundCritical = FALSE;
// Check if the current extension is a critical or non-critical bind name
//
if(*lpTemp == '!')
{
lpTemp++;
bFoundCritical = TRUE;
}
// Check if this starts with "bindname="
if(lstrlen(lpTemp) >= lstrlen(szBindName) && !StrICmpN(lpTemp, szBindName, lstrlen(szBindName), TRUE))
{
// yes this is a bindname
lpTemp+=lstrlen(szBindName);
lplu->lpszBindName = lpTemp;
lplu->lpszExtension = NULL;
}
else if(bFoundCritical)
{
// it's not a bindname ..
// if this is critical, whatever it is, then we cant handle it
DebugTrace(TEXT("Found unsupported Critical Extension in LDAPURL!!!"));
hr = MAPI_E_NO_SUPPORT;
goto exit;
}
// else // its not critical so we can ignore it
// check if there are any other extensions - walk to the next one
while(*lpTemp && *lpTemp!=',')
lpTemp++;
if(*lpTemp == ',')
{
*lpTemp = '\0'; // terminate current extension
lpTemp++;
}
}
}
}
hr = S_OK;
exit:
LocalFreeAndNull(&lpszMem);
if(HR_FAILED(hr))
FreeLDAPUrl(lplu);
return hr;
}
//*******************************************************************
//
// FUNCTION: HrProcessLDAPUrl
//
// PURPOSE:
// We need to decide what to do with this URL.
// Depending on how much information is in the URL, we decide to do
// different things ..
// If the URL looks complete, we try to do a query
// If the query has single results, we show details on the result
// If the query has multiple results, we show a list of results
// If the URL looks incomplete but has a server name, we will show a
// find dialog with the server name filled in ...
//
// PARAMETERS: ulFLags - 0 or WABOBJECT_LDAPURL_RETURN_MAILUSER
// if the flag is set, it means that return a mailuser
// if the URL query returned a single object
// else return MAPI_E_AMBIGUOUS_RECIPIENT
// specify MAPI_DIALOG to show mesage dialog boxes
//
// RETURNS: HRESULT
//
// HISTORY:
//
//*******************************************************************
HRESULT HrProcessLDAPUrl( LPADRBOOK lpAdrBook,
HWND hWnd,
ULONG ulFlags,
LPTSTR szLDAPUrl,
LPMAILUSER * lppMailUser)
{
HRESULT hr = S_OK;
LDAPURL lu = {0};
BOOL fInitDll = FALSE;
LPMAILUSER lpMailUser = NULL;
if ( (ulFlags & WABOBJECT_LDAPURL_RETURN_MAILUSER) &&
!lppMailUser)
{
hr = MAPI_E_INVALID_PARAMETER;
goto out;
}
if (! (fInitDll = InitLDAPClientLib()))
{
hr = ResultFromScode(MAPI_E_UNCONFIGURED);
goto out;
}
if(HR_FAILED(hr = ParseLDAPUrl(szLDAPUrl, &lu)))
goto out;
/*NULL server is valid .. but only a NULL server is not valid*/
if( ((!lu.lpszServer || !lstrlen(lu.lpszServer)) && lu.bServerOnly) ||
( lstrlen(lu.lpszServer) >= 500 ) ) //bug 21240: the combo box GetItemText fails down the line with really big
// server names so reject server names > 500
// which is a completely random number but should be safe (I hope)
{
DebugTrace(TEXT("Invalid LDAP URL .. aborting\n"));
hr = MAPI_E_INVALID_PARAMETER;
goto out;
}
if (ulFlags & LDAP_AUTH_SICILY || ulFlags & LDAP_AUTH_NEGOTIATE)
lu.dwAuthType = LDAP_AUTH_NEGOTIATE;
else
lu.dwAuthType = LDAP_AUTH_ANONYMOUS;
// Now depending on what we have, we do different things ..
// If there is a server name, but nothing else, show the find
// dialog with the server name filled in ...
//
if (lu.bServerOnly)
{
// we only had a server name
hr = HrShowSearchDialog(lpAdrBook,
hWnd,
(LPADRPARM_FINDINFO) NULL,
&lu,
NULL);
goto out;
}
else
{
LPSPropValue lpPropArray = NULL;
ULONG ulcProps = 0;
LPRECIPIENT_INFO lpList = NULL;
LPPTGDATA lpPTGData=GetThreadStoragePointer();
if(hWnd)
pt_hWndFind = hWnd;
hr = LDAPSearchWithoutContainer(hWnd,
&lu,
(LPSRestriction) NULL,
NULL,
TRUE,
(ulFlags & MAPI_DIALOG) ? MAPI_DIALOG : 0,
&lpList,
&ulcProps,
&lpPropArray);
if(hWnd)
pt_hWndFind = NULL;
if(!(HR_FAILED(hr)) && !lpList && !lpPropArray)
hr = MAPI_E_NOT_FOUND;
if(HR_FAILED(hr))
goto out;
lu.lpList = lpList;
if(ulcProps && lpPropArray)
{
// there is only one item .. show details on it ..
// unless we were asked to return a mailuser
// If we were asked to return a mailuser, then return
// one ...
// We should do IAB_OpenEntry with the LDAP EntryID because that
// will force translation of the UserCertificate property into the
// X509 certificate
// first find the entryid
ULONG i = 0, cbEID = 0, ulObjType = 0;
LPENTRYID lpEID = NULL;
for(i=0;i<ulcProps;i++)
{
if(lpPropArray[i].ulPropTag == PR_ENTRYID)
{
lpEID = (LPENTRYID) lpPropArray[i].Value.bin.lpb;
cbEID = lpPropArray[i].Value.bin.cb;
break;
}
}
if(!lpEID || !cbEID)
goto out;
if(HR_FAILED(hr = lpAdrBook->lpVtbl->OpenEntry(lpAdrBook, cbEID, lpEID,
NULL, 0, &ulObjType,
(LPUNKNOWN *)&lpMailUser)))
{
DebugPrintError(( TEXT("OpenEntry failed .. %x\n"), hr));
goto out;
}
if(ulFlags & WABOBJECT_LDAPURL_RETURN_MAILUSER)
{
*lppMailUser = lpMailUser;
}
else
{
hr = HrShowOneOffDetails( lpAdrBook,
hWnd,
0, (LPENTRYID) NULL,
MAPI_MAILUSER,
(LPMAPIPROP) lpMailUser,
szLDAPUrl,
SHOW_ONE_OFF);
}
if(lpPropArray)
MAPIFreeBuffer(lpPropArray);
}
else if(lpList)
{
// multiple items, display a list of results ..
// unless MailUser was requested in which case return
// ambiguous results ...
if(ulFlags & WABOBJECT_LDAPURL_RETURN_MAILUSER)
{
hr = MAPI_E_AMBIGUOUS_RECIP;
}
else
{
hr = HrShowSearchDialog(lpAdrBook,
hWnd,
(LPADRPARM_FINDINFO) NULL,
&lu,
NULL);
}
}
while(lu.lpList)
{
lpList = lu.lpList->lpNext;
FreeRecipItem(&(lu.lpList));
lu.lpList = lpList;
}
}
out:
if(!(ulFlags & WABOBJECT_LDAPURL_RETURN_MAILUSER) && lpMailUser)
lpMailUser->lpVtbl->Release(lpMailUser);
FreeLDAPUrl(&lu);
if (fInitDll)
DeinitLDAPClientLib();
return hr;
}
//*******************************************************************
//
// FUNCTION: LDAPSearchWithoutContainer
//
// PURPOSE: Searchs a LDAP server which is not registered as a
// container and creates an LPContentList of returned results
//
// PARAMETERS: lplu - LDAPUrl parameters
// lpres - restriction to convert into LDAP search, used if present
// lppContentsList - returned list of items
//
// if bReturnSinglePropArray is set to true, then returns the generated
// prop array if the search produced a single result
//
// RETURNS: HRESULT
//
// HISTORY:
// 97/11/3 created
//
//*******************************************************************
HRESULT LDAPSearchWithoutContainer(HWND hWnd,
LPLDAPURL lplu,
LPSRestriction lpres,
LPTSTR lpAdvFilter,
BOOL bReturnSinglePropArray,
ULONG ulFlags,
LPRECIPIENT_INFO * lppContentsList,
LPULONG lpulcProps,
LPSPropValue * lppPropArray)
{
SCODE sc;
HRESULT hr;
HRESULT hrDeferred = hrSuccess;
LPMAILUSER lpMailUser = NULL;
LPMAPIPROP lpMapiProp = NULL;
ULONG ulcProps = 0;
LPSPropValue lpPropArray = NULL;
LDAPMessage* lpResult = NULL;
LDAPMessage* lpEntry;
LDAP* pLDAP = NULL;
LPTSTR szDN;
ULONG ulResult = 0;
ULONG ulcEntries;
ULONG ulIndex = 0;
LPTSTR szFilter = NULL;
LPTSTR szNTFilter = NULL;
LPTSTR szSimpleFilter = NULL;
LPTSTR szBase = NULL;
BOOL fInitDLL = FALSE;
BOOL bIsNTDSEntry = FALSE;
if(lpAdvFilter)
{
// Advanced search, just use this filter as is
szFilter = lpAdvFilter;
szNTFilter = lpAdvFilter;
szSimpleFilter = lpAdvFilter;
}
else
{
// Convert the SRestriction into filters for ldap_search
if(lpres)
{
// Note Simple Search Filter is ignored in searchwithoutcontiner
hr = ParseSRestriction(lpres, &szFilter, &szSimpleFilter, &szNTFilter, FIRST_PASS, TRUE); //assumes UNICODE always
if (hrSuccess != hr)
goto exit;
}
}
// Load the client functions
if (! (fInitDLL = InitLDAPClientLib()))
{
hr = ResultFromScode(MAPI_E_UNCONFIGURED);
goto exit;
}
// Read the matching entries
ulResult = SearchWithCancel(&pLDAP,
(LPTSTR)lplu->lpszBase,
(lpres ? LDAP_SCOPE_SUBTREE : lplu->ulScope),
(LPTSTR)(lpres ? szFilter : lplu->lpszFilter),
(LPTSTR)(lpres ? szNTFilter : NULL),
(LPTSTR*)(lpres ? g_rgszOpenEntryAttrs/*g_rgszFindRowAttrs*/ : lplu->ppszAttrib),
0,
&lpResult,
(LPTSTR)lplu->lpszServer,
TRUE,
lplu->lpszBindName, lplu->dwAuthType,
FALSE, NULL, NULL, FALSE, &bIsNTDSEntry,
TRUE); //unicode by default ?
if (LDAP_SUCCESS != ulResult)
{
DebugTrace(TEXT("LDAPSearchWithoutContainer: ldap_search returned %d.\n"), ulResult);
hr = HRFromLDAPError(ulResult, pLDAP, 0);
// See if the result was the special value that tells us there were more
// entries than could be returned. If so, we need to check if we got
// some of the entries or none of the entries.
if ( (ResultFromScode(MAPI_E_UNABLE_TO_COMPLETE) == hr) &&
(ulcEntries = gpfnLDAPCountEntries(pLDAP, lpResult)) )
{
// We got some results back. Return MAPI_W_PARTIAL_COMPLETION
// instead of success.
hrDeferred = ResultFromScode(MAPI_W_PARTIAL_COMPLETION);
hr = hrSuccess;
}
else
goto exit;
}
else
{
// Count the entries.
ulcEntries = gpfnLDAPCountEntries(pLDAP, lpResult);
}
if (0 == ulcEntries)
{
// 96/08/08 markdu BUG 35481 No error and no results means "not found"
hr = ResultFromScode(MAPI_E_NOT_FOUND);
goto exit;
}
// get the first entry in the search result
lpEntry = gpfnLDAPFirstEntry(pLDAP, lpResult);
if (NULL == lpEntry)
{
DebugTrace(TEXT("LDAP_FindRow: No entry found for %s.\n"), szFilter);
hr = HRFromLDAPError(LDAP_ERROR, pLDAP, MAPI_E_CORRUPT_DATA);
if (hrSuccess == hr)
{
// No error occurred according to LDAP, which in theory means that there
// were no more entries. However, this should not happen, so return error.
hr = ResultFromScode(MAPI_E_CORRUPT_DATA);
}
goto exit;
}
while (lpEntry)
{
LPRECIPIENT_INFO lpItem = NULL;
hr = HrLDAPEntryToMAPIEntry( pLDAP, lpEntry,
(LPTSTR) lplu->lpszServer,
0, // standard number of attributes
bIsNTDSEntry,
&ulcProps,
&lpPropArray);
if (hrSuccess != hr)
continue;
// Get the next entry.
lpEntry = gpfnLDAPNextEntry(pLDAP, lpEntry);
if(!lpEntry &&
ulIndex == 0 &&
bReturnSinglePropArray &&
lppPropArray &&
lpulcProps)
{
// just return this propArray we created instead of wasting time on
// other things
*lppPropArray = lpPropArray;
*lpulcProps = ulcProps;
}
else
{
// multiple results or prop array not asked for ,
// return an lpItem list
//
lpItem = LocalAlloc(LMEM_ZEROINIT, sizeof(RECIPIENT_INFO));
if (!lpItem)
{
DebugPrintError(( TEXT("LocalAlloc Failed \n")));
hr = MAPI_E_NOT_ENOUGH_MEMORY;
goto exit;
}
GetRecipItemFromPropArray(ulcProps, lpPropArray, &lpItem);
lpItem->lpPrev = NULL;
if(*lppContentsList)
(*lppContentsList)->lpPrev = lpItem;
lpItem->lpNext = *lppContentsList;
*lppContentsList = lpItem;
MAPIFreeBuffer(lpPropArray);
lpPropArray = NULL;
ulcProps = 0;
}
ulIndex++;
}
// Free the search results memory
gpfnLDAPMsgFree(lpResult);
lpResult = NULL;
exit:
// free the search results memory
if (lpResult)
{
gpfnLDAPMsgFree(lpResult);
lpResult = NULL;
}
// close the connection
if (pLDAP)
{
gpfnLDAPUnbind(pLDAP);
pLDAP = NULL;
}
// Free the search filter memory
if(szFilter != lpAdvFilter)
LocalFreeAndNull(&szFilter);
if(szNTFilter != lpAdvFilter)
LocalFreeAndNull(&szNTFilter);
if(szSimpleFilter != lpAdvFilter)
LocalFreeAndNull(&szSimpleFilter);
LocalFreeAndNull(&szBase);
if (fInitDLL)
{
DeinitLDAPClientLib();
}
// Check if we had a deferred error to return instead of success.
if (hrSuccess == hr)
{
hr = hrDeferred;
}
if((HR_FAILED(hr)) && (MAPI_E_USER_CANCEL != hr) && (ulFlags & MAPI_DIALOG))
{
int ids;
UINT flags = MB_OK | MB_ICONEXCLAMATION;
switch(hr)
{
case MAPI_E_UNABLE_TO_COMPLETE:
case MAPI_E_AMBIGUOUS_RECIP:
ids = idsLDAPAmbiguousRecip;
break;
case MAPI_E_NOT_FOUND:
ids = idsLDAPSearchNoResults;
break;
case MAPI_E_NO_ACCESS:
ids = idsLDAPAccessDenied;
break;
case MAPI_E_TIMEOUT:
ids = idsLDAPSearchTimedOut;
break;
case MAPI_E_NETWORK_ERROR:
ids = idsLDAPCouldNotFindServer;
break;
default:
ids = idsLDAPErrorOccured;
break;
}
ShowMessageBoxParam(hWnd, ids, flags, ulResult ? gpfnLDAPErr2String(ulResult) : szEmpty);
}
else
{
if(hr == MAPI_W_PARTIAL_COMPLETION)
ShowMessageBox( hWnd, idsLDAPPartialResults, MB_OK | MB_ICONINFORMATION);
}
return hr;
}
//*******************************************************************
//
// FUNCTION: bIsSimpleSearch
//
// PURPOSE: Checks if this server has Simple Search set on it.
//
// PARAMETERS: lpszServer - name of LDAP server whose base string to get.
//
// RETURNS: BOOL
//
//*******************************************************************
BOOL bIsSimpleSearch(LPTSTR lpszServer)
{
LDAPSERVERPARAMS Params;
BOOL fRet;
GetLDAPServerParams((LPTSTR)lpszServer, &Params);
fRet = Params.fSimpleSearch;
FreeLDAPServerParams(Params);
return fRet;
}
#ifdef PAGED_RESULT_SUPPORT
/*
-
- dwGetPagedResultSupport
*/
DWORD dwGetPagedResultSupport(LPTSTR lpszServer)
{
LDAPSERVERPARAMS Params;
DWORD dwRet;
GetLDAPServerParams((LPTSTR)lpszServer, &Params);
dwRet = Params.dwPagedResult;
FreeLDAPServerParams(Params);
return dwRet;
}
/*
-
- SetPagedResultSupport
*/
void SetPagedResultSupport(LPTSTR lpszServer, BOOL bSupportsPagedResults)
{
LDAPSERVERPARAMS Params;
DWORD dwRet;
if(GetLDAPServerParams(lpszServer, &Params))
{
Params.dwPagedResult = bSupportsPagedResults ? LDAP_PRESULT_SUPPORTED : LDAP_PRESULT_NOTSUPPORTED;
SetLDAPServerParams(lpszServer, &Params);
}
FreeLDAPServerParams(Params);
}
#endif //#ifdef PAGED_RESULT_SUPPORT
/*
-
- dwGetNTDS - checks if this is an NTDS or not
*/
DWORD dwGetNTDS(LPTSTR lpszServer)
{
LDAPSERVERPARAMS Params;
DWORD dwRet;
GetLDAPServerParams((LPTSTR)lpszServer, &Params);
dwRet = Params.dwIsNTDS;
FreeLDAPServerParams(Params);
return dwRet;
}
/*
-
- SetNTDS
*/
void SetNTDS(LPTSTR lpszServer, BOOL bIsNTDS)
{
LDAPSERVERPARAMS Params;
DWORD dwRet;
if(GetLDAPServerParams(lpszServer, &Params))
{
Params.dwIsNTDS = bIsNTDS ? LDAP_NTDS_IS : LDAP_NTDS_ISNOT;
SetLDAPServerParams(lpszServer, &Params);
}
FreeLDAPServerParams(Params);
}
//*******************************************************************
//
// FUNCTION: DoSyncLDAPSearch
//
// PURPOSE: Does a synchronous LDAP search (this means no cancel dlg)
//
// PARAMETERS: .
//
// RETURNS: BOOL
//
//*******************************************************************
BOOL DoSyncLDAPSearch(PLDAPSEARCHPARAMS pLDAPSearchParams)
{
BOOL fRet = FALSE;
DebugTrace(TEXT("Doing Synchronous LDAP Search\n"));
if(bIsSimpleSearch(pLDAPSearchParams->lpszServer))
pLDAPSearchParams->ulFlags |= LSP_SimpleSearch;
pLDAPSearchParams->ulFlags |= LSP_UseSynchronousBind;
pLDAPSearchParams->ulFlags |= LSP_UseSynchronousSearch;
// Perform the bind operation.
Assert(pLDAPSearchParams->lpszServer);
{
BOOL fUseSynchronousBind = (pLDAPSearchParams->ulFlags & LSP_UseSynchronousBind);
pLDAPSearchParams->ulLDAPValue = use_ldap_v3;
pLDAPSearchParams->ulError = OpenConnection(pLDAPSearchParams->lpszServer,
pLDAPSearchParams->ppLDAP,
&pLDAPSearchParams->ulTimeout,
&pLDAPSearchParams->ulMsgID,
&fUseSynchronousBind,
pLDAPSearchParams->ulLDAPValue,
pLDAPSearchParams->lpszBindDN,
pLDAPSearchParams->dwAuthType);
if(fUseSynchronousBind)
pLDAPSearchParams->ulFlags |= LSP_UseSynchronousBind;
else
pLDAPSearchParams->ulFlags &= ~LSP_UseSynchronousBind;
}
if (LDAP_SUCCESS != pLDAPSearchParams->ulError)
goto out;
// The actions that need to be performed after the bind are done
// in BindProcessResults, so we call this even though there really are
// no results to process in the synchronous case.
if(!BindProcessResults(pLDAPSearchParams, NULL, NULL))
goto out;
// Process the results
if (pLDAPSearchParams->ulFlags & LSP_ResolveMultiple)
while (ResolveProcessResults(pLDAPSearchParams, NULL));
else if(LDAP_ERROR == pLDAPSearchParams->ulResult)
{
pLDAPSearchParams->ulError = (*pLDAPSearchParams->ppLDAP)->ld_errno;
goto out;
}
fRet = TRUE;
out:
return fRet;
}
/*
- CreateLDAPURLFromEntryID
-
* Takes an EntryID, checks if it is an LDAP EntryID and creates an LDAP URL from it ..
* Allocates and returns the URL .. caller responsible for freeing ..
*/
void CreateLDAPURLFromEntryID(ULONG cbEntryID, LPENTRYID lpEntryID, LPTSTR * lppBuf, BOOL * lpbIsNTDSEntry)
{
LPTSTR lpServerName = NULL, lpDN = NULL;
LPTSTR lpURL = NULL, lpURL1 = NULL;
DWORD dwURL = 0;
LDAPSERVERPARAMS Params = {0};
LPTSTR lpServer = NULL;
ULONG ulcNumProps = 0;
if(!lppBuf)
return;
if (WAB_LDAP_MAILUSER != IsWABEntryID( cbEntryID, lpEntryID, NULL, NULL, NULL, NULL, NULL) )
return;
// Deconstruct the entryid into server name and DN
IsWABEntryID( cbEntryID, lpEntryID, &lpServerName, &lpDN, NULL, (LPVOID *) &ulcNumProps, NULL);
if(lpbIsNTDSEntry)
*lpbIsNTDSEntry = (ulcNumProps & LDAP_NTDS_ENTRY) ? TRUE : FALSE;
if(lpServerName)
{
GetLDAPServerParams(lpServerName, &Params);
if(!Params.lpszName)
{
ULONG cchSize = lstrlen(lpServerName)+1;
if(Params.lpszName = LocalAlloc(LMEM_ZEROINIT, sizeof(TCHAR)*cchSize))
StrCpyN(Params.lpszName, lpServerName, cchSize);
}
}
// [PaulHi] 12/9/98 Raid NT5 - #26069
// The account manager provides the server name "NULL" for ActiveDirectory servers
// We need to make sure this name doesn't become part of the LDAP URL
lpServer = Params.lpszName ? Params.lpszName : szEmpty;
if ( !lstrcmpi(lpServer, szNULLString) )
lpServer = szEmpty;
if(!lpDN)
lpDN = szEmpty;
// [PaulHi] 3/24/99 InternetCanonicalizeUrlW doesn't take buffer count in bytes
// as stated in documentation, but in characters. Also doesn't account for NULL
// terminating character.
dwURL = 3*(lstrlen(lpServer)+lstrlen(lpDN)+10); //worst case - every character needs to be encoded ...
lpURL = LocalAlloc(LMEM_FIXED, sizeof(WCHAR)*(dwURL+1)); //10 is enuff space for 'ldap://' and terminating NULL
if(lpURL)
{
lpURL1 = LocalAlloc(LMEM_FIXED, sizeof(WCHAR)*(dwURL+1)); //10 is enuff space for 'ldap://' and terminating NULL
if(lpURL1)
{
DNtoLDAPURL(lpServer, lpDN, lpURL, dwURL+1);
DebugTrace(TEXT("==> pre-encoded: %s\n"),lpURL);
if ( !InternetCanonicalizeUrlW(lpURL, lpURL1, &dwURL, 0) )
{
DebugTrace(TEXT("ERROR: CreateLDAPURLFromEntryID, InternetCanonicalizeUrlW failed.\n"));
Assert(0);
}
DebugTrace(TEXT("==>post-encoded: %s\n"),lpURL1);
FreeLDAPServerParams(Params);
*lppBuf = lpURL1;
}
LocalFree(lpURL);
}
}
/*
-
- CheckErrorResults - Check for error results here
*
* ulExpectedResult - ldap_result returns the type of the result retrieved but not the
* the actual error code itself. If the expected Result is the same as the last ulResult,
* only then do we call ldap_result2error to get the actual error code ..
*/
ULONG CheckErrorResult(PLDAPSEARCHPARAMS pLDAPSearchParams, ULONG ulExpectedResult)
{
ULONG ulResult = 0;
// Upon successful completion, ldap_result returns the type of the result
// returned in the res parameter. If it is not the type we expect, treat
// this as an error.
if(ulExpectedResult == pLDAPSearchParams->ulResult)
{
// If an error occurred in ldap_result, return the error code
if (LDAP_ERROR == pLDAPSearchParams->ulResult)
ulResult = (*pLDAPSearchParams->ppLDAP)->ld_errno;
else if (NULL != *pLDAPSearchParams->lplpResult) // Check the result for errors
{
ulResult = gpfnLDAPResult2Error(*pLDAPSearchParams->ppLDAP,*pLDAPSearchParams->lplpResult,FALSE);
}
}
else
if( (LDAP_RES_BIND == pLDAPSearchParams->ulResult) ||
(LDAP_RES_SEARCH_RESULT == pLDAPSearchParams->ulResult) ||
(LDAP_RES_SEARCH_ENTRY == pLDAPSearchParams->ulResult) ||
(LDAP_RES_MODIFY == pLDAPSearchParams->ulResult) ||
(LDAP_RES_ADD == pLDAPSearchParams->ulResult) ||
(LDAP_RES_DELETE == pLDAPSearchParams->ulResult) ||
(LDAP_RES_MODRDN == pLDAPSearchParams->ulResult) ||
(LDAP_RES_COMPARE == pLDAPSearchParams->ulResult) ||
(LDAP_RES_SESSION == pLDAPSearchParams->ulResult) ||
//(LDAP_RES_REFERRAL == pLDAPSearchParams->ulResult)
(LDAP_RES_EXTENDED == pLDAPSearchParams->ulResult))
{
ulResult = LDAP_LOCAL_ERROR;
}
else
ulResult = pLDAPSearchParams->ulResult;
DebugTrace(TEXT("CheckErrorResult: 0x%.2x, %s\n"), ulResult, gpfnLDAPErr2String(ulResult));
return ulResult;
}
/*
- bSearchForOID
-
* Performs a sync search on the server looking for a specific OID in a
* specific attribute ..
*/
BOOL bSearchForOID(PLDAPSEARCHPARAMS pLDAPSearchParams, LPTSTR lpAttr, LPTSTR szOID)
{
LDAPMessage * pMsg = NULL;
LDAPMessage * pMsgCur = NULL;
BOOL bFound = FALSE;
DWORD dwRet = 0;
int nErr = 0;
LPTSTR AttrList[] = {lpAttr, NULL};
DebugTrace(TEXT(">>>Looking for %s in attribute %s\n"), szOID, lpAttr);
if(LDAP_SUCCESS != (nErr = gpfnLDAPSearchS( *pLDAPSearchParams->ppLDAP,
NULL,
LDAP_SCOPE_BASE,
(LPTSTR) cszAllEntriesFilter,
AttrList, 0, &pMsg)))
{
DebugTrace(TEXT("Synchronous OID determination failed: 0x%.2x, %s\n"), nErr, gpfnLDAPErr2String(nErr));
goto out;
}
pMsgCur = gpfnLDAPFirstEntry(*pLDAPSearchParams->ppLDAP, pMsg);
if(!pMsg || !pMsgCur)
goto out;
while( NULL != pMsgCur )
{
BerElement* pBerElement;
TCHAR* attr = gpfnLDAPFirstAttr(*pLDAPSearchParams->ppLDAP, pMsg, &pBerElement );
while( attr != NULL )
{
if( !lstrcmpi( attr, lpAttr ) )
{
TCHAR** pptch = gpfnLDAPGetValues(*pLDAPSearchParams->ppLDAP, pMsgCur, attr );
int i;
for(i = 0; NULL != pptch[i]; i++ )
{
if( !lstrcmpi( pptch[i], szOID ) )
{
DebugTrace(TEXT("Found %s [OID:%s]\n"),pLDAPSearchParams->lpszServer, pptch[i]);
bFound = TRUE;
goto out;
}
}
}
attr = gpfnLDAPNextAttr(*pLDAPSearchParams->ppLDAP, pMsgCur, pBerElement );
}
pMsgCur = gpfnLDAPNextEntry(*pLDAPSearchParams->ppLDAP, pMsgCur );
}
out:
if( NULL != pMsg )
gpfnLDAPMsgFree( pMsg );
return bFound;
}
/*
- bIsNTDS
-
* Checks to see if the server is an NTDS or not - if there is no existing info in the registry,
* then we do a one-time check and write the results into the registry
*
*/
#define LDAP_NTDS_DISCOVERY_OID_STRING TEXT("1.2.840.113556.1.4.800")
BOOL bCheckIfNTDS(PLDAPSEARCHPARAMS pLDAPSearchParams)
{
LDAPMessage * pMsg = NULL;
LDAPMessage * pMsgCur = NULL;
BOOL bFound = FALSE;
DWORD dwRet = 0;
int nErr = 0;
LPPTGDATA lpPTGData=GetThreadStoragePointer();
if(pLDAPSearchParams->ulFlags & LSP_IsNTDS)
return TRUE;
if(pLDAPSearchParams->ulFlags & LSP_IsNotNTDS)
return FALSE;
dwRet = dwGetNTDS(pLDAPSearchParams->lpszServer);
if(dwRet == LDAP_NTDS_IS)
{
pLDAPSearchParams->ulFlags |= LSP_IsNTDS;
return TRUE;
}
else if(dwRet == LDAP_NTDS_ISNOT)
{
pLDAPSearchParams->ulFlags |= LSP_IsNotNTDS;
return FALSE;
}
if(SubstringSearch(pLDAPSearchParams->lpszServer, TEXT("mich")))
{
LDAPSERVERPARAMS Params = {0};
BOOL bIsUmich = FALSE;
GetLDAPServerParams(pLDAPSearchParams->lpszServer, &Params);
if(Params.lpszName && SubstringSearch(Params.lpszName, TEXT("umich.edu")))
bIsUmich = TRUE;
else
if(SubstringSearch(pLDAPSearchParams->lpszServer, TEXT("umich.edu")))
bIsUmich = TRUE;
FreeLDAPServerParams(Params);
if(bIsUmich)
goto out; // The search below hangs on umich so just skip it
}
bFound = bSearchForOID(pLDAPSearchParams, TEXT("supportedCapabilities"), LDAP_NTDS_DISCOVERY_OID_STRING);
out:
pLDAPSearchParams->ulFlags |= (bFound ? LSP_IsNTDS : LSP_IsNotNTDS);
// Any LDAP search failure will mean we won't look for this ever again
SetNTDS(pLDAPSearchParams->lpszServer, bFound);
DebugTrace(TEXT(">>>%s %s a NT Directory Service \n"), pLDAPSearchParams->lpszServer, bFound? TEXT("is"): TEXT("is not"));
return bFound;
}
#ifdef PAGED_RESULT_SUPPORT
/*
- bSupportsLDAPPagedResults
-
* Checks to see if the server supports LDAP paged results or not ...
*
*/
BOOL bSupportsLDAPPagedResults(PLDAPSEARCHPARAMS pLDAPSearchParams)
{
LDAPMessage * pMsg = NULL;
LDAPMessage * pMsgCur = NULL;
BOOL bFound = FALSE;
DWORD dwRet = 0;
int nErr = 0;
LPPTGDATA lpPTGData=GetThreadStoragePointer();
/*--------------------------------------------------------------
*
* NOTE: There seems to be an Exchange server problem that if we send
* the filter (|(cn=XXX*)(mail=XXX*)) AND the number of matches on the
* server exceed the number of matches requested per page, the
* search times out. This doesn't happen for other filters so it's
* quirky and it's unacceptable.
*
* Therefore the paged result feature is being disabled. To re-enable it,
* comment out the line below
*---------------------------------------------------------------*/
return FALSE;
// don't support paged results for non-Outlook
//if(!pt_bIsWABOpenExSession)
// return FALSE;
if(pLDAPSearchParams->ulFlags & LSP_ResolveMultiple) //dont use paged results for name resolution
return FALSE;
if(pLDAPSearchParams->ulFlags & LSP_PagedResults)
return TRUE;
if(pLDAPSearchParams->ulFlags & LSP_NoPagedResults)
return FALSE;
dwRet = dwGetPagedResultSupport(pLDAPSearchParams->lpszServer);
if(dwRet == LDAP_PRESULT_SUPPORTED)
{
pLDAPSearchParams->ulFlags |= LSP_PagedResults;
return TRUE;
}
else if(dwRet == LDAP_PRESULT_NOTSUPPORTED)
{
pLDAPSearchParams->ulFlags |= LSP_NoPagedResults;
return FALSE;
}
if(SubstringSearch(pLDAPSearchParams->lpszServer, TEXT("mich")))
{
LDAPSERVERPARAMS Params = {0};
BOOL bIsUmich = FALSE;
GetLDAPServerParams(pLDAPSearchParams->lpszServer, &Params);
if(Params.lpszName && SubstringSearch(Params.lpszName, TEXT("umich.edu")))
bIsUmich = TRUE;
else
if(SubstringSearch(pLDAPSearchParams->lpszServer, TEXT("umich.edu")))
bIsUmich = TRUE;
FreeLDAPServerParams(Params);
if(bIsUmich)
goto out; // The search below hangs on umich so just skip it
}
bFound = bSearchForOID(pLDAPSearchParams, TEXT("supportedControl"), LDAP_PAGED_RESULT_OID_STRING);
out:
pLDAPSearchParams->ulFlags |= (bFound ? LSP_PagedResults : LSP_NoPagedResults);
// persist the fact that the paged-result search succeeded or failed so we don't
// try to do this every single time
// Any LDAP search failure will mean we won't look for this ever again
SetPagedResultSupport(pLDAPSearchParams->lpszServer, bFound);
DebugTrace(TEXT("<<<Paged Result support = %d\n"), bFound);
return bFound;
}
/*
- bMorePagedResultsAvailable
-
* Checks if more paged results are available
*
*/
BOOL bMorePagedResultsAvailable()
{
LPPTGDATA lpPTGData=GetThreadStoragePointer();
return (pt_pCookie != NULL);
}
/*
- CachePagedResultParams
-
* Temporarily stores the PagedResult Params for
* future paged results
*
*/
void CachePagedResultParams(PLDAPSEARCHPARAMS pLDAPSearchParams)
{
LPPTGDATA lpPTGData=GetThreadStoragePointer();
pt_pCookie = (pLDAPSearchParams->pCookie && pLDAPSearchParams->pCookie->bv_len) ?
pLDAPSearchParams->pCookie : NULL;
}
/*
- ClearCachedPagedResultParams
-
*/
void ClearCachedPagedResultParams()
{
LPPTGDATA lpPTGData=GetThreadStoragePointer();
pt_pCookie = NULL;
}
/*
- GetPagedResultParams
-
*
*/
void GetPagedResultParams(PLDAPSEARCHPARAMS pLDAPSearchParams)
{
LPPTGDATA lpPTGData=GetThreadStoragePointer();
pLDAPSearchParams->pCookie = pt_pCookie;
}
/*
- InitLDAPPagedSearch
-
* Initializes and starts a Paged Result search
*
*
*/
void InitLDAPPagedSearch(BOOL fSynchronous, PLDAPSEARCHPARAMS pLDAPSearchParams, LPTSTR lpFilter)
{
PLDAPControlA PagedControl[2];
LDAPSERVERPARAMS Params = {0};
LPTSTR szFilterT, szFilter = NULL;
// initialize search control parameters
GetLDAPServerParams(pLDAPSearchParams->lpszServer, &Params);
DebugTrace(TEXT("---Initiating paged result search...\n"));
GetPagedResultParams(pLDAPSearchParams);
gpfnLDAPCreatePageControl( *pLDAPSearchParams->ppLDAP,
Params.dwSearchSizeLimit,
pLDAPSearchParams->pCookie,
FALSE,
&(PagedControl[0]));
PagedControl[1] = NULL;
if (lpFilter)
szFilterT = lpFilter;
else
{
if ((pLDAPSearchParams->ulFlags & LSP_IsNTDS) && pLDAPSearchParams->szNTFilter)
szFilterT = pLDAPSearchParams->szNTFilter;
else
szFilterT = pLDAPSearchParams->szFilter;
}
Assert(szFilterT);
if (pLDAPSearchParams->ulFlags & LSP_IsNTDS)
{
// [PaulHi] 4/20/99 Raid 73205 Allow NTDS group searches.
LPTSTR tszFilterPerson = NULL;
LPTSTR tszFilterGroup = NULL;
BOOL bFilterSucceeded = FALSE;
// Put together person side
if (BuildOpFilter(&tszFilterPerson, szFilterT, (LPTSTR)cszAllPersonFilter, FILTER_OP_AND) == hrSuccess)
{
// Put together group side
if (BuildOpFilter(&tszFilterGroup, szFilterT, (LPTSTR)cszAllGroupFilter, FILTER_OP_AND) == hrSuccess)
{
// Put two together
bFilterSucceeded = (BuildOpFilter(&szFilter, tszFilterPerson, tszFilterGroup, FILTER_OP_OR) == hrSuccess);
LocalFreeAndNull(&tszFilterGroup);
}
LocalFreeAndNull(&tszFilterPerson);
}
if (!bFilterSucceeded)
goto out;
}
else
szFilter = szFilterT;
if(fSynchronous)
{
struct l_timeval Timeout;
// Poll the server for results
ZeroMemory(&Timeout, sizeof(struct l_timeval));
Timeout.tv_sec = Params.dwSearchTimeLimit;
Timeout.tv_usec = 0;
pLDAPSearchParams->ulError = gpfnLDAPSearchExtS( *pLDAPSearchParams->ppLDAP,
pLDAPSearchParams->szBase,
pLDAPSearchParams->ulScope,
szFilter,
pLDAPSearchParams->ppszAttrs,
0,
PagedControl,
NULL,
&Timeout,
0, // 0 means no limit
pLDAPSearchParams->lplpResult);
}
else
{
pLDAPSearchParams->ulError = gpfnLDAPSearchExt( *pLDAPSearchParams->ppLDAP,
pLDAPSearchParams->szBase,
pLDAPSearchParams->ulScope,
szFilter,
pLDAPSearchParams->ppszAttrs,
0,
PagedControl,
NULL,
Params.dwSearchTimeLimit, //timeout
0, // 0 means no limit
&(pLDAPSearchParams->ulMsgID));
}
out:
gpfnLDAPControlFree(PagedControl[0]);
FreeLDAPServerParams(Params);
if (szFilter != szFilterT)
LocalFreeAndNull(&szFilter);
}
/*
- ProcessLDAPPagedResultCookie
-
* Processes the cookie returned from one paged result search
*
*/
BOOL ProcessLDAPPagedResultCookie(PLDAPSEARCHPARAMS pLDAPSearchParams)
{
BOOL fRet = FALSE;
PLDAPControl *serverReturnedControls = NULL;
LDAPMessage* lpEntry;
ULONG ulCount = 0;
DWORD dwTotal = 0;
pLDAPSearchParams->ulError = gpfnLDAPParseResult(*pLDAPSearchParams->ppLDAP,
*pLDAPSearchParams->lplpResult,
NULL, NULL, NULL, NULL,
&serverReturnedControls, FALSE);
if (LDAP_SUCCESS != pLDAPSearchParams->ulError)
goto out;
pLDAPSearchParams->ulError = gpfnLDAPParsePageControl( *pLDAPSearchParams->ppLDAP,
serverReturnedControls,
&dwTotal, &(pLDAPSearchParams->pCookie));
if (LDAP_SUCCESS != pLDAPSearchParams->ulError)
goto out;
CachePagedResultParams(pLDAPSearchParams);
fRet = TRUE;
out:
if(serverReturnedControls)
gpfnLDAPControlsFree(serverReturnedControls);
return fRet;
}
#endif //#ifdef PAGED_RESULT_SUPPORT