/*********************************************************************** * * ABLOGON.C * * * The Microsoft At Work Fax Address Book Provider. * * This file has the code to implement the Microsoft At Work Fax Address Book's logon * object. * * The following routines are implemented in this file: * * ABPLOGON_QueryInterface * ABPLOGON_Release * ABPLOGON_Logoff * ABPLOGON_OpenEntry * ABPLOGON_CompareEntryIDs * ABPLOGON_Advise * ABPLOGON_Unadvise * ABPLOGON_OpenStatusEntry * ABPLOGON_OpenTemplateID * ABPLOGON_GetOneOffTable * ABPLOGON_PrepareRecips * * LpMuidFromLogon * HrLpszGetCurrentFileName * HrReplaceCurrentfileName * GenerateContainerDN * HrBuildRootHier * * * Copyright 1992, 1993, 1994 Microsoft Corporation. All Rights Reserved. * * Revision History: * * When Who What * -------- ------------------ --------------------------------------- * 8.3.94 MAPI Original source from MAPI sample ABP build 304 * 8.3.94 Yoram Yaacovi Moved code from the FAX ABP original abp.c to here * 11.11.94 Yoram Yaacovi Upgrade to MAPI 318 (PR_INSTANCE_KEY) * ***********************************************************************/ #include "faxab.h" ABPLOGON_Vtbl vtblABPLOGON = { ABPLOGON_QueryInterface, (ABPLOGON_AddRef_METHOD *) ROOT_AddRef, ABPLOGON_Release, (ABPLOGON_GetLastError_METHOD *) ROOT_GetLastError, ABPLOGON_Logoff, ABPLOGON_OpenEntry, ABPLOGON_CompareEntryIDs, ABPLOGON_Advise, ABPLOGON_Unadvise, ABPLOGON_OpenStatusEntry, ABPLOGON_OpenTemplateID, ABPLOGON_GetOneOffTable, ABPLOGON_PrepareRecips }; /* - HrNewABLogon - * * Creates a new Microsoft At Work Fax AB Logon object. */ HRESULT HrNewABLogon( LPABLOGON * lppABLogon, LPABPROVIDER lpABP, LPMAPISUP lpMAPISup, LPTSTR lpszFABFile, LPMAPIUID lpmuid, HINSTANCE hLibrary, LPALLOCATEBUFFER lpAllocBuff, LPALLOCATEMORE lpAllocMore, LPFREEBUFFER lpFreeBuff, LPMALLOC lpMalloc ) { SCODE sc; HRESULT hResult = hrSuccess; SPropValue rgSPVStat[6]; LPABPLOGON lpABPLogon = NULL; #ifdef UNICODE CHAR szFileName[ MAX_PATH ]; #endif /* * Allocate space for the lpABPLogon object */ sc = lpAllocBuff(SIZEOF(ABPLOGON), &lpABPLogon); if (FAILED(sc)) { hResult = ResultFromScode(sc); goto out; } /* * Initialize the ABPLogon object */ lpABPLogon->lpVtbl = &vtblABPLOGON; lpABPLogon->lcInit = 1; lpABPLogon->hResult = hrSuccess; lpABPLogon->idsLastError = 0; lpABPLogon->hLibrary = hLibrary; lpABPLogon->lpMalloc = lpMalloc; lpABPLogon->lpAllocBuff = lpAllocBuff; lpABPLogon->lpAllocMore = lpAllocMore; lpABPLogon->lpFreeBuff = lpFreeBuff; lpABPLogon->lpMapiSup = lpMAPISup; lpABPLogon->lpABP = (LPABPROVIDER) lpABP; lpABPLogon->lpszFileName = lpszFABFile; lpABPLogon->muidID = *lpmuid; lpABPLogon->lpTDatRoot = NULL; lpABPLogon->lpTDatOO = NULL; /* * Register my status row... */ // MAPI doesn't use UNICODE for this one... rgSPVStat[0].ulPropTag = PR_DISPLAY_NAME_A; #ifdef UNICODE szFileName[0] = 0; WideCharToMultiByte( CP_ACP, 0, lpszFABFile, -1, szFileName, ARRAYSIZE(szFileName), NULL, NULL ); rgSPVStat[0].Value.lpszA = szFileName; #else rgSPVStat[0].Value.lpszA = lpszFABFile; #endif rgSPVStat[1].ulPropTag = PR_RESOURCE_METHODS; rgSPVStat[1].Value.l = 0; rgSPVStat[2].ulPropTag = PR_RESOURCE_FLAGS; rgSPVStat[2].Value.l = 0; rgSPVStat[3].ulPropTag = PR_STATUS_CODE; rgSPVStat[3].Value.l = STATUS_AVAILABLE; // MAPI doesn't use UNICODE for this one rgSPVStat[4].ulPropTag = PR_STATUS_STRING_A; rgSPVStat[4].Value.lpszA = "Available"; // MAPI doesn't use UNICODE for this one rgSPVStat[5].ulPropTag = PR_PROVIDER_DISPLAY_A; rgSPVStat[5].Value.lpszA = "Microsoft Fax Address Book Provider"; /* * Set the Status Row for this provider, * but do not allow an error from setting the * status row to cause failure to Logon. */ (void)lpMAPISup->lpVtbl->ModifyStatusRow(lpMAPISup, ARRAYSIZE(rgSPVStat), rgSPVStat, 0); /* * AddRef the support object, because we're keeping * a pointer to it in our Logon object. */ lpMAPISup->lpVtbl->AddRef(lpMAPISup); /* * AddRef our parent ABInit object */ lpABP->lpVtbl->AddRef(lpABP); InitializeCriticalSection(&lpABPLogon->cs); *lppABLogon = (LPABLOGON) lpABPLogon; out: DebugTraceResult(HrNewABPLogon, hResult); return hResult; } /************************************************************************* * - ABPLOGON_QueryInterface - */ STDMETHODIMP ABPLOGON_QueryInterface( LPABPLOGON lpABPLogon, REFIID lpiid, LPVOID * ppvObj ) { if ( IsBadReadPtr(lpiid, SIZEOF(IID)) || IsBadWritePtr(ppvObj, SIZEOF(LPVOID)) ) { DebugTraceSc(ABPLOGON_QueryInterface, E_INVALIDARG); return ResultFromScode(E_INVALIDARG); } /* See if the requested interface is one of ours */ if ( memcmp(lpiid, &IID_IUnknown, SIZEOF(IID)) && memcmp(lpiid, &IID_IABLogon, SIZEOF(IID)) ) { *ppvObj = NULL; /* OLE requires zeroing [out] parameter on error */ DebugTraceSc(ABPLOGON_QueryInterface, E_NOINTERFACE); return ResultFromScode(E_NOINTERFACE); } /* We'll do this one. Bump the usage count and return a new pointer. */ EnterCriticalSection(&lpABPLogon->cs); ++lpABPLogon->lcInit; LeaveCriticalSection(&lpABPLogon->cs); *ppvObj = lpABPLogon; return hrSuccess; } /* * Use ROOTs AddRef */ /************************************************************************* * - ABPLOGON_Release - */ STDMETHODIMP_(ULONG) ABPLOGON_Release(LPABPLOGON lpABPLogon) { LONG lcInit; EnterCriticalSection(&lpABPLogon->cs); lcInit = --lpABPLogon->lcInit; LeaveCriticalSection(&lpABPLogon->cs); if (lcInit == 0) { DeleteCriticalSection(&lpABPLogon->cs); lpABPLogon->lpVtbl = NULL; lpABPLogon->lpFreeBuff(lpABPLogon); return (0); } return lcInit; } /************************************************************************* * - ABPLOGON_Logoff - * Logoff from this logon object. Clean up any resources/objects that * our logon object has accumulated. * * */ STDMETHODIMP ABPLOGON_Logoff(LPABPLOGON lpABPLogon, ULONG ulFlags) { DebugTrace("AWFXAB32(ABPLOGON_Logoff): entering\n"); #ifdef DO_WE_REALLY_NEED_TAPI /* * Let TAPI go */ DeinitTAPI(); #endif /* * Remove this logon object from the list of known * logon objects associated with this initialization * of this provider. */ (void) RemoveLogonObject(lpABPLogon->lpABP, lpABPLogon, lpABPLogon->lpFreeBuff); /* * No longer need to be holding on to our parent */ lpABPLogon->lpABP->lpVtbl->Release(lpABPLogon->lpABP); /* * Free up the file */ lpABPLogon->lpFreeBuff(lpABPLogon->lpszFileName); if (lpABPLogon->lpTDatRoot) lpABPLogon->lpTDatRoot->lpVtbl->Release(lpABPLogon->lpTDatRoot); if (lpABPLogon->lpTDatOO) lpABPLogon->lpTDatOO->lpVtbl->Release(lpABPLogon->lpTDatOO); /* * very last thing I should do is release the support object */ lpABPLogon->lpMapiSup->lpVtbl->Release(lpABPLogon->lpMapiSup); DebugTrace("AWFXAB32(ABPLOGON_Logoff): leaving\n"); return hrSuccess; } /************************************************************************* * - ABPLOGON_OpenEntry - * Creates an object with (at least) the IMAPIProp interface from an * entryID. * * There are four valid types of entryIDs handled: * * NULL <- return back the root container object * DIR_ENTRYID <- return back the directory container object * USR_ENTRYID <- return back the MAILUSER object * OOUSER_ENTRYID <- return back the OneOff MAILUSER object * * Note: This call is reused for all other internal objects that support OpenEntry(). * Those other calls *must* check their parameters before calling this method. * The only other way this method is called is via MAPI which does parameter checking * for us. The most we'll do here is assert our parameters. */ STDMETHODIMP ABPLOGON_OpenEntry( LPABPLOGON lpABPLogon, ULONG cbEntryID, LPENTRYID lpEntryID, LPCIID lpInterface, ULONG ulFlags, ULONG * lpulObjType, LPUNKNOWN * lppUnk ) { LPDIR_ENTRYID lpEID = (LPDIR_ENTRYID) lpEntryID; HRESULT hResult = hrSuccess; LPTSTR lpszFileName; /* * Check the EntryID */ // used to be: if (!lpEntryID) if (!cbEntryID) { LPABCONT lpABCont = NULL; /* * Special case: the root level object */ NFAssertSz(!lpEntryID, "Non-NULL entry id passed with 0 cb to OpenEntry()\n"); /* Make this new object */ /* * Get the current .FAB file name from our logon object */ hResult = HrLpszGetCurrentFileName((LPABLOGON) lpABPLogon, &lpszFileName); if (HR_FAILED(hResult)) goto out; // If there is a real Fax AB if ((fExposeFaxAB) && (lpszFileName[0] != 0)) hResult = HrNewROOT((LPABCONT *) lppUnk, lpulObjType, (LPABLOGON) lpABPLogon, lpInterface, lpABPLogon->hLibrary, lpABPLogon->lpAllocBuff, lpABPLogon->lpAllocMore, lpABPLogon->lpFreeBuff, lpABPLogon->lpMalloc); else // No Fax AB container return ResultFromScode (MAPI_E_INTERFACE_NOT_SUPPORTED); lpABPLogon->lpFreeBuff(lpszFileName); goto out; } /* * There's an entryID there, is it mine?? * I need to check because I'm reusing this routine for * my Container->OpenEntry call, and I can't be sure the * client will always be well behaved. * * When this routine is called from MAPI, this call is redundant. But * because I'm reusing this routine, I gotta check. */ /* Compare MAPIUIDs */ if (memcmp(&(((LPDIR_ENTRYID) lpEntryID)->muid), &muidABMAWF, SIZEOF(MAPIUID))) { /* * Not mine! */ hResult = ResultFromScode(MAPI_E_INVALID_ENTRYID); DebugTraceResult(ABPLOGON_OpenEntry, hResult); goto out; } /* * What object does this correspond to?? */ /* I've only got two types: containers and users */ if (lpEID->ulType == MAWF_DIRECTORY) { LPABLOGON lpABPLogonT = NULL; /* entry id must have the same verson number */ if (lpEID->ulVersion != MAWF_VERSION) { hResult = ResultFromScode(MAPI_E_UNKNOWN_ENTRYID); SetErrorIDS(lpABPLogon, hResult, IDS_OLD_EID); goto out; } /* * find the correct logon object for this entryid */ (void) FindLogonObject(lpABPLogon->lpABP, &lpEID->muidID, &lpABPLogonT); /* did we find the corresponding logon object */ if (!lpABPLogonT) { hResult = ResultFromScode(MAPI_E_UNKNOWN_ENTRYID); goto out; } // If I don't have a fab file at this point, I can't open the AB container HrLpszGetCurrentFileName((LPABLOGON) lpABPLogonT, &lpszFileName); if (lpszFileName[0] == 0) { hResult = ResultFromScode (MAPI_E_NO_SUPPORT); DebugTraceResult(ABPLOGON_OpenEntry, hResult); goto out; } hResult = HrNewFaxDirectory( (LPABCONT *) lppUnk, lpulObjType, (LPABLOGON) lpABPLogonT, lpInterface, lpABPLogon->hLibrary, lpABPLogon->lpAllocBuff, lpABPLogon->lpAllocMore, lpABPLogon->lpFreeBuff, lpABPLogon->lpMalloc); goto out; } if (lpEID->ulType == MAWF_USER) { if (cbEntryID == (ULONG) sizeof(USR_ENTRYID)) { hResult = HrNewFaxUser( (LPMAILUSER *) lppUnk, lpulObjType, cbEntryID, lpEntryID, (LPABLOGON) lpABPLogon, lpInterface, lpABPLogon->hLibrary, lpABPLogon->lpAllocBuff, lpABPLogon->lpAllocMore, lpABPLogon->lpFreeBuff, lpABPLogon->lpMalloc); goto out; } } if (lpEID->ulType == MAWF_ONEOFF) { if (cbEntryID == (ULONG) sizeof(OOUSER_ENTRYID)) { hResult = HrNewFaxOOUser( (LPMAILUSER *) lppUnk, lpulObjType, cbEntryID, lpEntryID, (LPABLOGON) lpABPLogon, lpInterface, lpABPLogon->hLibrary, lpABPLogon->lpAllocBuff, lpABPLogon->lpAllocMore, lpABPLogon->lpFreeBuff, lpABPLogon->lpMalloc); goto out; } } hResult = ResultFromScode(MAPI_E_UNKNOWN_ENTRYID); out: DebugTraceResult(ABPLOGON_OpenEntry, hResult); return hResult; } /************************************************************************* * - ABPLOGON_CompareEntryIDs - * If the two entryids are mine and they're of the same type, then * just do a binary comparison to see if they're equal. * */ STDMETHODIMP ABPLOGON_CompareEntryIDs( LPABPLOGON lpABPLogon, ULONG cbEntryID1, LPENTRYID lpEntryID1, ULONG cbEntryID2, LPENTRYID lpEntryID2, ULONG ulFlags, ULONG * lpulResult ) { LPDIR_ENTRYID lpEID1 = (LPDIR_ENTRYID) lpEntryID1; LPDIR_ENTRYID lpEID2 = (LPDIR_ENTRYID) lpEntryID2; HRESULT hResult = hrSuccess; /* * Check to see if their MUID is mine */ if ( memcmp(&(lpEID1->muid), &muidABMAWF, SIZEOF(MAPIUID)) || memcmp(&(lpEID2->muid), &muidABMAWF, SIZEOF(MAPIUID)) ) { /* * No recognition of these entryids. */ *lpulResult = (ULONG) FALSE; hResult = ResultFromScode(MAPI_E_UNKNOWN_ENTRYID); goto out; } /* * See if the type of entryids are the same */ if (lpEID1->ulType != lpEID2->ulType) { /* * They're not, so they don't match */ *lpulResult = (ULONG) FALSE; goto out; } /* * See if the entryids are the same size. They'd better be * if they're the same type. */ if (cbEntryID1 != cbEntryID2) { /* * They're not?!? Then I don't know these... */ *lpulResult = (ULONG) FALSE; hResult = ResultFromScode(MAPI_E_UNKNOWN_ENTRYID); goto out; } /* * Check for Directory entryids */ if (lpEID1->ulType == MAWF_DIRECTORY) { /* * Ok, I'm dealing with directory entryids */ /* * Better make sure it's the right size */ if (cbEntryID1 != sizeof(DIR_ENTRYID)) { /* * This doesn't make sense. I don't recognize this entryid. */ *lpulResult = (ULONG) FALSE; hResult = ResultFromScode(MAPI_E_UNKNOWN_ENTRYID); goto out; } /* * At this point it's just a memcmp */ if (memcmp(lpEID1, lpEID2, SIZEOF(DIR_ENTRYID))) { /* * They're not equal */ *lpulResult = (ULONG) FALSE; goto out; } /* * They must be the same */ *lpulResult = (ULONG) TRUE; goto out; } if (lpEID1->ulType == MAWF_USER) { /* * Ok, I'm dealing with user entryids */ /* * Better make sure it's the right size */ if (cbEntryID1 != sizeof(USR_ENTRYID)) { /* * This doesn't make sense. I don't recognize this entryid. */ *lpulResult = (ULONG) FALSE; hResult = ResultFromScode(MAPI_E_UNKNOWN_ENTRYID); goto out; } /* * At this point it's just a memcmp */ if (memcmp(lpEID1, lpEID2, SIZEOF(USR_ENTRYID))) { /* * They're not equal */ *lpulResult = (ULONG) FALSE; goto out; } /* * They must be the same */ *lpulResult = (ULONG) TRUE; goto out; } if (lpEID1->ulType == MAWF_ONEOFF) { /* * Ok, I'm dealing with oneoff user entryids */ /* * Better make sure it's the right size */ if (cbEntryID1 != SIZEOF(OOUSER_ENTRYID)) { /* * This doesn't make sense. I don't recognize this entryid. */ *lpulResult = (ULONG) FALSE; hResult = ResultFromScode(MAPI_E_UNKNOWN_ENTRYID); goto out; } /* * At this point it's just a memcmp */ if (memcmp(lpEID1, lpEID2, SIZEOF(OOUSER_ENTRYID))) { /* * They're not equal */ *lpulResult = (ULONG) FALSE; goto out; } /* * They must be the same */ *lpulResult = (ULONG) TRUE; goto out; } /* * It's no entryid I know of */ *lpulResult = (ULONG) FALSE; hResult = ResultFromScode(MAPI_E_UNKNOWN_ENTRYID); out: DebugTraceResult(ABPLOGON_CompareEntryIDs, hResult); return hResult; } /************************************************************************* * - ABPLOGON_OpenStatusEntry - * * * */ STDMETHODIMP ABPLOGON_OpenStatusEntry( LPABPLOGON lpABPLogon, LPCIID lpIID, ULONG ulFlags, ULONG FAR * lpulObjType, LPMAPISTATUS FAR * lppEntry ) { HRESULT hr; /* * Validate Parameters */ if ( IsBadReadPtr(lpABPLogon, (UINT) SIZEOF(ABPLOGON)) || (lpIID && IsBadReadPtr(lpIID, (UINT) SIZEOF(IID))) || IsBadWritePtr(lpulObjType, (UINT) SIZEOF(ULONG FAR *)) || IsBadWritePtr(lppEntry, (UINT) SIZEOF(LPMAPISTATUS)) ) { DebugTraceSc(ABPLogon_OpenStatusEntry, E_INVALIDARG); return ResultFromScode(E_INVALIDARG); } hr = HrNewStatusObject( lppEntry, lpulObjType, ulFlags, (LPABLOGON) lpABPLogon, lpIID, lpABPLogon->hLibrary, lpABPLogon->lpAllocBuff, lpABPLogon->lpAllocMore, lpABPLogon->lpFreeBuff, lpABPLogon->lpMalloc); DebugTraceResult(ABPLOGON_OpenStatusEntry, hr); return hr; } /************************************************************************* * - ABPLOGON_OpenTemplateID - * * * */ STDMETHODIMP ABPLOGON_OpenTemplateID( LPABPLOGON lpABPLogon, ULONG cbTemplateId, LPENTRYID lpTemplateId, ULONG ulTemplateFlags, LPMAPIPROP lpMAPIPropData, LPCIID lpInterface, LPMAPIPROP * lppMAPIPropNew, LPMAPIPROP lpMAPIPropSibling ) { HRESULT hResult; /* * Validate Parameters */ if ( IsBadReadPtr(lpABPLogon, (UINT) SIZEOF(ABPLOGON)) || IsBadReadPtr(lpTemplateId, (UINT) cbTemplateId) || IsBadReadPtr(lpMAPIPropData, (UINT) SIZEOF(LPVOID)) || (lpInterface && IsBadReadPtr(lpInterface, (UINT) sizeof(IID))) || IsBadWritePtr(lppMAPIPropNew, (UINT) SIZEOF(LPMAPIPROP)) || (lpMAPIPropSibling && IsBadReadPtr(lpMAPIPropSibling, (UINT) SIZEOF(LPVOID))) ) { DebugTraceSc(ABPLogon_OpenTemplateID, E_INVALIDARG); return ResultFromScode(E_INVALIDARG); } /* //$ need stronger checking here... */ /* entryid better be right size */ if (cbTemplateId != sizeof(OOUSER_ENTRYID) && cbTemplateId != sizeof(USR_ENTRYID)) { hResult = ResultFromScode(MAPI_E_INVALID_ENTRYID); goto out; } /* is it my entry id compare MAPIUIDs */ if (memcmp(&(((LPUSR_ENTRYID) lpTemplateId)->muid), &muidABMAWF, SIZEOF(MAPIUID))) { /* * Not mine! */ hResult = ResultFromScode( MAPI_E_INVALID_ENTRYID ); goto out; } /* better be a oneoff user entryid or a user entry id */ if (((LPUSR_ENTRYID) lpTemplateId)->ulType == MAWF_ONEOFF) { hResult = HrNewOOTID( lppMAPIPropNew, cbTemplateId, lpTemplateId, ulTemplateFlags, lpMAPIPropData, (LPABLOGON) lpABPLogon, lpInterface, lpABPLogon->hLibrary, lpABPLogon->lpAllocBuff, lpABPLogon->lpAllocMore, lpABPLogon->lpFreeBuff, lpABPLogon->lpMalloc ); } else if (((LPUSR_ENTRYID) lpTemplateId)->ulType == MAWF_USER) { hResult = HrNewTID( lppMAPIPropNew, cbTemplateId, lpTemplateId, ulTemplateFlags, lpMAPIPropData, (LPABLOGON) lpABPLogon, lpInterface, lpABPLogon->hLibrary, lpABPLogon->lpAllocBuff, lpABPLogon->lpAllocMore, lpABPLogon->lpFreeBuff, lpABPLogon->lpMalloc ); } else { hResult = MakeResult(MAPI_E_INVALID_ENTRYID); } out: DebugTraceResult(ABPLOGON_OpenTemplateID, hResult); return hResult; } /* - ABPLOGON_GetOneOffTable - * Returns the lists of one-offs that this providers can support creation of. * This list is added to the entries gathered from all the other AB logon objects * and exposed to the user as the list of things that can be created on a * message. Also this total list is available to other providers through the * support method GetOneOffTable(). * * Note: There's a bug here that if there are more than one Microsoft At Work Fax Address Books * installed on a particular profile, then there will be multiple entries in the * one-off table from this provider. This can be changed to only have one one-off * entry, no matter how many FABs are configured in a profile, if the one-off table * was associated with the ABInit object. */ /* * Column set for the oneoff table */ enum { ivalootPR_DISPLAY_NAME = 0, ivalootPR_ENTRYID, ivalootPR_DEPTH, ivalootPR_SELECTABLE, ivalootPR_ADDRTYPE, ivalootPR_DISPLAY_TYPE, ivalootPR_INSTANCE_KEY, ivalootMax }; static const SizedSPropTagArray(ivalootMax, tagaColSetOOTable) = { ivalootMax, { PR_DISPLAY_NAME_A, PR_ENTRYID, PR_DEPTH, PR_SELECTABLE, PR_ADDRTYPE_A, PR_DISPLAY_TYPE, PR_INSTANCE_KEY } }; STDMETHODIMP ABPLOGON_GetOneOffTable( LPABPLOGON lpABPLogon, ULONG ulFlags, LPMAPITABLE * lppTable ) { SCODE sc; HRESULT hResult; SRow sRow; SPropValue rgsPropValue[ivalootMax]; CHAR displayNameString[MAX_DISPLAY_NAME]; ULONG ulInstanceKey = 1; HINSTANCE hInst; #ifdef UNICODE CHAR szEMT[ MAX_PATH ]; #endif /* * Validate Parameters */ if ( ulFlags & ~(MAPI_UNICODE) ) { DebugTraceArg( APBLOGON_GetOneOffTable, "Unknown Flags" ); return ResultFromScode( MAPI_E_UNKNOWN_FLAGS ); } if ( ulFlags & MAPI_UNICODE ) { DebugTraceArg( APBLOGON_GetOneOffTable, "UNICODE not supported" ); return ResultFromScode( MAPI_E_BAD_CHARWIDTH ); } if ( IsBadReadPtr(lpABPLogon, (UINT) SIZEOF(ABPLOGON)) || IsBadWritePtr(lppTable, (UINT) SIZEOF(LPMAPITABLE)) ) { DebugTraceSc(ABPLogon_GetOneOffTable, E_INVALIDARG); return ResultFromScode(E_INVALIDARG); } EnterCriticalSection(&lpABPLogon->cs); /* * If there's not one already associated with this logon object, * then create one. */ if (!lpABPLogon->lpTDatOO) { /* Create a Table data object */ sc = CreateTable( (LPIID) &IID_IMAPITableData, lpABPLogon->lpAllocBuff, lpABPLogon->lpAllocMore, lpABPLogon->lpFreeBuff, lpABPLogon->lpMalloc, 0, PR_DISPLAY_NAME_A, (LPSPropTagArray) &tagaColSetOOTable, &(lpABPLogon->lpTDatOO)); if (FAILED(sc)) { hResult = ResultFromScode(sc); goto out; } // Get the instance handle, so that I can get the display strings off the resource file hInst = lpABPLogon->hLibrary; // Initialize the row sRow.cValues = ivalootMax; sRow.lpProps = rgsPropValue; /* * Fill the table * * we want to add two entries to the one-off table, so that we'll get: * * Microsoft Fax * Fax */ // First do the 'Microsoft Fax' // Name of the One-Off rgsPropValue[ivalootPR_DISPLAY_NAME].ulPropTag = PR_DISPLAY_NAME_A; LoadStringA( hInst, IDS_MAWF_NAME, displayNameString, ARRAYSIZE(displayNameString)); rgsPropValue[ivalootPR_DISPLAY_NAME].Value.lpszA = displayNameString; rgsPropValue[ivalootPR_ENTRYID].ulPropTag = PR_ENTRYID; rgsPropValue[ivalootPR_ENTRYID].Value.bin.cb = SIZEOF(OOUSER_ENTRYID); rgsPropValue[ivalootPR_ENTRYID].Value.bin.lpb = (LPVOID) &ONEOFF_EID; // the hierarcy level (how far to indent a display name). I choose not to indent rgsPropValue[ivalootPR_DEPTH].ulPropTag = PR_DEPTH; rgsPropValue[ivalootPR_DEPTH].Value.l = 0; // Selection flags. TRUE indicates this entry ID can be used in CreateEntry() call. rgsPropValue[ivalootPR_SELECTABLE].ulPropTag = PR_SELECTABLE; rgsPropValue[ivalootPR_SELECTABLE].Value.b = TRUE; // The address type that would be generated by an entry // created from this template rgsPropValue[ivalootPR_ADDRTYPE].ulPropTag = PR_ADDRTYPE_A; #ifdef UNICODE szEMT[0] = 0; WideCharToMultiByte( CP_ACP, 0, lpszEMT, -1, szEMT, ARRAYSIZE(szEMT), NULL, NULL ); rgsPropValue[ivalootPR_ADDRTYPE].Value.lpszA = szEMT; #else rgsPropValue[ivalootPR_ADDRTYPE].Value.LPSZ = lpszEMT; #endif /* * The display type associated with a recipient built with this template */ rgsPropValue[ivalootPR_DISPLAY_TYPE].ulPropTag = PR_DISPLAY_TYPE; rgsPropValue[ivalootPR_DISPLAY_TYPE].Value.lpszA = DT_MAILUSER; /* * The instance key of this row in this one-off table. * using 1 for this row */ ulInstanceKey = 1; rgsPropValue[ivalootPR_INSTANCE_KEY].ulPropTag = PR_INSTANCE_KEY; rgsPropValue[ivalootPR_INSTANCE_KEY].Value.bin.cb = SIZEOF(ULONG); rgsPropValue[ivalootPR_INSTANCE_KEY].Value.bin.lpb = (LPBYTE) &ulInstanceKey; (void) lpABPLogon->lpTDatOO->lpVtbl->HrModifyRow( lpABPLogon->lpTDatOO, &sRow); #ifdef DO_MULTI_LEVEL_ADDRESS_BOOK_STUFF // Now do the 'Fax' // Name of the One-Off rgsPropValue[ivalootPR_DISPLAY_NAME].ulPropTag = PR_DISPLAY_NAME_A; LoadStringA(hInst, IDS_FAX_NAME, displayNameString, ARRAYSIZE(displayNameString)); rgsPropValue[ivalootPR_DISPLAY_NAME].Value.lpszA = displayNameString; // The entry ID for this template. MAPI will call OpenEntry() with this entry ID // RtlZeroMemory(&EntryID, sizeof(OOUSER_ENTRYID)); rgsPropValue[ivalootPR_ENTRYID].ulPropTag = PR_ENTRYID; rgsPropValue[ivalootPR_ENTRYID].Value.bin.cb = SIZEOF(OOUSER_ENTRYID); rgsPropValue[ivalootPR_ENTRYID].Value.bin.lpb = (LPVOID) &ONEOFF_EID; // the hierarcy level (how far to indent a display name). I choose not to indent rgsPropValue[ivalootPR_DEPTH].ulPropTag = PR_DEPTH; rgsPropValue[ivalootPR_DEPTH].Value.l = 1; // Selection flags. TRUE indicates this entry ID can be used in CreateEntry() call. rgsPropValue[ivalootPR_SELECTABLE].ulPropTag = PR_SELECTABLE; rgsPropValue[ivalootPR_SELECTABLE].Value.b = TRUE; // The address type that would be generated by an entry // created from this template rgsPropValue[ivalootPR_ADDRTYPE].ulPropTag = PR_ADDRTYPE_A; #ifdef UNICODE rgsPropValue[ivalootPR_ADDRTYPE].Value.lpszA = szEMT; #else rgsPropValue[ivalootPR_ADDRTYPE].Value.LPSZ = lpszEMT; #endif /* * The display type associated with a recipient built with this template */ rgsPropValue[ivalootPR_DISPLAY_TYPE].ulPropTag = PR_DISPLAY_TYPE; rgsPropValue[ivalootPR_DISPLAY_TYPE].Value.lpszA = DT_MAILUSER; /* * The instance key of this row in this one-off table. * using 2 for this row */ ulInstanceKey = 2; rgsPropValue[ivalootPR_INSTANCE_KEY].ulPropTag = PR_INSTANCE_KEY; rgsPropValue[ivalootPR_INSTANCE_KEY].Value.bin.cb = SIZEOF(ULONG); rgsPropValue[ivalootPR_INSTANCE_KEY].Value.bin.lpb = (LPBYTE) &ulInstanceKey; (void) lpABPLogon->lpTDatOO->lpVtbl->HrModifyRow( lpABPLogon->lpTDatOO, &sRow); #endif } /* * Get a view to return to the caller */ hResult = lpABPLogon->lpTDatOO->lpVtbl->HrGetView( lpABPLogon->lpTDatOO, NULL, NULL, 0, (LPMAPITABLE *) lppTable); out: LeaveCriticalSection(&lpABPLogon->cs); DebugTraceResult(ABPLogon_GetOneOffTable, hResult); return hResult; } /************************************************************************* * - ABPLOGON_Advise - * NYI * * */ STDMETHODIMP ABPLOGON_Advise( LPABPLOGON lpABPLogon, ULONG cbEntryID, LPENTRYID lpEntryID, ULONG ulEventMask, LPMAPIADVISESINK lpAdviseSink, ULONG FAR * lpulConnection ) { DebugTraceSc(ABPLOGON_Advise, MAPI_E_NO_SUPPORT); return ResultFromScode(MAPI_E_NO_SUPPORT); } /************************************************************************* * - ABPLOGON_Unadvise - * NYI * * */ STDMETHODIMP ABPLOGON_Unadvise(LPABPLOGON lpABPLogon, ULONG ulConnection) { DebugTraceSc(ABPLOGON_Unadvise, MAPI_E_NO_SUPPORT); return ResultFromScode(MAPI_E_NO_SUPPORT); } /************************************************************************* * - ABPLOGON_PrepareRecips - * Takes a list of recipients and sets values for a requested list of * properties (lpPropTagArray) on each of the recipients, from the respective * address book entry of the recipient. If the recipient already has * some properties set on it, those that exist on the address book entry * will override the one that are on the recipient. Those that do NOT * exist in the address book entry will stay. * * */ STDMETHODIMP ABPLOGON_PrepareRecips( LPABPLOGON lpABPLogon, ULONG ulFlags, LPSPropTagArray lpPropTagArray, LPADRLIST lpRecipList ) { HRESULT hResult = hrSuccess; UINT iRecip; UINT iProp; ULONG cValues; LPSPropValue lpspvUser = NULL; LPSPropValue lpNewRecip = NULL; LPMAPIPROP lpMAPIPropEntry = NULL; SCODE sc = S_OK; ULONG ulObjType; BOOL fUselpspvUser; if (!lpPropTagArray) { /* * They only want us to update our entryID from ephemeral to * permanent. Since ours are already permanent, we don't need to * do anything. */ goto out; } /* loop through all the recipients */ for (iRecip = 0; iRecip < lpRecipList->cEntries; iRecip++) { LPUSR_ENTRYID lpEntryID = NULL; ULONG cbEntryID; LPSPropValue lpPropVal = NULL; LPSPropValue rgpropvalsRecip = lpRecipList->aEntries[iRecip].rgPropVals; ULONG cPropsRecip = lpRecipList->aEntries[iRecip].cValues; /* For each recipient, find its entryid */ lpPropVal = PpropFindProp( rgpropvalsRecip, cPropsRecip, PR_ENTRYID ); if ( lpPropVal ) { lpEntryID = (LPUSR_ENTRYID)lpPropVal->Value.bin.lpb; cbEntryID = lpPropVal->Value.bin.cb; } else continue; /* Is it one of ours? */ if ( cbEntryID < CbNewENTRYID(0) || IsBadReadPtr( (LPVOID) lpEntryID, (UINT) cbEntryID ) ) { continue; /* no, keep looking */ } if ( memcmp( &(lpEntryID->muid), &muidABMAWF, SIZEOF(MAPIUID) ) ) continue; /* no, keep looking */ /* Try and open it. */ hResult = HrNewFaxUser( (LPMAILUSER *)&lpMAPIPropEntry, &ulObjType, cbEntryID, (LPENTRYID) lpEntryID, (LPABLOGON) lpABPLogon, NULL, lpABPLogon->hLibrary, lpABPLogon->lpAllocBuff, lpABPLogon->lpAllocMore, lpABPLogon->lpFreeBuff, lpABPLogon->lpMalloc ); if ( HR_FAILED(hResult) ) { /* Couldn't open it...; Ignore it and keep looking */ hResult = hrSuccess; DebugTrace( "ABPLOGON_PrepareRecips sees a bad user entry ID\n" ); continue; } /* Get the properties requested */ hResult = lpMAPIPropEntry->lpVtbl->GetProps( lpMAPIPropEntry, lpPropTagArray, 0, /* ansi */ &cValues, &lpspvUser ); /* No longer need lpMAPIPropEntry */ lpMAPIPropEntry->lpVtbl->Release(lpMAPIPropEntry); lpMAPIPropEntry = NULL; if (HR_FAILED(hResult)) { /* Failed getting properties. Cleanup and ignore this entry */ hResult = hrSuccess; continue; } hResult = hrSuccess; Assert(cValues == lpPropTagArray->cValues); /* * This is the hard part. * Merge the two property sets: lpspvUser and lpsPropVal. Note that * both of these sets may have the same property - chances are they do. * for these conflicts, lpspvUser should be the one we get the property * from. * * Guess how big the resultant SPropValue array is, and allocate one of that * size. */ sc = lpABPLogon->lpAllocBuff( (cValues + cPropsRecip) * SIZEOF( SPropValue ), &lpNewRecip); if (FAILED(sc)) { /* * Ok, to fail the call here. If we're running into out of memory conditions * we're all in trouble. */ hResult = ResultFromScode( sc ); goto err; } /* * Copy lpspvUser properties over to lpNewRecip * Check each property in lpsvUser to ensure that it isn't PT_ERROR, if so * find the propval in rgpropvalsRecip ( the [in] recip prop val array ), * if it exists and use that property. */ for (iProp = 0; iProp < cValues; iProp++) { fUselpspvUser = TRUE; if ( PROP_TYPE( lpspvUser[iProp].ulPropTag ) == PT_ERROR ) { lpPropVal = PpropFindProp( rgpropvalsRecip, cPropsRecip, lpPropTagArray->aulPropTag[iProp] ); if ( lpPropVal ) { sc = PropCopyMore( lpNewRecip + iProp, lpPropVal, lpABPLogon->lpAllocMore, lpNewRecip ); fUselpspvUser = FALSE; } } if ( fUselpspvUser ) { sc = PropCopyMore( lpNewRecip + iProp, lpspvUser + iProp, lpABPLogon->lpAllocMore, lpNewRecip ); } if (FAILED(sc)) { if (sc == MAPI_E_NOT_ENOUGH_MEMORY) { hResult = MakeResult(sc); goto err; } /* * Otherwise we've run into something wierd in the prop value array * like PT_UNSPECIFIED, PT_NULL, or PT_OBJECT. In which case continue * on. */ } } /* Done with lpspvUser */ lpABPLogon->lpFreeBuff( lpspvUser ); lpspvUser = NULL; /* * Copy those properties that aren't already in lpNewRecip * from rgpropvalsRecip. Don't copy over the PT_ERROR prop vals */ for ( iProp = 0; iProp < cPropsRecip; iProp++ ) { if ( PpropFindProp( lpNewRecip, cValues, rgpropvalsRecip[iProp].ulPropTag ) || PROP_TYPE( rgpropvalsRecip[iProp].ulPropTag ) == PT_ERROR ) continue; sc = PropCopyMore( lpNewRecip + cValues, rgpropvalsRecip + iProp, lpABPLogon->lpAllocMore, lpNewRecip ); if ( FAILED( sc ) ) { if (sc == MAPI_E_NOT_ENOUGH_MEMORY) { hResult = ResultFromScode( sc ); goto err; } /* * Otherwise we've run into something wierd in the prop value array * like PT_UNSPECIFIED, PT_NULL, or PT_OBJECT. In which case continue * on. */ } cValues++; } /* * Replace the AdrEntry in the AdrList with this new lpNewRecip. And * don't forget the cValues! */ lpRecipList->aEntries[iRecip].rgPropVals = lpNewRecip; lpRecipList->aEntries[iRecip].cValues = cValues; /* Finally, free up the old AdrEntry. */ lpABPLogon->lpFreeBuff( rgpropvalsRecip ); } out: DebugTraceResult( ABPLOGON_PrepareRecips, hResult ); return hResult; err: lpABPLogon->lpFreeBuff( lpspvUser ); goto out; } /************************************************************************* * LpMuidFromLogon - * Returns the particular ABPLOGON object's unique identifier. * */ LPMAPIUID LpMuidFromLogon(LPABLOGON lpABLogon) { LPABPLOGON lpABPLogon = (LPABPLOGON) lpABLogon; AssertSz(!IsBadReadPtr(lpABPLogon, SIZEOF(ABPLOGON)), "Bad logon object!\n"); return (&(lpABPLogon->muidID)); } /************************************************************************* * HrLpszGetCurrentFileName - * Returns a copy of the current .FAB file pointed to by this logon object. * */ HRESULT HrLpszGetCurrentFileName(LPABLOGON lpABLogon, LPTSTR * lppszFileName) { LPABPLOGON lpABPLogon = (LPABPLOGON) lpABLogon; SCODE sc; HRESULT hResult = hrSuccess; AssertSz(!IsBadReadPtr(lpABPLogon, SIZEOF(ABPLOGON)), "FAB: Bad logon object!\n"); AssertSz(!IsBadWritePtr(lppszFileName, SIZEOF(LPTSTR)), "FAB: Bad dest string!\n"); EnterCriticalSection(&lpABPLogon->cs); sc = lpABPLogon->lpAllocBuff( (lstrlen(lpABPLogon->lpszFileName)+1)*SIZEOF(TCHAR), lppszFileName); if (FAILED(sc)) { hResult = ResultFromScode(sc); goto ret; } lstrcpy( *lppszFileName, lpABPLogon->lpszFileName); ret: LeaveCriticalSection(&lpABPLogon->cs); DebugTraceResult(HrLpszGetCurrentFileName, hResult); return hResult; } /* * HrReplaceCurrentFileName - * Replaces the current file name associated with this logon object and tries * to save it all away in the profile. */ HRESULT HrReplaceCurrentFileName(LPABLOGON lpABLogon, LPTSTR lpszNewFile) { LPABPLOGON lpABPLogon = (LPABPLOGON) lpABLogon; HRESULT hResult = hrSuccess; LPPROFSECT lpProfSect = NULL; LPTSTR lpstrT; SCODE sc; SPropValue rgspv[1]; #ifdef UNICODE CHAR szAnsiFileName[ MAX_PATH ]; #endif AssertSz(!IsBadReadPtr(lpABPLogon, SIZEOF(ABPLOGON)), "Bad logon object!\n"); EnterCriticalSection(&lpABPLogon->cs); /* * FAB file name has changed have to update profile and objects */ if (lstrcmp(lpszNewFile, lpABPLogon->lpszFileName)) { /* * Open the private profile section... */ hResult = lpABPLogon->lpMapiSup->lpVtbl->OpenProfileSection( lpABPLogon->lpMapiSup, NULL, MAPI_MODIFY, &lpProfSect); if (HR_FAILED(hResult)) { /* * Shouldn't get here, but in case I do, just... */ goto ret; } /* * Save the new name back into the profile */ rgspv[0].ulPropTag = PR_FAB_FILE_A; #ifdef UNICODE szAnsiFileName[0] = 0; WideCharToMultiByte( CP_ACP, 0, lpszNewFile, -1, szAnsiFileName, ARRAYSIZE(szAnsiFileName), NULL, NULL ); rgspv[0].Value.lpszA = szAnsiFileName; #else rgspv[0].Value.LPSZ = lpszNewFile; #endif /* * Don't care if I can save it in the profile or not. * Saving it's a nice to have, but absolutely required * for operation of this particular provider. */ (void) lpProfSect->lpVtbl->SetProps( lpProfSect, 1, // ansi rgspv, NULL); lpProfSect->lpVtbl->Release(lpProfSect); /* * Allocate and copy this new one */ sc = lpABPLogon->lpAllocBuff( (lstrlen(lpszNewFile)+1)*SIZEOF(TCHAR), &lpstrT); if (FAILED(sc)) { hResult = ResultFromScode(sc); goto ret; } lstrcpy( lpstrT, lpszNewFile ); /* * Free up the old one... */ lpABPLogon->lpFreeBuff(lpABPLogon->lpszFileName); /* * Put in the new one. */ lpABPLogon->lpszFileName = lpstrT; /* * Update the hierarchy table */ hResult = HrBuildRootHier((LPABLOGON)lpABPLogon, NULL); } ret: LeaveCriticalSection(&lpABPLogon->cs); DebugTraceResult(HrReplaceCurrentFileName, hResult); return hResult; } /* * GenerateContainerDN - * Common code for generating the display name of the single * container exposed from this logon object. */ #ifdef SAB // from sample AB void GenerateContainerDN(LPABLOGON lpABLogon, LPTSTR lpszName) { LPABPLOGON lpABPLogon = (LPABPLOGON) lpABLogon; LPTSTR lpszFileName; int ich; AssertSz(!IsBadReadPtr(lpABPLogon, SIZEOF(ABPLOGON)), "Bad logon object!\n"); EnterCriticalSection(&lpABPLogon->cs); lpszFileName = lpABPLogon->lpszFileName; // get the filename without the path for (ich = lstrlen(lpszFileName) - 1; ich >= 0; ich--) { if (lpszFileName[ich] == TEXT('\\')) break; } // skip past the backslash ich++; wsprintf(lpszName, TEXT("FAB using %s"), lpszFileName + ich); LeaveCriticalSection(&lpABPLogon->cs); } #else // Fax AB void GenerateContainerDN(HINSTANCE hInst, LPTSTR lpszName) { LoadString (hInst, IDS_ADDRESS_BOOK_ROOT_CONT, lpszName, MAX_DISPLAY_NAME); } #endif /* - HrBuildRootHier - * * Builds up the root hierarchy for the Microsoft At Work Fax Address Book. * * */ enum { ivalPR_DISPLAY_NAME = 0, ivalPR_ENTRYID, ivalPR_DEPTH, ivalPR_OBJECT_TYPE, ivalPR_DISPLAY_TYPE, ivalPR_CONTAINER_FLAGS, ivalPR_INSTANCE_KEY, ivalPR_AB_PROVIDER_ID, cvalMax }; static const SizedSPropTagArray(cvalMax, tagaRootColSet) = { cvalMax, { PR_DISPLAY_NAME_A, PR_ENTRYID, PR_DEPTH, PR_OBJECT_TYPE, PR_DISPLAY_TYPE, PR_CONTAINER_FLAGS, PR_INSTANCE_KEY, PR_AB_PROVIDER_ID } }; HRESULT HrBuildRootHier(LPABLOGON lpABLogon, LPMAPITABLE * lppMAPITable) { HRESULT hResult; SCODE sc; SRow sRow; SPropValue rgsPropValue[cvalMax]; ULONG ulInstanceKey = 1; TCHAR szBuf[MAX_PATH]; #ifdef UNICODE CHAR szAnsiBuf[MAX_PATH]; #endif HINSTANCE hInst; LPABPLOGON lpABPLogon = (LPABPLOGON) lpABLogon; DIR_ENTRYID eidRoot = { {0, 0, 0, 0}, MUIDABMAWF, MAWF_VERSION, MAWF_DIRECTORY }; EnterCriticalSection(&lpABPLogon->cs); hInst = lpABPLogon->hLibrary; /* * See if we have a TaD yet */ if (!lpABPLogon->lpTDatRoot) { /* Create a Table Data object */ if ( sc = CreateTable((LPIID) &IID_IMAPITableData, lpABPLogon->lpAllocBuff, lpABPLogon->lpAllocMore, lpABPLogon->lpFreeBuff, lpABPLogon->lpMalloc, 0, PR_ENTRYID, (LPSPropTagArray) &tagaRootColSet, &(lpABPLogon->lpTDatRoot)) ) { hResult = ResultFromScode(sc); goto out; } } /* Constants */ sRow.cValues = cvalMax; sRow.lpProps = rgsPropValue; /* First, the Display Name stuff*/ rgsPropValue[ivalPR_DISPLAY_NAME].ulPropTag = PR_DISPLAY_NAME_A; #ifdef SAB GenerateContainerDN((LPABLOGON) lpABPLogon, szBuf); #else GenerateContainerDN(hInst, szBuf); #endif #ifdef UNICODE szAnsiBuf[0] = 0; WideCharToMultiByte( CP_ACP, 0, szBuf, -1, szAnsiBuf, ARRAYSIZE(szAnsiBuf), NULL, NULL ); rgsPropValue[ivalPR_DISPLAY_NAME].Value.lpszA = szAnsiBuf; #else rgsPropValue[ivalPR_DISPLAY_NAME].Value.lpszA = szBuf; #endif /* * For each FAB logon object associated with it's init object, * we have a unique MAPIUID. It's the only thing that distinguishes * one FAB entryid from another in the merged hierarchy table that * MAPI generates. */ rgsPropValue[ivalPR_ENTRYID].ulPropTag = PR_ENTRYID; eidRoot.muidID = lpABPLogon->muidID; rgsPropValue[ivalPR_ENTRYID].Value.bin.cb = SIZEOF(DIR_ENTRYID); rgsPropValue[ivalPR_ENTRYID].Value.bin.lpb = (LPVOID) &eidRoot; rgsPropValue[ivalPR_DEPTH].ulPropTag = PR_DEPTH; rgsPropValue[ivalPR_DEPTH].Value.l = 0; rgsPropValue[ivalPR_OBJECT_TYPE].ulPropTag = PR_OBJECT_TYPE; rgsPropValue[ivalPR_OBJECT_TYPE].Value.l = MAPI_ABCONT; rgsPropValue[ivalPR_DISPLAY_TYPE].ulPropTag = PR_DISPLAY_TYPE; rgsPropValue[ivalPR_DISPLAY_TYPE].Value.l = DT_NOT_SPECIFIC; rgsPropValue[ivalPR_CONTAINER_FLAGS].ulPropTag = PR_CONTAINER_FLAGS; rgsPropValue[ivalPR_CONTAINER_FLAGS].Value.l = AB_RECIPIENTS | AB_UNMODIFIABLE; rgsPropValue[ivalPR_INSTANCE_KEY].ulPropTag = PR_INSTANCE_KEY; rgsPropValue[ivalPR_INSTANCE_KEY].Value.bin.cb = SIZEOF(ULONG); rgsPropValue[ivalPR_INSTANCE_KEY].Value.bin.lpb = (LPBYTE) &ulInstanceKey; rgsPropValue[ivalPR_AB_PROVIDER_ID].ulPropTag = PR_AB_PROVIDER_ID; rgsPropValue[ivalPR_AB_PROVIDER_ID].Value.bin.cb = SIZEOF(MAPIUID); rgsPropValue[ivalPR_AB_PROVIDER_ID].Value.bin.lpb = (LPBYTE) &muidABMAWF; hResult = lpABPLogon->lpTDatRoot->lpVtbl->HrModifyRow( lpABPLogon->lpTDatRoot, &sRow ); if (HR_FAILED(hResult)) goto out; /* * Check to see if they want a view returned as well */ if (lppMAPITable) { /* Get a view from the Table data object */ hResult = lpABPLogon->lpTDatRoot->lpVtbl->HrGetView( lpABPLogon->lpTDatRoot, NULL, NULL, 0, lppMAPITable); } out: LeaveCriticalSection(&lpABPLogon->cs); DebugTraceResult(HrBuildRootHier, hResult); return hResult; } /* * Checks to see if the file passed in is still the actual file that * should be browsed. */ BOOL FEqualFABFiles( LPABLOGON lpABLogon, LPTSTR lpszFileName) { LPABPLOGON lpABPLogon = (LPABPLOGON) lpABLogon; BOOL fEqual; AssertSz(!IsBadReadPtr(lpABPLogon, SIZEOF(ABPLOGON)), "Bad logon object!\n"); EnterCriticalSection(&lpABPLogon->cs); fEqual = !lstrcmp( lpszFileName, lpABPLogon->lpszFileName ); LeaveCriticalSection(&lpABPLogon->cs); return fEqual; }