You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
8807 lines
297 KiB
8807 lines
297 KiB
//*******************************************************************
|
|
//
|
|
// 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
|