/* =============================================================================== Module - edk.cpp System - EnterpriseAdministrator Creator - Steven Bailey Created - 2 Apr 97 Description - Exchange MAPI helper routines. Updates - Christy Boles - added more MAPI helper functions, and some DAPI helper functions. =============================================================================== */ #ifdef USE_STDAFX # include "stdafx.h" #else # include #endif #include #include "edk2.hpp" #include #include #include #include #include <_ENTRYID.H> extern LPMAPIALLOCATEBUFFER pMAPIAllocateBuffer; extern LPMAPIFREEBUFFER pMAPIFreeBuffer; extern LPMAPIINITIALIZE pMAPIInitialize; extern LPMAPIUNINITIALIZE pMAPIUninitialize; extern LPMAPILOGONEX pMAPILogonEx; extern LPMAPIADMINPROFILES pMAPIAdminProfiles; extern LPFREEPADRLIST pFreePadrlist; extern LPFREEPROWS pFreeProws; extern LPSCDUPPROPSET pScDupPropset; extern LPHRQUERYALLROWS pHrQueryAllRows; extern LPULRELEASE pUlRelease; #define CHAR WCHAR #define HR_LOG(x) x // definition of the Exchange address type. // from edk.h #define EXCHANGE_ADDRTYPE L"EX" #define CbNewFlagList(_cflag) \ (offsetof(FlagList,ulFlag) + (_cflag)*sizeof(ULONG)) //$--cbStrLen@------------------------------------------------ // Returns total number of bytes (including NULL) used by // a string. Useful for string allocations... // ----------------------------------------------------------- #define cbStrLenA(sz) ((lstrlenA((sz)) + 1) * sizeof(CHAR)) #if defined(_M_IX86) #define cbStrLenW(sz) ((lstrlenW((sz)) + 1) * sizeof(WCHAR)) #else // lstrlenW can return 0 for UNALIGNED UNICODE strings on non-IX86 platforms __inline static size_t cbStrLenW( IN UNALIGNED const WCHAR *wsz) { size_t cbWsz = 0; for(; *wsz; wsz++) cbWsz += sizeof( WCHAR); return( cbWsz + sizeof( WCHAR)); } #endif #ifdef UNICODE #define cbStrLen cbStrLenW #else #define cbStrLen cbStrLenA #endif #define ULRELEASE(x) \ { \ (*pUlRelease)((x)); \ (x) = NULL; \ } #define MAPIFREEBUFFER(x) \ { \ (*pMAPIFreeBuffer)((x)); \ (x) = NULL; \ } #define FREEPADRLIST(x) \ { \ (*pFreePadrlist)((x)); \ (x) = NULL; \ } #define FREEPROWS(x) \ { \ (*pFreeProws)((x)); \ (x) = NULL; \ } // Definitions from ExchInst.c #define MAX_CSV_LINE_SIZ 2048 #define MAX_WORD 0xFFFF #define FILE_PREFIX L"EXCH" #define NEW_LINE L"\r\n" #define EXCHINST_DELIM L'\t' #define EXCHINST_QUOTE L'"' #define EXCHINST_MV_SEP L'%' #define SZ_EXCHINST_DELIM L"\t" #define SZ_EXCHINST_QUOTE L"\"" #define SZ_EXCHINST_MV_SEP L"%" #define BEGIN_CSV_LINE(a,b) lstrcpy(a, b) #define APPEND_CSV_LINE(a,b) \ { \ lstrcat(a, SZ_EXCHINST_DELIM); \ lstrcat(a, b); \ } #define DELETEFILE(_file) \ { \ if((_file) != NULL && (_file)[0] != 0) \ { \ if(! DeleteFile ((_file))) \ { \ HRESULT _hr = HR_LOG(E_FAIL); \ } \ } \ (_file)[0] = 0; \ } #define CLOSEHANDLE(h) \ { \ if(((h) != NULL) && ((h) != INVALID_HANDLE_VALUE)) \ { \ if(CloseHandle((h)) == FALSE) \ { \ /* HRESULT _hr = HR_LOG(E_FAIL); \ */ HR_LOG(E_FAIL); \ } \ (h) = NULL; \ } \ } // // Attribute Defines // #define OBJ_CLASS L"Obj-Class" #define MODE L"Mode" #define ADDR_SYNTAX L"Address-Syntax" #define ADDR_ENTRY_DT L"Address-Entry-Display-Table" #define ADDR_TYPE L"Address-Type" #define ADMIN_DISPLAY_NAME L"Admin-Display-Name" #define DISPLAY_NAME L"Display-Name" #define COMMON_NAME L"Common-Name" #define DELIVERY_MECHANISM L"Delivery-Mechanism" #define DELIV_EXT_CONT_TYPES L"Deliv-Ext-Cont-Types" #define EXTENSION_DATA L"Extension-Data" #define EXTENSION_NAME L"Extension-Name" #define HELP_FILE_NAME L"Help-File-Name" #define COMPUTER_NAME L"Computer-Name" #define GATEWAY_PROXY L"Gateway-Proxy" #define HOME_SERVER L"Home-Server" #define FILE_VERSION L"File-Version" #define PER_MSG_DDT L"Per-Msg-Dialog-Display-Table" #define PER_RECIP_DDT L"Per-Recip-Dialog-Display-Table" #define PROXY_GENERATOR_DLL L"Proxy-Generator-DLL" #define ROUTING_LIST L"Routing-List" #define OBJ_DIST_NAME L"Obj-Dist-Name" #define ORGANIZATION L"Organization" #define ORGANIZATIONAL_UNIT L"Organizational-Unit" #define CONTAINER L"Container" #define HELP_DATA16 L"Help-Data16" #define HELP_DATA32 L"Help-Data32" #define OBJ_ADMIN L"Obj-Admins" #define SITE_ADDRESSING L"Site-Addressing" #define ADMIN_EXTENSION_DLL L"Admin-Extension-Dll" #define CAN_PRESERVE_DNS L"Can-Preserve-DNs" #define HEURISTICS L"Heuristics" #define CONTAINER_INFO L"Container-Info" // // Attribute Value Defines // #define OBJ_CLASS_GW L"Mail-Gateway" #define OBJ_CLASS_MB L"Mailbox-Agent" #define OBJ_CLASS_SITE L"Site-Addressing" #define OBJ_CLASS_ADDR_TYPE L"Addr-Type" #define OBJ_CLASS_ADDR_TEMPLATE L"Address-Template" #define OBJ_CLASS_ADMIN_EXTENSION L"Admin-Extension" #define OBJ_CLASS_COMPUTER L"Computer" #define OBJ_CLASS_CONTAINER L"Container" // // Container Information Defines // #define ADDRESS_TEMPLATE_CONTAINER_INFO L"256" // // Import Mode Defines // #define MODE_CREATE L"Create" #define MODE_MODIFY L"Modify" #define MODE_DELETE L"Delete" #define DELIVERY_MECHANISM_GW L"2" #define DELIVERY_MECHANISM_MB L"0" #define CONTAINER_CONFIGURATION L"/cn=Configuration" #define CONTAINER_GW L"/cn=Configuration/cn=Connections" #define CONTAINER_ADDR_TYPE L"/cn=Configuration/cn=Addressing/cn=Address-Types" #define CONTAINER_ADDR_TEMPLATE L"/cn=Configuration/cn=Addressing/cn=Address-Templates" #define CONTAINER_SERVERS L"/cn=Configuration/cn=Servers" #define CONTAINER_SITE_ADDR L"/cn=Configuration/cn=Site-Addressing" #define CONTAINER_ADD_INS L"/cn=Configuration/cn=Add-Ins" #define ACCOUNT_NAME L"Obj-Users" // // Common macros. // #define CREATEKEY(hkParent, pszName, hkOut, dwDisposition) \ RegCreateKeyEx(hkParent, pszName, 0, "", REG_OPTION_NON_VOLATILE, \ KEY_ALL_ACCESS, NULL, &hkOut, &dwDisposition) #define SETSZVALUE(hk, pszName, pszValue) \ RegSetValueEx(hk, pszName, 0, REG_SZ, pszValue, lstrlen(pszValue)+1) #define SETMULTISZVALUE(hk, pszName, pszValue) \ RegSetValueEx(hk, pszName, 0, REG_MULTI_SZ, pszValue, \ CbMultiSz(pszValue)+sizeof(CHAR)) #define FREEHKEY(hk) \ if(hk != INVALID_HANDLE_VALUE) \ RegCloseKey(hk); static CHAR szExport[] = L"Export"; static CHAR szNull[] = L"(null)"; static CHAR szNullDisplayName[] = L"No Display Name!"; #define GLOBALFREE(x) { if((x) != NULL) {GlobalFree((void *)(x)); (x) = NULL;} } //--HrMAPICreateSizedAddressList------------------------------------------------ // Create a sized address list. // ----------------------------------------------------------------------------- HRESULT HrMAPICreateSizedAddressList( ULONG cEntries, // in - count of entries in address list LPADRLIST * lppAdrList) // out- pointer to address list pointer { HRESULT hr = NOERROR; SCODE sc = 0; ULONG cBytes = 0; *lppAdrList = NULL; cBytes = CbNewADRLIST(cEntries); sc = (*pMAPIAllocateBuffer)(cBytes, (void **)lppAdrList); if(FAILED(sc)) { hr = HR_LOG(E_OUTOFMEMORY); goto cleanup; } // Initialize ADRLIST structure ZeroMemory(*lppAdrList, cBytes); (*lppAdrList)->cEntries = cEntries; cleanup: return hr; } //--HrMAPISetAddressList-------------------------------------------------------- // Set an address list. // ----------------------------------------------------------------------------- HRESULT HrMAPISetAddressList( ULONG iEntry, // in - index of address list entry ULONG cProps, // in - count of values in address list // entry LPSPropValue lpPropValues, // in - pointer to address list entry LPADRLIST lpAdrList) // i/o - pointer to address list pointer { HRESULT hr = NOERROR; SCODE sc = 0; LPSPropValue lpNewPropValues = NULL; // ULONG cBytes = 0; if(iEntry >= lpAdrList->cEntries) { hr = HR_LOG(E_FAIL); goto cleanup; } sc = (*pScDupPropset)( cProps, lpPropValues, (*pMAPIAllocateBuffer), &lpNewPropValues); if(FAILED(sc)) { hr = HR_LOG(E_FAIL); goto cleanup; } if(lpAdrList->aEntries[iEntry].rgPropVals != NULL) { MAPIFREEBUFFER(lpAdrList->aEntries[iEntry].rgPropVals); } lpAdrList->aEntries[iEntry].cValues = cProps; lpAdrList->aEntries[iEntry].rgPropVals = lpNewPropValues; cleanup: return hr; } HRESULT HrGWResolveAddressW( LPABCONT lpGalABCont, // in - pointer to GAL container LPCWSTR lpszAddress, // in - pointer to proxy address BOOL * lpfMapiRecip, // out- MAPI recipient ULONG * lpcbEntryID, // out- count of bytes in entry ID LPENTRYID * lppEntryID) // out- pointer to entry ID { HRESULT hr = NOERROR; HRESULT hrT = 0; SCODE sc = 0; LPADRLIST lpAdrList = NULL; LPFlagList lpFlagList = NULL; SPropValue prop[2] = {0}; ULONG cbEntryID = 0; LPENTRYID lpEntryID = NULL; static const SizedSPropTagArray(2, rgPropTags) = { 2, { PR_ENTRYID, PR_SEND_RICH_INFO } }; *lpfMapiRecip = FALSE; *lpcbEntryID = 0; *lppEntryID = NULL; sc = (*pMAPIAllocateBuffer)( CbNewFlagList(1), (LPVOID*)&lpFlagList); if(FAILED(sc)) { hr = HR_LOG(E_OUTOFMEMORY); goto cleanup; } lpFlagList->cFlags = 1; lpFlagList->ulFlag[0] = MAPI_UNRESOLVED; hr = HrMAPICreateSizedAddressList(1, &lpAdrList); if(FAILED(hr)) { hr = HR_LOG(E_FAIL); goto cleanup; } prop[0].ulPropTag = PR_DISPLAY_NAME_W; prop[0].Value.lpszW = (LPWSTR)lpszAddress; prop[1].ulPropTag = PR_RECIPIENT_TYPE; prop[1].Value.ul = MAPI_TO; hr = HrMAPISetAddressList( 0, 2, prop, lpAdrList); if(FAILED(hr)) { hr = HR_LOG(E_FAIL); goto cleanup; } hrT = lpGalABCont->ResolveNames( (LPSPropTagArray)&rgPropTags, EMS_AB_ADDRESS_LOOKUP, lpAdrList, lpFlagList); if(lpFlagList->ulFlag[0] != MAPI_RESOLVED) { if(lpFlagList->ulFlag[0] == MAPI_AMBIGUOUS) { hrT = MAPI_E_AMBIGUOUS_RECIP; } else { hrT = MAPI_E_NOT_FOUND; } } if(FAILED(hrT)) { if(hrT == MAPI_E_NOT_FOUND) { hr = HR_LOG(EDK_E_NOT_FOUND); } else { hr = HR_LOG(E_FAIL); }; goto cleanup; } cbEntryID = lpAdrList->aEntries[0].rgPropVals[0].Value.bin.cb; lpEntryID = (LPENTRYID)lpAdrList->aEntries[0].rgPropVals[0].Value.bin.lpb; sc = (*pMAPIAllocateBuffer)( cbEntryID, (LPVOID*)lppEntryID); if(FAILED(sc)) { hr = HR_LOG(E_OUTOFMEMORY); goto cleanup; } CopyMemory(*lppEntryID, lpEntryID, cbEntryID); *lpcbEntryID = cbEntryID; *lpfMapiRecip = lpAdrList->aEntries[0].rgPropVals[1].Value.b; cleanup: MAPIFREEBUFFER(lpFlagList); FREEPADRLIST(lpAdrList); return hr; } //--HrFindExchangeGlobalAddressList------------------------------------------------- // Returns the entry ID of the global address list container in the address // book. // ----------------------------------------------------------------------------- // CAVEAT: If this function is successful, you must free the buffer returned in lppeid. HRESULT HrFindExchangeGlobalAddressList( LPADRBOOK lpAdrBook, // in - address book pointer ULONG * lpcbeid, // out- pointer to count of bytes in entry ID LPENTRYID * lppeid) // out- pointer to entry ID pointer { HRESULT hr = NOERROR; ULONG ulObjType = 0; // ULONG i = 0; LPMAPIPROP lpRootContainer = NULL; LPMAPIPROP lpContainer = NULL; LPMAPITABLE lpContainerTable = NULL; LPSRowSet lpRows = NULL; ULONG cbContainerEntryId = 0; LPENTRYID lpContainerEntryId = NULL; LPSPropValue lpCurrProp = NULL; SRestriction SRestrictAnd[2] = {0}; SRestriction SRestrictGAL = {0}; SPropValue SPropID = {0}; SPropValue SPropProvider = {0}; BYTE muid[] = MUIDEMSAB; SizedSPropTagArray(1, rgPropTags) = { 1, { PR_ENTRYID, } }; *lpcbeid = 0; *lppeid = NULL; // Open the root container of the address book hr = lpAdrBook->OpenEntry( 0, NULL, NULL, MAPI_DEFERRED_ERRORS, &ulObjType, (LPUNKNOWN FAR *)&lpRootContainer); if(FAILED(hr)) { goto cleanup; } if(ulObjType != MAPI_ABCONT) { hr = E_FAIL; goto cleanup; } // Get the hierarchy table of the root container hr = ((LPABCONT)lpRootContainer)->GetHierarchyTable( MAPI_DEFERRED_ERRORS|CONVENIENT_DEPTH, &lpContainerTable); if(FAILED(hr)) { goto cleanup; } // Restrict the table to the global address list (GAL) // --------------------------------------------------- // Initialize provider restriction to only Exchange providers SRestrictAnd[0].rt = RES_PROPERTY; SRestrictAnd[0].res.resProperty.relop = RELOP_EQ; SRestrictAnd[0].res.resProperty.ulPropTag = PR_AB_PROVIDER_ID; SPropProvider.ulPropTag = PR_AB_PROVIDER_ID; SPropProvider.Value.bin.cb = 16; SPropProvider.Value.bin.lpb = (LPBYTE)muid; SRestrictAnd[0].res.resProperty.lpProp = &SPropProvider; // Initialize container ID restriction to only GAL container SRestrictAnd[1].rt = RES_PROPERTY; SRestrictAnd[1].res.resProperty.relop = RELOP_EQ; SRestrictAnd[1].res.resProperty.ulPropTag = PR_EMS_AB_CONTAINERID; SPropID.ulPropTag = PR_EMS_AB_CONTAINERID; SPropID.Value.l = 0; SRestrictAnd[1].res.resProperty.lpProp = &SPropID; // Initialize AND restriction SRestrictGAL.rt = RES_AND; SRestrictGAL.res.resAnd.cRes = 2; SRestrictGAL.res.resAnd.lpRes = &SRestrictAnd[0]; // Restrict the table to the GAL - only a single row should remain // Get the row corresponding to the GAL // // Query all the rows // hr = (*pHrQueryAllRows)( lpContainerTable, (LPSPropTagArray)&rgPropTags, &SRestrictGAL, NULL, 0, &lpRows); if(FAILED(hr) || (lpRows == NULL) || (lpRows->cRows != 1)) { hr = E_FAIL; goto cleanup; } // Get the entry ID for the GAL lpCurrProp = &(lpRows->aRow[0].lpProps[0]); if(lpCurrProp->ulPropTag == PR_ENTRYID) { cbContainerEntryId = lpCurrProp->Value.bin.cb; lpContainerEntryId = (LPENTRYID)lpCurrProp->Value.bin.lpb; } else { hr = EDK_E_NOT_FOUND; goto cleanup; } hr = (*pMAPIAllocateBuffer)(cbContainerEntryId, (LPVOID *)lppeid); if(FAILED(hr)) { *lpcbeid = 0; *lppeid = NULL; } else { CopyMemory( *lppeid, lpContainerEntryId, cbContainerEntryId); *lpcbeid = cbContainerEntryId; } cleanup: if (lpRootContainer) lpRootContainer->Release(); if (lpContainerTable) lpContainerTable->Release(); if (lpContainer) lpContainer->Release(); (*pFreeProws)(lpRows); if(FAILED(hr)) { (*pMAPIFreeBuffer)(*lppeid); *lpcbeid = 0; *lppeid = NULL; } return hr; } //--HrSearchGAL---------------------------------------------------------------- // Look for the entry ID by searching for the DN in the global address list. // // RETURNS: hr == NOERROR Found // hr == EDK_E_NOT_FOUND DN Not Found // hr == (anything else) Other Error // ----------------------------------------------------------------------------- static HRESULT HrSearchGAL( LPADRBOOK lpAdrBook, // in - address book (directory) to look in LPWSTR lpszDN, // in - object distinguished name ULONG * lpcbEntryID, // out- count of bytes in entry ID LPENTRYID * lppEntryID) // out- pointer to entry ID { HRESULT hr = NOERROR; LPWSTR lpszAddress = NULL; ULONG cbGALEntryID = 0; LPENTRYID lpGALEntryID = NULL; ULONG ulGALObjectType = 0; LPABCONT lpGAL = NULL; BOOL fMapiRecip = FALSE; // Initially zero out the return variables. *lpcbEntryID = 0; *lppEntryID = NULL; // Create an address string consisting of "EX:" followed by the DN. hr = (*pMAPIAllocateBuffer)( wcslen(EXCHANGE_ADDRTYPE L":") + wcslen(lpszDN) + 1, (void **)&lpszAddress); if (FAILED(hr)) goto cleanup; wcscpy(lpszAddress, EXCHANGE_ADDRTYPE L":"); wcscat(lpszAddress, lpszDN); // Open the global address list. hr = HrFindExchangeGlobalAddressList( lpAdrBook, &cbGALEntryID, &lpGALEntryID); if (FAILED(hr)) goto cleanup; hr = lpAdrBook->OpenEntry( cbGALEntryID, lpGALEntryID, NULL, MAPI_BEST_ACCESS | MAPI_DEFERRED_ERRORS, &ulGALObjectType, (LPUNKNOWN *) &lpGAL); if (FAILED(hr)) goto cleanup; // Make sure it's the right object type. if (ulGALObjectType != MAPI_ABCONT) { //hr = HR_LOG(E_FAIL); goto cleanup; } // Resolve the address (returns EDK_E_NOT_FOUND if not found). hr = HrGWResolveAddressW( lpGAL, lpszAddress, &fMapiRecip, lpcbEntryID, lppEntryID); if (FAILED(hr)) goto cleanup; cleanup: ULRELEASE(lpGAL); MAPIFREEBUFFER(lpGALEntryID); MAPIFREEBUFFER(lpszAddress); return hr; } //$--HrCreateDirEntryIdEx------------------------------------------------------- // Create a directory entry ID given the address of the object // in the directory. // ----------------------------------------------------------------------------- HRESULT HrCreateDirEntryIdEx( // RETURNS: HRESULT IN LPADRBOOK lpAdrBook, // address book (directory) to look in IN LPWSTR lpszAddress, // object distinguished name OUT ULONG * lpcbEntryID, // count of bytes in entry ID OUT LPENTRYID * lppEntryID) // pointer to entry ID { HRESULT hr = NOERROR; // ULONG cbHierarchyEntryID = 0; // LPENTRYID lpHierarchyEntryID = NULL; ULONG ulEntryIDType = 0; // Initially zero out the return variables. *lpcbEntryID = 0; *lppEntryID = NULL; // Look for the DN in the global address list. hr = HrSearchGAL( lpAdrBook, lpszAddress, lpcbEntryID, lppEntryID); if (FAILED(hr)) goto cleanup; // If the type was DT_AGENT or DT_ORGANIZATION, then we have to // do a further lookup in the hierarchy table to determine the // DN's real type. ulEntryIDType = ((LPDIR_ENTRYID) *lppEntryID)->ulType; cleanup: return hr; } //$--CbMultiSz------------------------------------------------------------------ // Count of bytes in a REG_MULTI_SZ string (not including terminating NULL). // ----------------------------------------------------------------------------- static DWORD CbMultiSz( // RETURNS: count of bytes IN LPWSTR lpszRegMultiSz) // REG_MULTI_SZ string { // HRESULT hr = NOERROR; DWORD cch = 0; DWORD cb = 0; LPWSTR lpsz = NULL; if(lpszRegMultiSz == NULL) { goto cleanup; } lpsz = lpszRegMultiSz; while(*lpsz) { cch = lstrlenW(lpsz); cch++; cb += cch * sizeof(WCHAR); lpsz += cch; } cleanup: return(cb); } //$--HrGetRegistryValue--------------------------------------------------------- // Get a registry value - allocating memory to hold it. // ----------------------------------------------------------------------------- static HRESULT HrGetRegistryValue( // RETURNS: return code IN HKEY hk, // the key. IN LPWSTR lpszValue, // value name in key. OUT DWORD * lpType, // where to put type info. OUT DWORD * lpcb, // where to put byte count info. OUT LPVOID * lppData) // where to put the data. { HRESULT hr = E_FAIL; LONG lRet = 0; *lppData = NULL; // // Get its size // lRet = RegQueryValueEx( hk, lpszValue, NULL, lpType, NULL, lpcb); if(lRet != ERROR_SUCCESS) { hr = HR_LOG(HRESULT_FROM_WIN32(lRet)); goto cleanup; } // // Allocate memory for it // *lppData = (LPVOID)GlobalAlloc(GMEM_FIXED, *lpcb); if(*lppData == NULL) { hr = HR_LOG(HRESULT_FROM_WIN32(GetLastError())); goto cleanup; } // // Get the current value // lRet = RegQueryValueEx(hk, lpszValue, NULL, lpType, (UCHAR*)*lppData, lpcb); if(lRet != ERROR_SUCCESS) { hr = HR_LOG(HRESULT_FROM_WIN32(lRet)); goto cleanup; } hr = NOERROR; cleanup: if(FAILED(hr)) { if(lppData != NULL) { GLOBALFREE(*lppData); } } return hr; } //--FCsvGetField--------------------------------------------------------------- // Given a record and a field separator and a field number, this routine // will extract the field requested. //------------------------------------------------------------------------------ static BOOL FCsvGetField( // RETURNS: TRUE/FALSE IN WORD wLen, // maximum length of the field to extract IN WORD wFieldNum, // field number we want from the record IN CHAR cFieldSeparator, // character to use as a field separator IN CHAR *lpszRecord, // record to extract the field from OUT CHAR *lpszField) // field we have extracted { // HRESULT hr = NOERROR; BOOL fRet = FALSE; // CHAR *lpszBeginField = lpszField; while((wFieldNum > 0) && (*lpszRecord != 0)) { // If we found a field separator, increment current field if(*lpszRecord == cFieldSeparator) { wFieldNum--; } // If we are at the desired field, copy the current character into it else if(wFieldNum == 1 && wLen > 1) { *lpszField = *lpszRecord; lpszField++; wLen--; } lpszRecord++; } *lpszField = 0; // If the requested field # existed, return True, // otherwise we ran out of fields - return False if(wFieldNum <= 1) { fRet = TRUE; } else { fRet = FALSE; } return(fRet); } //$--FCsvGetRecord-------------------------------------------------------------- // Given a buffer, the buffer's length and a file handle, this // function fills the buffer with a single line read from the file. // The NL & CR are NOT put into the buffer. No unprintable characters are // placed in the buffer // ----------------------------------------------------------------------------- BOOL FCsvGetRecord( // RETURNS: TRUE/FALSE IN WORD wBufferLen, // length of the record buffer IN HANDLE hFile, // file handle to read from OUT CHAR *lpszBuffer) // record we have retrieved { // HRESULT hr = NOERROR; DWORD dwBytesRead = 0; BOOL fReadData = FALSE; while((ReadFile(hFile, (LPVOID)lpszBuffer, 1, &dwBytesRead, NULL) == TRUE) && (wBufferLen > 1) && (*lpszBuffer != '\n') && (dwBytesRead > 0)) { fReadData = TRUE; // Only store character in buffer if it is printable! if((isprint(*lpszBuffer)) || (*lpszBuffer == EXCHINST_DELIM)) { lpszBuffer++; wBufferLen--; } } // If a given record is too long it is a problem!!! if(wBufferLen <= 0) { fReadData = FALSE; } *lpszBuffer = 0; return(fReadData); } //$--HrEDKExportObject---------------------------------------------------------- // This function will export an object from an Exchange server. // ----------------------------------------------------------------------------- static HRESULT HrEDKExportObject( // RETURNS: return code IN LPWSTR lpszServer, // server name IN LPWSTR lpszBasePoint, // base point IN DWORD dwControlFlags, // control flags IN LPWSTR *rgpszClasses, // classes IN LPWSTR lpszObjectAttributes, // list of attributes to export OUT LPWSTR lpszTempName) // temporary file name { HRESULT hr = E_FAIL; ULONG cErrors = 0; HANDLE hTempFile = INVALID_HANDLE_VALUE; CHAR szTempPath[MAX_PATH+1] = {0}; DWORD dwNumberOfBytesWritten = 0; BEXPORT_PARMS BExportParms = {0}; BOOL fRet = FALSE; // Get temporary directory path if(!GetTempPath(MAX_PATH, szTempPath)) { hr = HR_LOG(HRESULT_FROM_WIN32(GetLastError())); goto cleanup; } // Get temporary file name if(!GetTempFileName(szTempPath, FILE_PREFIX, 0, lpszTempName)) { hr = HR_LOG(HRESULT_FROM_WIN32(GetLastError())); goto cleanup; } // Create the temporary file hTempFile = CreateFile(lpszTempName, GENERIC_WRITE, 0, (LPSECURITY_ATTRIBUTES)NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL); // Check to see if temporary file was created... if(hTempFile == INVALID_HANDLE_VALUE) { hr = HR_LOG(HRESULT_FROM_WIN32(GetLastError())); goto cleanup; } // Write data to the temporary file & close it fRet = WriteFile( hTempFile, lpszObjectAttributes, lstrlen(lpszObjectAttributes)*sizeof(CHAR), &dwNumberOfBytesWritten, NULL); if(fRet == FALSE) { hr = HR_LOG(HRESULT_FROM_WIN32(GetLastError())); goto cleanup; } fRet = WriteFile( hTempFile, NEW_LINE, lstrlen(NEW_LINE)*sizeof(CHAR), &dwNumberOfBytesWritten, NULL); if(fRet == FALSE) { hr = HR_LOG(HRESULT_FROM_WIN32(GetLastError())); goto cleanup; } CLOSEHANDLE(hTempFile); // // Batch Export // BExportParms.dwDAPISignature = DAPI_SIGNATURE; BExportParms.dwFlags = dwControlFlags | DAPI_MODIFY_REPLACE_PROPERTIES | DAPI_SUPPRESS_PROGRESS | DAPI_SUPPRESS_COMPLETION | DAPI_SUPPRESS_ARCHIVES | DAPI_IMPORT_NO_ERR_FILE; BExportParms.pszExportFile = lpszTempName; BExportParms.pszBasePoint = lpszBasePoint; BExportParms.pszDSAName = lpszServer; BExportParms.rgpszClasses = rgpszClasses; BExportParms.chColSep = EXCHINST_DELIM; BExportParms.chQuote = EXCHINST_QUOTE; BExportParms.chMVSep = EXCHINST_MV_SEP; cErrors = (*pBatchExport)(&BExportParms); if(cErrors == 0) { hr = NOERROR; } else { hr = HR_LOG(E_FAIL); } cleanup: CLOSEHANDLE(hTempFile); return hr; } //$--HrEDKEnumDNs--------------------------------------------------------------- // Enumerates the distinguished name(s). // ----------------------------------------------------------------------------- static HRESULT HrEDKEnumDNs( // RETURNS: return code IN LPWSTR lpszRootDN, // distinguished name of DIT root IN LPWSTR lpszServer, // server name IN DWORD dwControlFlags, // control flags IN LPWSTR *rgpszClasses, // classes OUT LPWSTR *lppszDNs) // distinguished names { HRESULT hr = NOERROR; HANDLE hTempFile = INVALID_HANDLE_VALUE; CHAR szObjectAttributes[MAX_CSV_LINE_SIZ] = {0}; // CHAR szAttributeValues[MAX_CSV_LINE_SIZ] = {0}; // CHAR szCurRecord[MAX_CSV_LINE_SIZ] = {0}; CHAR szCurLine[MAX_CSV_LINE_SIZ] = {0}; CHAR szCurField[MAX_PATH+1] = {0}; CHAR szTempName[MAX_PATH+1] = {0}; WORD wAttribField = MAX_WORD; WORD wCurrField = 0; LPWSTR lpsz = NULL; ULONG ulCurrOffset = 0; *lppszDNs = NULL; BEGIN_CSV_LINE (szObjectAttributes, OBJ_CLASS); APPEND_CSV_LINE (szObjectAttributes, OBJ_DIST_NAME); hr = HrEDKExportObject( lpszServer, lpszRootDN, dwControlFlags, rgpszClasses, szObjectAttributes, szTempName); if(SUCCEEDED(hr)) { // Open the temporary file hTempFile = CreateFile( szTempName, GENERIC_READ, 0, (LPSECURITY_ATTRIBUTES)NULL, OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, (HANDLE)NULL); if(hTempFile == INVALID_HANDLE_VALUE) { hr = HR_LOG(E_FAIL); goto cleanup; } // // The first line contains the list of fields - find which field has // the attribute we are looking for. // FCsvGetRecord(MAX_CSV_LINE_SIZ, hTempFile, szCurLine); for( wCurrField = 1; FCsvGetField( MAX_PATH, wCurrField, EXCHINST_DELIM, szCurLine, szCurField); wCurrField++) { if(wcscmp(szCurField, OBJ_DIST_NAME) == 0) { wAttribField = wCurrField; break; } } // Was the field exported & found above? if(wAttribField == MAX_WORD) { hr = HR_LOG(E_FAIL); goto cleanup; } ulCurrOffset = 0; while(FCsvGetRecord (MAX_CSV_LINE_SIZ, hTempFile, szCurLine)) { FCsvGetField( MAX_PATH, wAttribField, EXCHINST_DELIM, szCurLine, szCurField); if( *szCurField) { if(lpsz == NULL) { lpsz = (LPWSTR)GlobalAlloc( GMEM_FIXED, cbStrLen(szCurField) + sizeof(CHAR)); } else { lpsz = (LPWSTR)GlobalReAlloc( lpsz, GlobalSize(lpsz) + cbStrLen(szCurField), GMEM_MOVEABLE); } if(lpsz == NULL) { hr = HR_LOG(HRESULT_FROM_WIN32(GetLastError())); goto cleanup; } lstrcpy(&lpsz[ulCurrOffset], szCurField); ulCurrOffset += cbStrLen(szCurField); lpsz[ulCurrOffset] = 0; } } } *lppszDNs = lpsz; cleanup: CLOSEHANDLE(hTempFile); if(FAILED(hr)) { GLOBALFREE(*lppszDNs); } return hr; } //$--HrEnumOrganizations----------------------------------------------------- // Enumerates the organization name(s). // ----------------------------------------------------------------------------- HRESULT HrEnumOrganizations( // RETURNS: return code IN LPWSTR lpszRootDN, // distinguished name of DIT root IN LPWSTR lpszServer, // server name OUT LPWSTR *lppszOrganizations) // organizations { HRESULT hr = NOERROR; LPWSTR rgpszClasses[2] = {0}; rgpszClasses[0] = ORGANIZATION; rgpszClasses[1] = NULL; hr = HrEDKEnumDNs( lpszRootDN, lpszServer, DAPI_EXPORT_SUBTREE, rgpszClasses, lppszOrganizations); return hr; } //$--HrEnumSites------------------------------------------------------------- // Enumerates the site name(s). // ----------------------------------------------------------------------------- HRESULT HrEnumSites( // RETURNS: return code IN LPWSTR lpszServer, // server name IN LPWSTR lpszOrganizationDN, // distinguished name of organization OUT LPWSTR *lppszSites) // sites { HRESULT hr = NOERROR; LPWSTR rgpszClasses[2] = {0}; rgpszClasses[0] = ORGANIZATIONAL_UNIT; rgpszClasses[1] = NULL; hr = HrEDKEnumDNs( lpszOrganizationDN, lpszServer, 0, rgpszClasses, lppszSites); return hr; } //$--HrEnumContainers-------------------------------------------------------- // Enumerates the container name(s). // ----------------------------------------------------------------------------- HRESULT HrEnumContainers( // RETURNS: return code IN LPWSTR lpszServer, // server name IN LPWSTR lpszSiteDN, // distinguished name of site IN BOOL fSubtree, // sub-tree? OUT LPWSTR *lppszContainers) // containers { HRESULT hr = NOERROR; LPWSTR rgpszClasses[2] = {0}; DWORD dwControlFlags = 0; rgpszClasses[0] = CONTAINER; rgpszClasses[1] = NULL; if(fSubtree == TRUE) { dwControlFlags = DAPI_EXPORT_SUBTREE; } hr = HrEDKEnumDNs( lpszSiteDN, lpszServer, dwControlFlags, rgpszClasses, lppszContainers); return hr; } /**/