// badata.cpp: implementation of the CAddressBook class. // WAB & Messenger integration to OE // Created 04/20/98 by YST // ////////////////////////////////////////////////////////////////////// #include "pch.hxx" #include #include "msoert.h" #include "bactrl.h" #include "badata.h" #include "baui.h" #include "baprop.h" #include "shlwapi.h" #include "ourguid.h" #include "mapiguid.h" #include "shlwapip.h" // #include "ipab.h" // #include "multiusr.h" // #include "demand.h" // #include "secutil.h" #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif #define WAB_USE_OUTLOOK_CONTACT_STORE 0x10000000// Note: This internal flag needs to be // harmonious with external flags defined // in wabapi.h for WAB_PARAM structs ////////////////////////////////////////////////////////////////////// // Defines for the various MAPI Tables we request from the WAB ////////////////////////////////////////////////////////////////////// #define MAX_QUERY_SIZE 1000 // Here are some private properties that the WAB stores #define WAB_INTERNAL_BASE 0x6600 #define PR_WAB_LDAP_SERVER PROP_TAG(PT_TSTRING, WAB_INTERNAL_BASE + 1) #define PR_WAB_RESOLVE_FLAG PROP_TAG(PT_BOOLEAN, WAB_INTERNAL_BASE + 2) // This BA's private GUID: // {2BAD7EE0-36AB-11d1-9BAC-00A0C91F9C8B} static const GUID WAB_ExtBAGuid = { 0x2bad7ee0, 0x36ab, 0x11d1, { 0x9b, 0xac, 0x0, 0xa0, 0xc9, 0x1f, 0x9c, 0x8b } }; static const LPTSTR lpMsgrPropNames[] = { "MsgrID" }; enum _MsgrTags { MsgrID = 0, msgrMax }; ULONG MsgrPropTags[msgrMax]; ULONG PR_MSGR_DEF_ID = 0; // These two define the table we request from the WAB when showing the // contents of the local address book. enum { ieidPR_DISPLAY_NAME = 0, ieidPR_ENTRYID, ieidPR_OBJECT_TYPE, ieidPR_MSGR_DEF_ID, ieidPR_HOME_TELEPHONE_NUMBER, ieidPR_BUSINESS_TELEPHONE_NUMBER, ieidPR_MOBILE_TELEPHONE_NUMBER, // ieidPR_WAB_IPPHONE, ieidPR_EMAIL_ADDRESS, ieidPR_USER_X509_CERTIFICATE, ieidPR_RECORD_KEY, ieidMax }; static SizedSPropTagArray(ieidMax, ptaEid)= { ieidMax, { PR_DISPLAY_NAME_W, PR_ENTRYID, PR_OBJECT_TYPE, 0, PR_HOME_TELEPHONE_NUMBER_W, PR_BUSINESS_TELEPHONE_NUMBER_W, PR_MOBILE_TELEPHONE_NUMBER_W, // PR_WAB_IPPHONE, PR_EMAIL_ADDRESS_W, PR_USER_X509_CERTIFICATE, PR_RECORD_KEY } }; // These two define the table we request to see which LDAP servers should // be resolved against. enum { irnPR_OBJECT_TYPE = 0, irnPR_WAB_RESOLVE_FLAG, irnPR_ENTRYID, irnPR_DISPLAY_NAME, irnMax }; static const SizedSPropTagArray(irnMax, irnColumns) = { irnMax, { PR_OBJECT_TYPE, PR_WAB_RESOLVE_FLAG, PR_ENTRYID, PR_DISPLAY_NAME_W, } }; enum { icrPR_DEF_CREATE_MAILUSER = 0, icrPR_DEF_CREATE_DL, icrMax }; const SizedSPropTagArray(icrMax, ptaCreate)= { icrMax, { PR_DEF_CREATE_MAILUSER, PR_DEF_CREATE_DL, } }; // -------------------------------------------------------------------------- // PszAllocW // -------------------------------------------------------------------------- LPWSTR IEPszAllocW(INT nLen) { // Locals LPWSTR pwsz=NULL; // Empty ? if (nLen == 0) goto exit; // Allocate if (FAILED(HrAlloc((LPVOID *)&pwsz, (nLen + 1) * sizeof (WCHAR)))) goto exit; exit: // Done return pwsz; } // -------------------------------------------------------------------------- // IEPszToUnicode // -------------------------------------------------------------------------- LPWSTR IEPszToUnicode(UINT cp, LPCSTR pcszSource) { // Locals INT cchNarrow, cchWide; LPWSTR pwszDup=NULL; // No Source if (pcszSource == NULL) goto exit; // Length cchNarrow = lstrlenA(pcszSource) + 1; // Determine how much space is needed for translated widechar cchWide = MultiByteToWideChar(cp, MB_PRECOMPOSED, pcszSource, cchNarrow, NULL, 0); // Error if (cchWide == 0) goto exit; // Alloc temp buffer pwszDup = IEPszAllocW(cchWide + 1); if (!pwszDup) goto exit; // Do the actual translation cchWide = MultiByteToWideChar(cp, MB_PRECOMPOSED, pcszSource, cchNarrow, pwszDup, cchWide+1); // Error if (cchWide == 0) { SafeMemFree(pwszDup); goto exit; } exit: // Done return pwszDup; } ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// CAddressBookData::CAddressBookData() { m_pWABObject = NULL; m_hInstWAB = NULL; m_pAdrBook = NULL; m_ulConnection = 0; } CAddressBookData::~CAddressBookData() { // Release the Address Book pointer SafeRelease(m_pAdrBook); // Release the WAB object SafeRelease(m_pWABObject); if (m_hInstWAB) { FreeLibrary(m_hInstWAB); m_hInstWAB = NULL; } } // // FUNCTION: CAddressBookData::OpenWabFile() // // PURPOSE: Finds the WAB DLL, loads the DLL, and opens the WAB. // HRESULT CAddressBookData::OpenWabFile(BOOL fWAB) { TCHAR szDll[MAX_PATH]; TCHAR szExpanded[MAX_PATH]; DWORD dwType = 0; LPTSTR psz = szDll; ULONG cbData = sizeof(szDll); HKEY hKey = NULL; HRESULT hr = E_FAIL; LPWABOPEN lpfnWABOpen; // Initialize the path string *szDll = '\0'; // First look under the default WAB DLL path location in the Registry. // WAB_DLL_PATH_KEY is defined in wabapi.h if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, WAB_DLL_PATH_KEY, 0, KEY_READ, &hKey)) { if (ERROR_SUCCESS == RegQueryValueEx(hKey, _T(""), NULL, &dwType, (LPBYTE) szDll, &cbData)) if (REG_EXPAND_SZ == dwType) { ExpandEnvironmentStrings(szDll, szExpanded, ARRAYSIZE(szExpanded)); psz = szExpanded; } RegCloseKey(hKey); } // If the registry thing came up blank, then do a LoadLibrary on wab32.dll m_hInstWAB = LoadLibrary((lstrlen(psz)) ? (LPCTSTR) psz : (LPCTSTR) WAB_DLL_NAME); if (m_hInstWAB) { // If we've got the DLL, then get the entry point lpfnWABOpen = (LPWABOPEN) GetProcAddress(m_hInstWAB, "WABOpen"); if (lpfnWABOpen) { WAB_PARAM wp = {0}; wp.cbSize = sizeof(WAB_PARAM); wp.guidPSExt = CLSID_IEMsgAb; wp.ulFlags = WAB_ENABLE_PROFILES | WAB_USE_OE_SENDMAIL; if(!fWAB) { wp.ulFlags = wp.ulFlags | WAB_USE_OUTLOOK_CONTACT_STORE; hr = lpfnWABOpen(&m_pAdrBook, &m_pWABObject, &wp, 0); if(hr == S_OK) return (hr); // in error case, try to open WAB else wp.ulFlags = WAB_ENABLE_PROFILES | WAB_USE_OE_SENDMAIL; } hr = lpfnWABOpen(&m_pAdrBook, &m_pWABObject, &wp, 0); } } if(!m_pAdrBook || !m_pWABObject || FAILED(hr)) { WCHAR wszTitle[CCHMAX_STRINGRES]; WCHAR wszMsg[CCHMAX_STRINGRES]; if(!AthLoadString(idsAthena, wszTitle, ARRAYSIZE(wszTitle))) wszTitle[0] = L'\0'; if(!AthLoadString(idsWabNotFound, wszMsg, ARRAYSIZE(wszMsg))) wszMsg[0] = L'\0'; MessageBoxW(NULL, wszMsg, wszTitle, MB_OK | MB_ICONSTOP); } return (hr); } // // FUNCTION: CAddressBookData::LoadWabContents() // // PURPOSE: Loads the contents of the WAB into the provided ListView control. // // PARAMETERS: // [in] ctlList - Pointer to the ListView control to load the WAB into. // HRESULT CAddressBookData::LoadWabContents(CContainedWindow& ctlList, CIEMsgAb *pSink) { ULONG ulObjType = 0; LPMAPITABLE lpAB = NULL; LPTSTR *lppszArray = NULL; ULONG cRows = 0; LPSRowSet lpRow = NULL; LPSRowSet lpRowAB = NULL; LPABCONT lpContainer = NULL; int cNumRows = 0; int nRows = 0; HRESULT hr = E_FAIL; ULONG lpcbEID; LPENTRYID lpEID = NULL; LPSRowSet pSRowSet = NULL; Assert(m_pAdrBook); if (!m_pAdrBook) return E_UNEXPECTED; // Get the entryid of the root PAB container hr = m_pAdrBook->GetPAB(&lpcbEID, &lpEID); if(!PR_MSGR_DEF_ID) InitNamedProps(); // Open the root PAB container. This is where all the WAB contents reside. ulObjType = 0; hr = m_pAdrBook->OpenEntry(lpcbEID, (LPENTRYID)lpEID, NULL, 0, &ulObjType, (LPUNKNOWN *) &lpContainer); if(HR_FAILED(hr)) goto exit; if(pSink) m_pAB = pSink; #ifdef LATER if (pSink && !m_ulConnection) m_pAdrBook->Advise(lpcbEID, lpEID, fnevObjectModified, (IMAPIAdviseSink *) pSink, &m_ulConnection); #endif // Get a contents table of all the contents in the WABs root container. hr = lpContainer->GetContentsTable(WAB_PROFILE_CONTENTS | MAPI_UNICODE, &lpAB); if(HR_FAILED(hr)) goto exit; // Order the columns in the ContentsTable to conform to the ones we want // - which are mainly DisplayName, EntryID and ObjectType. The table is // guaranteed to set the columns in the order requested. Assert(PR_MSGR_DEF_ID); ptaEid.aulPropTag[ieidPR_MSGR_DEF_ID] = PR_MSGR_DEF_ID; hr = lpAB->SetColumns((LPSPropTagArray) &ptaEid, 0); if(HR_FAILED(hr)) goto exit; // Reset to the beginning of the table hr = lpAB->SeekRow(BOOKMARK_BEGINNING, 0, NULL); if(HR_FAILED(hr)) goto exit; // If we got this far, we have a populated table. We can query the rows // now. hr = _QueryAllRows(lpAB, NULL, NULL, NULL, MAX_QUERY_SIZE, &pSRowSet); if (FAILED(hr) || !pSRowSet) goto exit; // Fill the provided ListView with this table _FillListFromTable(ctlList, pSRowSet); exit: if (lpEID) m_pWABObject->FreeBuffer(lpEID); if (pSRowSet) _FreeProws(pSRowSet); if (lpContainer) lpContainer->Release(); if (lpAB) lpAB->Release(); return hr; } HRESULT CAddressBookData::DoLDAPSearch(LPTSTR pszText, CContainedWindow& ctlList) { // Build a restriction based on the given text SRestriction SRes; if (SUCCEEDED(_GetLDAPSearchRestriction(pszText, &SRes))) { // Figure out what the entry ID is for the LDAP container ULONG cbEntryID = 0; LPENTRYID pEntryID = 0; if (SUCCEEDED(_GetLDAPContainer(&cbEntryID, &pEntryID))) { // Perform the search SORT_INFO si = {0, 0}; _GetLDAPContentsList(cbEntryID, pEntryID, si, &SRes, ctlList); if (pEntryID) m_pWABObject->FreeBuffer(pEntryID); } if (SRes.res.resAnd.lpRes) m_pWABObject->FreeBuffer(SRes.res.resAnd.lpRes); } return (S_OK); } void CAddressBookData::_FreeProws(LPSRowSet prows) { if (prows) { for (ULONG irow = 0; irow < prows->cRows; ++irow) m_pWABObject->FreeBuffer(prows->aRow[irow].lpProps); m_pWABObject->FreeBuffer(prows); } } // // FUNCTION: CAddressBookData::_MergeRowSets() // // PURPOSE: Merges prows with *pprowsDst, reallocating *pprowsDst if // necessary. Destroys the container portion of prows (but not // the individual rows it contains). // // PARAMETERS: // [in] prows - source set of rows // [in, out] *pprowsDst - set of rows to merge the prows into // // RETURN VALUE: // HRESULT // HRESULT CAddressBookData::_MergeRowSets(LPSRowSet prows, LPSRowSet FAR *pprowsDst) { SCODE sc = S_OK; LPSRowSet prowsT; UINT crowsSrc; UINT crowsDst; _ASSERTE(!IsBadWritePtr(pprowsDst, sizeof(LPSRowSet))); _ASSERTE(prows); // If the table is completely empty we want to return this. if (!*pprowsDst || (*pprowsDst)->cRows == 0) { m_pWABObject->FreeBuffer(*pprowsDst); *pprowsDst = prows; prows = NULL; // Don't free it! goto exit; } if (prows->cRows == 0) { goto exit; } // OK, now we know there are rows in both rowsets, we have to do a real // merge. crowsSrc = (UINT) prows->cRows; crowsDst = (UINT) (*pprowsDst)->cRows; // handle 0 if (FAILED(sc = m_pWABObject->AllocateBuffer(CbNewSRowSet(crowsSrc + crowsDst), (LPVOID*) &prowsT))) goto exit; if (crowsDst) CopyMemory(prowsT->aRow, (*pprowsDst)->aRow, crowsDst * sizeof(SRow)); CopyMemory(&prowsT->aRow[crowsDst], prows->aRow, crowsSrc * sizeof(SRow)); prowsT->cRows = crowsSrc + crowsDst; m_pWABObject->FreeBuffer(*pprowsDst); *pprowsDst = prowsT; exit: // if (prows) // m_pWABObject->FreeBuffer(&prows); return ResultFromScode(sc); } /* - HrQueryAllRows - * Purpose: * Retrieves all rows from an IMAPITable interface up to a set * maximum. It will optionally set the column set, sort order, * and restriction on the table before querying. * * If the table is empty, an SRowSet with zero rows is * returned (just like QueryRows). * * The seek position of the table is undefined both before and * after this call. * * If the function fails with an error other than * MAPI_E_NOT_ENOUGH_MEMORY, extended error information is * available through the table interface. * * Arguments: * ptable in the table interface to query * ptaga in if not NULL, column set for the table * pres in if not NULL, restriction to be applied * psos in if not NULL, sort order to be applied * crowsMax in if nonzero, limits the number of rows * to be returned. * pprows out all rows of the table * * Returns: * HRESULT. Extended error information normally is in the * table. * * Side effects: * Seek position of table is undefined. * * Errors: * MAPI_E_TABLE_TOO_BIG if the table contains more than * cRowsMax rows. */ HRESULT CAddressBookData::_QueryAllRows(LPMAPITABLE ptable, LPSPropTagArray ptaga, LPSRestriction pres, LPSSortOrderSet psos, LONG crowsMax, LPSRowSet FAR *pprows) { HRESULT hr; LPSRowSet prows = NULL; UINT crows = 0; LPSRowSet prowsT=NULL; UINT crowsT; *pprows = NULL; // Set up the table, if the corresponding setup parameter is present. if (ptaga && FAILED(hr = ptable->SetColumns(ptaga, TBL_BATCH))) goto exit; if (pres && FAILED(hr = ptable->Restrict(pres, TBL_BATCH))) goto exit; if (psos && FAILED(hr = ptable->SortTable(psos, TBL_BATCH))) goto exit; // Set position to beginning of the table. if (FAILED(hr = ptable->SeekRow(BOOKMARK_BEGINNING, 0, NULL))) goto exit; if (crowsMax == 0) crowsMax = 0xffffffff; for (;;) { prowsT = NULL; // Retrieve some rows. Ask for the limit. hr = ptable->QueryRows(crowsMax, 0, &prowsT); if (FAILED(hr)) { // Note: the failure may actually have happened during one of the // setup calls, since we set TBL_BATCH. goto exit; } _ASSERTE(prowsT->cRows <= 0xFFFFFFFF); crowsT = (UINT) prowsT->cRows; // Did we get more rows than caller can handle? if ((LONG) (crowsT + (prows ? prows->cRows : 0)) > crowsMax) { hr = ResultFromScode(MAPI_E_TABLE_TOO_BIG); //_FreeProws(prowsT); goto exit; } // Add the rows just retrieved into the set we're building. // // NOTE: this handles boundary conditions including either row set is // empty. // // NOTE: the merge destroys prowsT. if (FAILED(hr = _MergeRowSets(prowsT, &prows))) goto exit; // Did we hit the end of the table? Unfortunately, we have to ask twice // before we know. if (crowsT == 0) break; } if(prows->cRows != 0) *pprows = prows; exit: if (prowsT) _FreeProws(prowsT); if (FAILED(hr)) _FreeProws(prows); return hr; } HRESULT CAddressBookData::_GetLDAPContainer(ULONG *pcbEntryID, LPENTRYID *ppEntryID) { ULONG ulObjectType = 0; IABContainer *pRoot = 0; LPMAPITABLE pRootTable = 0; HRESULT hr = S_OK; // Get the root Address Book container hr = m_pAdrBook->OpenEntry(0, NULL, NULL, 0, &ulObjectType, (LPUNKNOWN *) &pRoot); if (FAILED(hr)) goto exit; // From the address book container, get a table of it's contents hr = pRoot->GetContentsTable(0, &pRootTable); if (FAILED(hr)) goto exit; // Set the columns pRootTable->SetColumns((LPSPropTagArray) &irnColumns, 0); // Build a restriction to only display LDAP servers that we're supposed // to resolve against. SRestriction resAnd[2]; // 0 = LDAP, 1 = ResolveFlag SRestriction resLDAPResolve; SPropValue ResolveFlag; // 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; // Apply the restruction hr = pRootTable->Restrict(&resLDAPResolve, 0); if (HR_FAILED(hr)) goto exit; // We're going to just blindly grab the first item in this table as the // LDAP container we're going to use. LPSRowSet pRowSet; hr = pRootTable->QueryRows(1, 0, &pRowSet); if (FAILED(hr)) goto exit; // Grab the size of the entry id *pcbEntryID = pRowSet->aRow[0].lpProps[irnPR_ENTRYID].Value.bin.cb; // Make a copy of the entry id hr = m_pWABObject->AllocateBuffer(*pcbEntryID, (LPVOID *) ppEntryID); if (FAILED(hr)) goto exit; CopyMemory(*ppEntryID, pRowSet->aRow[0].lpProps[irnPR_ENTRYID].Value.bin.lpb, *pcbEntryID); exit: if (pRootTable) pRootTable->Release(); if (pRoot) pRoot->Release(); if (pRowSet) _FreeProws(pRowSet); return (hr); } HRESULT CAddressBookData::_GetLDAPSearchRestriction(LPTSTR pszText, LPSRestriction lpSRes) { SRestriction SRes = { 0 }; LPSRestriction lpPropRes = NULL; ULONG ulcPropCount = 0; HRESULT hr = E_FAIL; ULONG i = 0; SCODE sc = ERROR_SUCCESS; LPSPropValue lpPropArray = NULL; if (!lstrlen(pszText)) { ATLTRACE(_T("No Search Props")); goto exit; } lpSRes->rt = RES_AND; lpSRes->res.resAnd.cRes = 1; // Allocate a buffer for the restriction lpSRes->res.resAnd.lpRes = NULL; sc = m_pWABObject->AllocateBuffer(1 * sizeof(SRestriction), (LPVOID *) &(lpSRes->res.resAnd.lpRes)); if (S_OK != sc || !(lpSRes->res.resAnd.lpRes)) { ATLTRACE("MAPIAllocateBuffer Failed"); hr = MAPI_E_NOT_ENOUGH_MEMORY; goto exit; } lpPropRes = lpSRes->res.resAnd.lpRes; // Create the first part of the OR clause lpPropRes[0].rt = RES_PROPERTY; lpPropRes[0].res.resProperty.relop = RELOP_EQ; lpPropRes[0].res.resProperty.ulPropTag = PR_EMAIL_ADDRESS; lpPropRes[0].res.resProperty.lpProp = NULL; m_pWABObject->AllocateMore(sizeof(SPropValue), lpPropRes, (LPVOID*) &(lpPropRes[0].res.resProperty.lpProp)); lpPropArray = lpPropRes[0].res.resProperty.lpProp; if (!lpPropArray) { ATLTRACE("MAPIAllocateBuffer Failed"); hr = MAPI_E_NOT_ENOUGH_MEMORY; goto exit; } lpPropArray->ulPropTag = PR_EMAIL_ADDRESS; lpPropArray->Value.LPSZ = NULL; ULONG cchSize = lstrlen(pszText) + 1; m_pWABObject->AllocateMore(cchSize * sizeof(TCHAR), lpPropRes, (LPVOID *) &(lpPropArray->Value.LPSZ)); if (!lpPropArray->Value.LPSZ) { ATLTRACE("MAPIAllocateBuffer Failed"); hr = MAPI_E_NOT_ENOUGH_MEMORY; goto exit; } StrCpyN(lpPropArray->Value.LPSZ, pszText, cchSize); hr = S_OK; exit: return hr; } HRESULT CAddressBookData::_GetLDAPContentsList(ULONG cbContainerEID, LPENTRYID pContainerEID, SORT_INFO rSortInfo, LPSRestriction pPropRes, CContainedWindow& ctlList) { HRESULT hr = S_OK; HRESULT hrTemp; ULONG ulObjectType = 0; LPMAPICONTAINER pContainer = NULL; LPMAPITABLE pContentsTable = NULL; LPSRowSet pSRowSet = NULL; // Open the container object corresponding to pContainerEID hr = m_pAdrBook->OpenEntry(cbContainerEID, pContainerEID, NULL, 0, &ulObjectType, (LPUNKNOWN *) &pContainer); if (FAILED(hr)) goto exit; // Get the contents table from this container hr = pContainer->GetContentsTable(0, &pContentsTable); if (FAILED(hr)) goto exit; // Order the columns in the ContentsTable to conform to the ones we want // - which are mainly DisplayName, EntryID and ObjectType. The table is // guaranteed to set the columns in the order requested. Assert(PR_MSGR_DEF_ID); ptaEid.aulPropTag[ieidPR_MSGR_DEF_ID] = PR_MSGR_DEF_ID; hr = pContentsTable->SetColumns((LPSPropTagArray) &ptaEid, 0); if(HR_FAILED(hr)) goto exit; // Do the find hr = pContentsTable->FindRow(pPropRes, BOOKMARK_BEGINNING, 0); if (FAILED(hr)) goto exit; // If this was a partial completion error, we want to continue but also // return this information to the caller if (MAPI_W_PARTIAL_COMPLETION == hr) hrTemp = hr; // If we got this far, we have a populated table. We can query the rows // now. hr = _QueryAllRows(pContentsTable, NULL, NULL, NULL, MAX_QUERY_SIZE, &pSRowSet); if (FAILED(hr) || !pSRowSet) goto exit; // Fill in the ListView from the table _FillListFromTable(ctlList, pSRowSet, TRUE); exit: if (pSRowSet) _FreeProws(pSRowSet); if (pContentsTable) pContentsTable->Release(); if (pContainer) pContainer->Release(); return (hr); } HRESULT CAddressBookData::_FillListFromTable(CContainedWindow& ctlList, LPSRowSet pSRowSet, BOOL bLDAP) { LPSBinary lpSB; PNONEENTRIES Phones; // Make sure the caller passed us a rowset Assert(pSRowSet); Assert(PR_MSGR_DEF_ID); // Loop through the rows in the rowset for (ULONG i = 0; i < pSRowSet->cRows; i++) { // Get the information out of the table that we need. Right now we're // grabbing the entry ID, the display name, and the business phone number. LPENTRYID lpEID = (LPENTRYID) pSRowSet->aRow[i].lpProps[ieidPR_ENTRYID].Value.bin.lpb; ULONG cbEID = pSRowSet->aRow[i].lpProps[ieidPR_ENTRYID].Value.bin.cb; DWORD nFlag = (pSRowSet->aRow[i].lpProps[ieidPR_OBJECT_TYPE].Value.l == MAPI_DISTLIST) ? MAB_GROUP : MAB_CONTACT; LPWSTR lpwszID = NULL; LPWSTR lpwszName = NULL; if(nFlag == MAB_CONTACT) { if(pSRowSet->aRow[i].lpProps[ieidPR_USER_X509_CERTIFICATE].ulPropTag == PR_USER_X509_CERTIFICATE) nFlag |= MAB_CERT; } if(PROP_TYPE(pSRowSet->aRow[i].lpProps[ieidPR_MSGR_DEF_ID/*ieidPR_EMAIL_ADDRESS*/].ulPropTag) == PT_UNICODE ) { nFlag = nFlag | MAB_BUDDY; lpwszID = pSRowSet->aRow[i].lpProps[ieidPR_MSGR_DEF_ID/*ieidPR_EMAIL_ADDRESS*/].Value.lpszW; } else if(PROP_TYPE(pSRowSet->aRow[i].lpProps[ieidPR_EMAIL_ADDRESS].ulPropTag) == PT_UNICODE ) lpwszID = pSRowSet->aRow[i].lpProps[ieidPR_EMAIL_ADDRESS].Value.lpszW; else lpwszID = NULL; if(PROP_TYPE(pSRowSet->aRow[i].lpProps[ieidPR_DISPLAY_NAME].ulPropTag) == PT_UNICODE ) lpwszName = pSRowSet->aRow[i].lpProps[ieidPR_DISPLAY_NAME].Value.lpszW; else lpwszName = lpwszID; // LPTSTR lpszPhone = pSRowSet->aRow[i].lpProps[ieidPR_BUSINESS_TELEPHONE_NUMBER].Value.lpszW; if(PROP_TYPE(pSRowSet->aRow[i].lpProps[ieidPR_HOME_TELEPHONE_NUMBER].ulPropTag) == PT_UNICODE ) Phones.pchHomePhone = pSRowSet->aRow[i].lpProps[ieidPR_HOME_TELEPHONE_NUMBER].Value.lpszW; else Phones.pchHomePhone = NULL; if(PROP_TYPE(pSRowSet->aRow[i].lpProps[ieidPR_BUSINESS_TELEPHONE_NUMBER].ulPropTag) == PT_UNICODE ) Phones.pchWorkPhone = pSRowSet->aRow[i].lpProps[ieidPR_BUSINESS_TELEPHONE_NUMBER].Value.lpszW; else Phones.pchWorkPhone = NULL; if(PROP_TYPE(pSRowSet->aRow[i].lpProps[ieidPR_MOBILE_TELEPHONE_NUMBER].ulPropTag) == PT_UNICODE ) Phones.pchMobilePhone = pSRowSet->aRow[i].lpProps[ieidPR_MOBILE_TELEPHONE_NUMBER].Value.lpszW; else Phones.pchMobilePhone = NULL; Phones.pchIPPhone = NULL; // temporary disabled pSRowSet->aRow[i].lpProps[ieidPR_HOME_TELEPHONE_NUMBER].Value.lpszW; // Allocate an ITEMINFO struct to store this information lpSB = NULL; m_pWABObject->AllocateBuffer(sizeof(SBinary), (LPVOID *) &lpSB); if (lpSB) { // Save the information we'll need later m_pWABObject->AllocateMore(cbEID, lpSB, (LPVOID *) &(lpSB->lpb)); if (!lpSB->lpb) { m_pWABObject->FreeBuffer(lpSB); continue; } CopyMemory(lpSB->lpb, lpEID, cbEID); lpSB->cb = cbEID; // Create an item to add to the list m_pAB->CheckAndAddAbEntry(lpSB, lpwszID, lpwszName, nFlag, &Phones); } } // Let's make sure the first item is selected // ListView_SetItemState(ctlList, 0, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED); return (S_OK); } HRESULT CAddressBookData::NewContact(HWND hwndParent) { HRESULT hr; ULONG cbNewEntry; LPENTRYID pNewEntry; ULONG cbContainerID; LPENTRYID pContainerID = 0; if(!m_pAdrBook) return E_FAIL; hr = m_pAdrBook->GetPAB(&cbContainerID, &pContainerID); if (FAILED(hr)) return(hr); hr = m_pAdrBook->NewEntry((ULONG_PTR) hwndParent, /*CREATE_CHECK_DUP_STRICT*/ 0, cbContainerID, pContainerID, 0, NULL, &cbNewEntry, &pNewEntry); return (hr); } // Add new (Msgr) contact to address book HRESULT CAddressBookData::AutoAddContact(TCHAR * pchName, TCHAR * pchID) { LPMAILUSER lpMailUser = NULL; ULONG ulcPropCount = 1; ULONG ulIndex = 0; LPSPropValue lpPropArray = NULL; SCODE sc; LPWSTR pwszName = NULL, pwszID = NULL; HRESULT hr = S_OK; if(!m_pAdrBook || !m_pWABObject) return(S_FALSE); // 1. Add entry to Address book pwszName = IEPszToUnicode(CP_ACP, pchName); if (!pwszName && (pchName && *pchName)) { hr = E_OUTOFMEMORY; goto out; } pwszID = IEPszToUnicode(CP_ACP, pchID); if (!pwszID && (pchID && *pchID)) { hr = E_OUTOFMEMORY; goto out; } #ifdef LATER hr = HrWABCreateEntry(m_pAdrBook, m_pWABObject, pwszName, pwszID, CREATE_CHECK_DUP_STRICT, &lpMailUser, KEEP_OPEN_READWRITE); #endif if(SUCCEEDED(hr)) { // 2. Set custom property: default address for buddy // Create a return prop array to pass back to the WAB sc = m_pWABObject->AllocateBuffer(sizeof(SPropValue), (LPVOID *)&lpPropArray); if (sc!=S_OK) goto out; int nLen = lstrlen(pchID) + 1; if(nLen) { lpPropArray[0].ulPropTag = MsgrPropTags[0]; sc = m_pWABObject->AllocateMore((nLen)*sizeof(TCHAR), lpPropArray, (LPVOID *)&(lpPropArray[0].Value.LPSZ)); if (sc!=S_OK) goto out; StrCpyN(lpPropArray[0].Value.LPSZ, pchID, nLen); } // Set this new data on the object // if(lpMailUser) { hr = lpMailUser->SetProps(1, lpPropArray, NULL); if(SUCCEEDED(hr)) hr = lpMailUser->SaveChanges(FORCE_SAVE); } } out: MemFree(pwszName); MemFree(pwszID); if(lpPropArray) m_pWABObject->FreeBuffer(lpPropArray); if(lpMailUser) ReleaseObj(lpMailUser); return(hr); } // Unicode string property version of template array SizedSPropTagArray(6, ptaAddr_W) = { 6, { PR_ADDRTYPE_W, PR_DISPLAY_NAME_W, PR_EMAIL_ADDRESS_W, PR_ENTRYID, PR_CONTACT_EMAIL_ADDRESSES_W, //4 PR_SEARCH_KEY } }; // ANSI string property version of template array SizedSPropTagArray(6, ptaAddr_A) = { 6, { PR_ADDRTYPE_A, PR_DISPLAY_NAME_A, PR_EMAIL_ADDRESS_A, PR_ENTRYID, PR_CONTACT_EMAIL_ADDRESSES_A, PR_SEARCH_KEY } }; HRESULT CAddressBookData::AddRecipient(LPMIMEADDRESSTABLEW pAddrTable, LPSBinary pSB, BOOL fGroup) { HRESULT hr; ULONG ulType = 0; IMailUser *pMailUser = 0; ULONG cValues; SPropValue *pPropArray = 0; ULONG ulObjType = 0; LPMAPITABLE lpAB = NULL; LPABCONT lpContainer = NULL; ULONG lpcbEID; LPENTRYID lpEID = NULL; // Retrieve the item from the wab hr = m_pAdrBook->OpenEntry(pSB->cb, (LPENTRYID) pSB->lpb, NULL, MAPI_BEST_ACCESS, &ulType, (IUnknown **) &pMailUser); if (FAILED(hr)) goto exit; // Get the appropriate properties from the object hr = pMailUser->GetProps((SPropTagArray *) &ptaAddr_W, 0, &cValues, &pPropArray); if (FAILED(hr)) goto exit; // Set those badboys on the address table if(pPropArray[2].ulPropTag == PR_EMAIL_ADDRESS_W) pAddrTable->AppendW(IAT_TO, IET_DECODED, pPropArray[2].Value.lpszW, NULL , NULL); else if(pPropArray[4].ulPropTag == PR_CONTACT_EMAIL_ADDRESSES_W) pAddrTable->AppendW(IAT_TO, IET_DECODED, pPropArray[4].Value.MVszW.lppszW[0], NULL , NULL); else if((pPropArray[1].ulPropTag == PR_DISPLAY_NAME_W) && fGroup) pAddrTable->AppendW(IAT_TO, IET_DECODED, pPropArray[1].Value.lpszW, NULL , NULL); // Bug 34077 - they don't want to have a display name... /* else if(pPropArray[1].ulPropTag == PR_DISPLAY_NAME) pAddrTable->Append(IAT_TO, IET_DECODED, pPropArray[1].Value.lpszW, NULL , NULL); else Assert(FALSE); */ exit: if (pPropArray) m_pWABObject->FreeBuffer(pPropArray); SafeRelease(pMailUser); return (hr); } HRESULT CAddressBookData::SetDefaultMsgrID(LPSBinary pSB, LPWSTR pchID) { ULONG ulType = 0; IMailUser *lpMailUser = 0; LPSPropValue lpPropArray = NULL; SCODE sc; HRESULT hr = m_pAdrBook->OpenEntry(pSB->cb, (LPENTRYID) pSB->lpb, NULL, MAPI_BEST_ACCESS, &ulType, (IUnknown **) &lpMailUser); if (FAILED(hr)) goto exit; if(SUCCEEDED(hr)) { // 2. Set custom property: default address for buddy // Create a return prop array to pass back to the WAB sc = m_pWABObject->AllocateBuffer(sizeof(SPropValue), (LPVOID *)&lpPropArray); if (sc!=S_OK) goto exit; int nLen = lstrlenW(pchID) +1 ; if(nLen) { lpPropArray[0].ulPropTag = MsgrPropTags[0]; sc = m_pWABObject->AllocateMore(nLen * sizeof(WCHAR), lpPropArray, (LPVOID *)&(lpPropArray[0].Value.lpszW)); if (sc!=S_OK) goto exit; StrCpyNW(lpPropArray[0].Value.lpszW, pchID, nLen); } // Set this new data on the object // if(lpMailUser) { hr = lpMailUser->SetProps(1, lpPropArray, NULL); if(SUCCEEDED(hr)) hr = lpMailUser->SaveChanges(FORCE_SAVE); } } exit: // if(lpPropArray) // m_pWABObject->FreeBuffer(lpPropArray); SafeRelease(lpMailUser); return (hr); } HRESULT CAddressBookData::GetDisplayName(LPSBinary pSB, LPWSTR wszDisplayName, int nMax) { ULONG ulType = 0; IMailUser *pMailUser = 0; ULONG cValues; SPropValue *pPropArray = 0; HRESULT hr = m_pAdrBook->OpenEntry(pSB->cb, (LPENTRYID) pSB->lpb, NULL, MAPI_BEST_ACCESS, &ulType, (IUnknown **) &pMailUser); if (FAILED(hr)) goto exit; // Get the appropriate properties from the object hr = pMailUser->GetProps((SPropTagArray *) &ptaAddr_W, 0, &cValues, &pPropArray); if (FAILED(hr)) goto exit; StrCpyNW(wszDisplayName, pPropArray[1].Value.lpszW, nMax); wszDisplayName[nMax - 1] = L'\0'; exit: if (pPropArray) m_pWABObject->FreeBuffer(pPropArray); SafeRelease(pMailUser); return (hr); } BOOL CAddressBookData::CheckEmailAddr(LPSBinary pSB, LPWSTR wszEmail) { ULONG ulType = 0; IMailUser *pMailUser = 0; ULONG cValues; SPropValue *pPropArray = 0; LPSPropValue lpPropMVEmail = NULL; UINT i = 0; BOOL fRet = FALSE; HRESULT hr = m_pAdrBook->OpenEntry(pSB->cb, (LPENTRYID) pSB->lpb, NULL, MAPI_BEST_ACCESS, &ulType, (IUnknown **) &pMailUser); if (FAILED(hr)) goto exit; // Get the appropriate properties from the object hr = pMailUser->GetProps((SPropTagArray *) &ptaAddr_W, 0, &cValues, &pPropArray); if (FAILED(hr)) goto exit; // 4 element is PR_CONTACT_EMAIL_ADDRESSES in ptaAddr lpPropMVEmail = &(pPropArray[4]); if(lpPropMVEmail && (lpPropMVEmail->ulPropTag == PR_CONTACT_EMAIL_ADDRESSES)) { // we have a multiple emails //Assume, if this is present, so is MVAddrType, and defaultindex for(i = 0; i < lpPropMVEmail->Value.MVszW.cValues; i++) { if(!lstrcmpiW(lpPropMVEmail->Value.MVszW.lppszW[i], wszEmail)) fRet = TRUE; } } exit: if (pPropArray) m_pWABObject->FreeBuffer(pPropArray); SafeRelease(pMailUser); return (fRet); } HRESULT CAddressBookData::FreeListViewItem(LPSBinary pSB) { if (pSB) m_pWABObject->FreeBuffer(pSB); return (S_OK); } HRESULT CAddressBookData::ShowDetails(HWND hwndParent, LPSBinary pSB) { HRESULT hr; ULONG_PTR ulUIParam = (ULONG_PTR) hwndParent; hr = m_pAdrBook->Details(&ulUIParam, NULL, NULL, pSB->cb, (LPENTRYID) pSB->lpb, NULL, NULL, NULL, 0); return (hr); } HRESULT CAddressBookData::AddAddress(LPWSTR pwszDisplay, LPWSTR pwszAddress) { HRESULT hr; LPABCONT pABContainer = NULL; ULONG cbContainerID, ul; LPENTRYID pContainerID = 0; LPMAPIPROP lpProps = 0; SPropValue rgpv[3]; SPropTagArray ptaEID = { 1, { PR_ENTRYID } }; LPSPropValue ppvDefMailUser=0; SizedSPropTagArray(1, ptaDefMailUser) = { 1, { PR_DEF_CREATE_MAILUSER } }; DWORD cUsedValues; // Get the entry ID for the PAB hr = m_pAdrBook->GetPAB(&cbContainerID, &pContainerID); if (FAILED(hr)) goto error; // Request the container hr = m_pAdrBook->OpenEntry(cbContainerID, pContainerID, NULL, 0, &ul, (LPUNKNOWN *) &pABContainer); Assert(ul == MAPI_ABCONT); if (FAILED(hr)) goto error; // Free the entry ID m_pWABObject->FreeBuffer(pContainerID); // Get the properties for the default mail template hr = pABContainer->GetProps((LPSPropTagArray) &ptaDefMailUser, 0, &ul, &ppvDefMailUser); if (FAILED(hr) || !ppvDefMailUser || ppvDefMailUser->ulPropTag != PR_DEF_CREATE_MAILUSER) goto error; // Create a new entry hr=pABContainer->CreateEntry(ppvDefMailUser->Value.bin.cb, (LPENTRYID) ppvDefMailUser->Value.bin.lpb, CREATE_CHECK_DUP_STRICT, &lpProps); if (FAILED(hr)) goto error; // Fill in the properties for the display name and address rgpv[0].ulPropTag = PR_DISPLAY_NAME_W; rgpv[0].Value.lpszW = pwszDisplay; rgpv[1].ulPropTag = PR_EMAIL_ADDRESS_W; rgpv[1].Value.lpszW = pwszAddress; cUsedValues = 2; // Set those props on the entry hr = lpProps->SetProps(cUsedValues, rgpv, NULL); if (FAILED(hr)) goto error; // Save 'em hr = lpProps->SaveChanges(KEEP_OPEN_READONLY); if (FAILED(hr)) goto error; error: ReleaseObj(lpProps); ReleaseObj(pABContainer); if (ppvDefMailUser) m_pWABObject->FreeBuffer(ppvDefMailUser); return hr; } HRESULT CAddressBookData::DeleteItems(ENTRYLIST *pList) { ULONG ulObjType = 0; LPABCONT lpContainer = NULL; HRESULT hr = E_FAIL; ULONG lpcbEID; LPENTRYID lpEID = NULL; // Get the entryid of the root PAB container hr = m_pAdrBook->GetPAB(&lpcbEID, &lpEID); // Open the root PAB container. This is where all the WAB contents reside. ulObjType = 0; hr = m_pAdrBook->OpenEntry(lpcbEID, (LPENTRYID)lpEID, NULL, 0, &ulObjType, (LPUNKNOWN *) &lpContainer); m_pWABObject->FreeBuffer(lpEID); lpEID = NULL; // Delete those items lpContainer->DeleteEntries(pList, 0); lpContainer->Release(); return (S_OK); } HRESULT CAddressBookData::Find(HWND hwndParent) { m_pWABObject->Find(m_pAdrBook, hwndParent); return (S_OK); } HRESULT CAddressBookData::NewGroup(HWND hwndParent) { HRESULT hr; ULONG cbNewEntry = 0; LPENTRYID pNewEntry = 0; LPENTRYID pTplEid; ULONG cbTplEid; hr = _GetWABTemplateID(MAPI_DISTLIST, &cbTplEid, &pTplEid); if (SUCCEEDED(hr)) { hr = m_pAdrBook->NewEntry((ULONG_PTR) hwndParent, 0, 0, NULL, cbTplEid, pTplEid, &cbNewEntry, &pNewEntry); Assert(pTplEid); m_pWABObject->FreeBuffer(pTplEid); } return (hr); } HRESULT CAddressBookData::AddressBook(HWND hwndParent) { #ifdef LATER CWab *pWab = NULL; if (SUCCEEDED(HrCreateWabObject(&pWab))) { pWab->HrBrowse(hwndParent); pWab->Release(); } else { AthMessageBoxW(hwndParent, MAKEINTRESOURCEW(idsAthena), MAKEINTRESOURCEW(idsGeneralWabError), NULL, MB_OK | MB_ICONEXCLAMATION); } #endif return (S_OK); } HRESULT CAddressBookData::_GetWABTemplateID(ULONG ulObjectType, ULONG *lpcbEID, LPENTRYID *lppEID) { LPABCONT lpContainer = NULL; HRESULT hr = S_OK; SCODE sc = ERROR_SUCCESS; ULONG ulObjType = 0; ULONG cbWABEID = 0; LPENTRYID lpWABEID = NULL; LPSPropValue lpCreateEIDs = NULL; LPSPropValue lpNewProps = NULL; ULONG cNewProps; ULONG nIndex; if ((!m_pAdrBook) || ((ulObjectType != MAPI_MAILUSER) && (ulObjectType != MAPI_DISTLIST)) ) { hr = MAPI_E_INVALID_PARAMETER; goto out; } *lpcbEID = 0; *lppEID = NULL; if (FAILED(hr = m_pAdrBook->GetPAB(&cbWABEID, &lpWABEID))) { goto out; } if (HR_FAILED(hr = m_pAdrBook->OpenEntry(cbWABEID, // size of EntryID to open lpWABEID, // EntryID to open NULL, // interface 0, // flags &ulObjType, (LPUNKNOWN *) &lpContainer))) { goto out; } // Get us the default creation entryids if (FAILED(hr = lpContainer->GetProps((LPSPropTagArray) &ptaCreate, 0, &cNewProps, &lpCreateEIDs))) { goto out; } // Validate the properites if (lpCreateEIDs[icrPR_DEF_CREATE_MAILUSER].ulPropTag != PR_DEF_CREATE_MAILUSER || lpCreateEIDs[icrPR_DEF_CREATE_DL].ulPropTag != PR_DEF_CREATE_DL) { goto out; } if (ulObjectType == MAPI_DISTLIST) nIndex = icrPR_DEF_CREATE_DL; else nIndex = icrPR_DEF_CREATE_MAILUSER; *lpcbEID = lpCreateEIDs[nIndex].Value.bin.cb; m_pWABObject->AllocateBuffer(*lpcbEID, (LPVOID *) lppEID); if (sc != S_OK) { hr = MAPI_E_NOT_ENOUGH_MEMORY; goto out; } CopyMemory(*lppEID,lpCreateEIDs[nIndex].Value.bin.lpb,*lpcbEID); out: if (lpCreateEIDs) m_pWABObject->FreeBuffer(lpCreateEIDs); if (lpContainer) lpContainer->Release(); if (lpWABEID) m_pWABObject->FreeBuffer(lpWABEID); return hr; } /*//$$**************************************************************** // // InitNamedProps // // Gets the PropTags for the Named Props this app is interested in // //********************************************************************/ HRESULT CAddressBookData::InitNamedProps(/*LPWABEXTDISPLAY lpWED*/) { ULONG i; HRESULT hr = E_FAIL; LPSPropTagArray lptaMsgrProps = NULL; LPMAPINAMEID * lppMsgrPropNames; SCODE sc; // LPMAILUSER lpMailUser = NULL; WCHAR szBuf[msgrMax][MAX_PATH]; /* if(!lpWED) goto err; lpMailUser = (LPMAILUSER) lpWED->lpPropObj; if(!lpMailUser) goto err; */ sc = m_pWABObject->AllocateBuffer(sizeof(LPMAPINAMEID) * msgrMax, (LPVOID *) &lppMsgrPropNames); if(sc) { hr = ResultFromScode(sc); goto err; } for(i=0;iAllocateMore(sizeof(MAPINAMEID), lppMsgrPropNames, (LPVOID *)&(lppMsgrPropNames[i])); if(sc) { hr = ResultFromScode(sc); goto err; } lppMsgrPropNames[i]->lpguid = (LPGUID) &WAB_ExtBAGuid; lppMsgrPropNames[i]->ulKind = MNID_STRING; *(szBuf[i]) = '\0'; // Convert prop name to wide-char if ( !MultiByteToWideChar( GetACP(), 0, lpMsgrPropNames[i], -1, szBuf[i], ARRAYSIZE(szBuf[i]) )) { continue; } lppMsgrPropNames[i]->Kind.lpwstrName = (LPWSTR) szBuf[i]; } hr = m_pAdrBook->GetIDsFromNames( msgrMax, lppMsgrPropNames, MAPI_CREATE, &lptaMsgrProps); if(HR_FAILED(hr)) goto err; if(lptaMsgrProps) { // Set the property types on the returned props MsgrPropTags[MsgrID] = PR_MSGR_DEF_ID = CHANGE_PROP_TYPE(lptaMsgrProps->aulPropTag[MsgrID], PT_TSTRING); } err: if(lptaMsgrProps) m_pWABObject->FreeBuffer( lptaMsgrProps); if(lppMsgrPropNames) m_pWABObject->FreeBuffer( lppMsgrPropNames); return hr; }