/*********************************************************************** * * ABUSER.C * * Microsoft At Work Fax AB Mail User object * This file contains the code for implementing the Microsoft At Work Fax AB * Mail user. * * The mail user has a read-only interface. Hence a few of the methods * will always return MAPI_E_NO_ACCESS. Certain aspects of this * implementation are not implemented, such as retrieving the display * table in order to build up a details screen in the UI. For the most * part, however, this interface has enough to retrieve enough properties * to actually send mail. * * An important thing that's not implemented in this version is the * verification of the entryid to see if it's still valid. In most * mail systems, you would want to check to see if a particular recipient * still exists before actually trying to send mail to that recipient. * * The following routines are implemented in this file: * * * HrHrNewFaxUser * ABU_QueryInterface * ABU_Release * ABU_OpenProperty * HrBuildListBoxTable * HrBuildDDListboxTable * HrBuildComboBoxTable * * Copyright 1992, 1993 Microsoft Corporation. All Rights Reserved. * * Revision History: * * When Who What * -------- ------------------ --------------------------------------- * 1.1.94 MAPI Original source from MAPI sample AB Provider * 1.27.94 Yoram Yaacovi Modifications to make it an At Work Fax ABP * 3.7.94 Yoram Yaacovi Update to MAPI build 154 * 8.3.94 Yoram Yaacovi Update to MAPI build 304 and new Fax AB spec * 11.11.94 Yoram Yaacovi Update to MAPI 318 * ***********************************************************************/ #include "faxab.h" #ifdef UNICODE #include #else #include #endif #define _FAXAB_ABUSER /* * Private functions */ /***************************** ** Display Table structures * *****************************/ // Cover page name of the user DTBLEDIT editUserDisplayName = { SIZEOF(DTBLEDIT), 0, MAX_DISPLAY_NAME, PR_FAX_CP_NAME_A }; // The area code component of the email address (fax number) DTBLEDIT editUserAreaCode = { SIZEOF(DTBLEDIT), 0, AREA_CODE_SIZE, PR_AREA_CODE_A }; // The number component of the email address (fax number) DTBLEDIT editUserTelNumber = { SIZEOF(DTBLEDIT), 0, TELEPHONE_NUMBER_SIZE, PR_TEL_NUMBER_A }; // The country codes list box DTBLDDLBX ddlbxCountryCodes = { SIZEOF(DTBLDDLBX), PR_DISPLAY_NAME_A, PR_COUNTRY_ID, PR_DDLBX_COUNTRIES_TABLE }; // Description the the Fax AB pane in MAPI dialog description language static DTCTL rgdtctlUserGeneral[] = { /* general property page */ { DTCT_PAGE, 0, NULL, 0, NULL, 0, &dtblpage }, /* cover page name static control and edit control */ { DTCT_LABEL, 0, NULL, 0, NULL, IDC_RECIP_DISPLAY_NAME_LABEL, &dtbllabel }, { DTCT_EDIT, 0, NULL, 0, (LPTSTR)szNoFilter, IDC_RECIP_DISPLAY_NAME, &editUserDisplayName }, /* Country codes label and drop down list box */ { DTCT_LABEL, 0, NULL, 0, NULL, IDC_RECIP_COUNTRY_CODE_LABEL, &dtbllabel }, { DTCT_DDLBX, 0, NULL, 0, NULL, IDC_RECIP_COUNTRY_CODE, &ddlbxCountryCodes }, /* Area code and fax number label */ { DTCT_LABEL, 0, NULL, 0, NULL, IDC_RECIP_FAX_NUMBER_LABEL, &dtbllabel }, /* area code edit control */ { DTCT_EDIT, 0, NULL, 0, (LPTSTR)szDigitsOnlyFilter, IDC_RECIP_FAX_NUMBER_AREA_CODE, &editUserAreaCode }, /* Fax number static control and edit control */ { DTCT_LABEL, 0, NULL, 0, NULL, IDC_RECIP_FAX_NUMBER_LABEL2, &dtbllabel }, { DTCT_EDIT, 0, NULL, 0, (LPTSTR)szDigitsOnlyFilter, IDC_RECIP_FAX_NUMBER, &editUserTelNumber} }; #if defined (RECIP_OPTIONS) // User options property page. currently not implemented DTCTL rgdtctlUserOptions[] = { /* options property page */ { DTCT_PAGE, 0, NULL, 0, NULL, 0, &dtblpage }, /* static control and listbox */ { DTCT_LABEL, 0, NULL, 0, NULL, IDC_STATIC_CONTROL, &dtbllabel }, }; #endif /* Display table pages */ /* shared with oouser.c */ static DTPAGE rgdtpageUser[] = { { ARRAYSIZE(rgdtctlUserGeneral), (LPTSTR)MAKEINTRESOURCE(MAWFRecipient), TEXT(""), rgdtctlUserGeneral }, #if defined (RECIP_OPTIONS) { ARRAYSIZE(rgdtctlUserOptions), (LPTSTR)MAKEINTRESOURCE(UserOptionsPage), TEXT(""), rgdtctlUserOptions } #endif }; WORD sizeof_rgdtpageUser = SIZEOF(rgdtpageUser); /* * ABUser jump table is defined here... */ ABU_Vtbl vtblABU = { ABU_QueryInterface, (ABU_AddRef_METHOD *) ROOT_AddRef, ABU_Release, (ABU_GetLastError_METHOD *) ROOT_GetLastError, (ABU_SaveChanges_METHOD *) WRAP_SaveChanges, (ABU_GetProps_METHOD *) WRAP_GetProps, (ABU_GetPropList_METHOD *) WRAP_GetPropList, ABU_OpenProperty, (ABU_SetProps_METHOD *) WRAP_SetProps, (ABU_DeleteProps_METHOD *) WRAP_DeleteProps, (ABU_CopyTo_METHOD *) WRAP_CopyTo, (ABU_CopyProps_METHOD *) WRAP_CopyProps, (ABU_GetNamesFromIDs_METHOD *) WRAP_GetNamesFromIDs, (ABU_GetIDsFromNames_METHOD *) WRAP_GetIDsFromNames, }; enum { ivalusrPR_DISPLAY_TYPE = 0, ivalusrPR_OBJECT_TYPE, ivalusrPR_ENTRYID, ivalusrPR_RECORD_KEY, ivalusrPR_DISPLAY_NAME, ivalusrPR_TRANSMITABLE_DISPLAY_NAME, ivalusrPR_FAX_CP_NAME, ivalusrPR_EMAIL_ADDRESS, ivalusrPR_ADDRTYPE, ivalusrPR_SEARCH_KEY, ivalusrPR_AREA_CODE, ivalusrPR_TEL_NUMBER, ivalusrPR_COUNTRY_ID, ivalusrPR_TEMPLATEID, cvalusrMax }; /************************************************************************* * - HrNewFaxUser - * Creates the IMAPIProp associated with a mail user. * * */ HRESULT HrNewFaxUser( LPMAILUSER * lppMAPIPropEntry, ULONG * lpulObjType, ULONG cbEntryID, LPENTRYID lpEntryID, LPABLOGON lpABPLogon, LPCIID lpInterface, HINSTANCE hLibrary, LPALLOCATEBUFFER lpAllocBuff, LPALLOCATEMORE lpAllocMore, LPFREEBUFFER lpFreeBuff, LPMALLOC lpMalloc ) { LPABUSER lpABUser = NULL; SCODE sc; HRESULT hr = hrSuccess; LPPROPDATA lpPropData = NULL; SPropValue spv[cvalusrMax]; ULONG cbT = 0; LPBYTE lpbT = NULL; LPSTR lpszEMA = NULL; PARSEDTELNUMBER sParsedFaxAddr; DWORD dwCountryID; #ifdef UNICODE CHAR szAnsiDisplayName[ MAX_PATH ]; CHAR szAnsiEmailAddress[ MAX_PATH ]; CHAR szAnsiTelNumber[ 50 ]; CHAR szAnsiAreaCode[ 5 ]; CHAR szAnsiEMT[ 25 ]; #endif /* Do I support this interface?? */ if (lpInterface) { if ( memcmp(lpInterface, &IID_IMailUser, SIZEOF(IID)) && memcmp(lpInterface, &IID_IMAPIProp, SIZEOF(IID)) && memcmp(lpInterface, &IID_IUnknown, SIZEOF(IID)) ) { DebugTraceSc(HrNewSampUser, MAPI_E_INTERFACE_NOT_SUPPORTED); return ResultFromScode(MAPI_E_INTERFACE_NOT_SUPPORTED); } } /* * Allocate space for the ABUSER structure */ sc = lpAllocBuff( SIZEOF(ABUSER), (LPVOID *) & lpABUser); if (FAILED(sc)) { hr = ResultFromScode (sc); goto err; } lpABUser->lpVtbl = &vtblABU; lpABUser->lcInit = 1; lpABUser->hResult = hrSuccess; lpABUser->idsLastError = 0; lpABUser->hLibrary = hLibrary; lpABUser->lpAllocBuff = lpAllocBuff; lpABUser->lpAllocMore = lpAllocMore; lpABUser->lpFreeBuff = lpFreeBuff; lpABUser->lpMalloc = lpMalloc; lpABUser->lpABLogon = lpABPLogon; lpABUser->lpTDatDDListBox = NULL; /* * Create lpPropData */ sc = CreateIProp( (LPIID) &IID_IMAPIPropData, lpAllocBuff, lpAllocMore, lpFreeBuff, lpMalloc, &lpPropData ); if (FAILED(sc)) { hr = ResultFromScode (sc); goto err; } /* * Set up initial set of properties associated with this * container. */ /* * Easy ones first */ spv[ivalusrPR_DISPLAY_TYPE].ulPropTag = PR_DISPLAY_TYPE; spv[ivalusrPR_DISPLAY_TYPE].Value.l = DT_MAILUSER; spv[ivalusrPR_OBJECT_TYPE].ulPropTag = PR_OBJECT_TYPE; spv[ivalusrPR_OBJECT_TYPE].Value.l = MAPI_MAILUSER; spv[ivalusrPR_ENTRYID].ulPropTag = PR_ENTRYID; spv[ivalusrPR_ENTRYID].Value.bin.cb = SIZEOF(USR_ENTRYID); spv[ivalusrPR_ENTRYID].Value.bin.lpb = (LPBYTE) lpEntryID; /* * Now the calculated props */ spv[ivalusrPR_RECORD_KEY].ulPropTag = PR_RECORD_KEY; spv[ivalusrPR_RECORD_KEY].Value.bin.cb = SIZEOF(USR_ENTRYID); spv[ivalusrPR_RECORD_KEY].Value.bin.lpb = (LPBYTE) lpEntryID; spv[ivalusrPR_DISPLAY_NAME].ulPropTag = PR_DISPLAY_NAME_A; #ifdef UNICODE szAnsiDisplayName[0] = 0; WideCharToMultiByte( CP_ACP, 0, ((LPUSR_ENTRYID) lpEntryID)->abcrec.rgchDisplayName, -1, szAnsiDisplayName, ARRAYSIZE(szAnsiDisplayName), NULL, NULL ); spv[ivalusrPR_DISPLAY_NAME].Value.lpszA = szAnsiDisplayName; #else spv[ivalusrPR_DISPLAY_NAME].Value.lpszA = ((LPUSR_ENTRYID) lpEntryID)->abcrec.rgchDisplayName; #endif /* Should always be the same as PR_DISPLAY_NAME */ spv[ivalusrPR_TRANSMITABLE_DISPLAY_NAME].ulPropTag = PR_TRANSMITABLE_DISPLAY_NAME_A; #ifdef UNICODE spv[ivalusrPR_TRANSMITABLE_DISPLAY_NAME].Value.lpszA = szAnsiDisplayName; #else spv[ivalusrPR_TRANSMITABLE_DISPLAY_NAME].Value.LPSZ = ((LPUSR_ENTRYID) lpEntryID)->abcrec.rgchDisplayName; #endif // Make the cover page name identical to the display name spv[ivalusrPR_FAX_CP_NAME].ulPropTag = PR_FAX_CP_NAME_A; #ifdef UNICODE spv[ivalusrPR_FAX_CP_NAME].Value.lpszA = szAnsiDisplayName; #else spv[ivalusrPR_FAX_CP_NAME].Value.LPSZ = ((LPUSR_ENTRYID)lpEntryID)->abcrec.rgchDisplayName; #endif spv[ivalusrPR_EMAIL_ADDRESS].ulPropTag = PR_EMAIL_ADDRESS_A; #ifdef UNICODE szAnsiEmailAddress[0] = 0; WideCharToMultiByte( CP_ACP, 0, ((LPUSR_ENTRYID) lpEntryID)->abcrec.rgchEmailAddress, -1, szAnsiEmailAddress, ARRAYSIZE(szAnsiEmailAddress), NULL, NULL ); spv[ivalusrPR_EMAIL_ADDRESS].Value.lpszA = szAnsiEmailAddress; lpszEMA = szAnsiEmailAddress; #else spv[ivalusrPR_EMAIL_ADDRESS].Value.LPSZ = ((LPUSR_ENTRYID) lpEntryID)->abcrec.rgchEmailAddress; lpszEMA = ((LPUSR_ENTRYID) lpEntryID)->abcrec.rgchEmailAddress; #endif spv[ivalusrPR_ADDRTYPE].ulPropTag = PR_ADDRTYPE_A; #ifdef UNICODE szAnsiEMT[0] = 0; WideCharToMultiByte( CP_ACP, 0, lpszEMT, -1, szAnsiEMT, ARRAYSIZE(szAnsiEMT), NULL, NULL ); spv[ivalusrPR_ADDRTYPE].Value.lpszA = szAnsiEMT; #else spv[ivalusrPR_ADDRTYPE].Value.LPSZ = lpszEMT; #endif /* * Build the search key... */ /* Search keys for mailable recipients that have email addresses are * defined as "EmailType':'EmailAddress\0". We do the +2 for the ':' and * '\0'. */ #ifdef UNICODE cbT = lstrlenA(lpszEMA) + lstrlenA(szAnsiEMT) + 2; #else cbT = lstrlen(lpszEMA) + lstrlen(lpszEMT) + 2; #endif sc = lpAllocBuff( cbT, (LPVOID *) &lpbT ); if (FAILED(sc)) { hr = ResultFromScode(sc); goto err; } #ifdef UNICODE lstrcpyA((LPSTR) lpbT, szAnsiEMT); #else lstrcpyA((LPSTR) lpbT, lpszEMT); #endif lstrcatA((LPSTR) lpbT, ":"); lstrcatA((LPSTR) lpbT, lpszEMA); CharUpperBuffA((LPSTR) lpbT, (UINT) cbT); spv[ivalusrPR_SEARCH_KEY].ulPropTag = PR_SEARCH_KEY; spv[ivalusrPR_SEARCH_KEY].Value.bin.cb = cbT; spv[ivalusrPR_SEARCH_KEY].Value.bin.lpb = lpbT; // Need to decode the email address into "displayable" components // and set them on the object so that MAPI can display // Country code is stored separately in the address book. DecodeFaxAddress((LPTSTR)((LPUSR_ENTRYID)lpEntryID)->abcrec.rgchEmailAddress, &sParsedFaxAddr); // EncodeFaxAddress(tempString, &sParsedFaxAddr); /* * Now set the "displayable" components on the user object */ spv[ivalusrPR_TEL_NUMBER].ulPropTag = PR_TEL_NUMBER_A; #ifdef UNICODE szAnsiTelNumber[0] = 0; WideCharToMultiByte( CP_ACP, 0, sParsedFaxAddr.szTelNumber, -1, szAnsiTelNumber, ARRAYSIZE(szAnsiTelNumber), NULL, NULL ); spv[ivalusrPR_TEL_NUMBER].Value.lpszA = szAnsiTelNumber; #else spv[ivalusrPR_TEL_NUMBER].Value.LPSZ = sParsedFaxAddr.szTelNumber; #endif // Country ID/code brings some trouble. I need the country ID, but it // might be that it is not in the FAB. If it's not, I need to try my // best on getting the country ID from the country code spv[ivalusrPR_COUNTRY_ID].ulPropTag = PR_COUNTRY_ID; #ifdef UNICODE dwCountryID = (DWORD) wcstol(((LPUSR_ENTRYID)lpEntryID)->abcrec.rgchCountryID,NULL,10); #else dwCountryID = (DWORD) atol(((LPUSR_ENTRYID)lpEntryID)->abcrec.rgchCountryID); #endif if (dwCountryID == 0) { // no country code in the FAB // GetCountryID will return 1 (U.S.) for country ID in case of error // GetCountryID((DWORD) atol(sParsedFaxAddr.szCountryCode), &dwCountryID); // This should work just the same since country code should map into a country // ID (actually into several. Best I can do is get the first country that // uses this country code. Could analyze area codes, ask the user, etc.... #ifdef UNICODE dwCountryID = (DWORD) wcstol(sParsedFaxAddr.szCountryCode,NULL,10); #else dwCountryID = (DWORD) atol(sParsedFaxAddr.szCountryCode); #endif } spv[ivalusrPR_COUNTRY_ID].Value.ul = dwCountryID; // if no area code in the address, and it's not a 'no country' thing, get the // area code of the current location if ((!lstrcmp(sParsedFaxAddr.szAreaCode, TEXT(""))) && (dwCountryID != 0)) { lstrcpy(sParsedFaxAddr.szAreaCode, (LPCTSTR) GetCurrentLocationAreaCode()); } spv[ivalusrPR_AREA_CODE].ulPropTag = PR_AREA_CODE_A; #ifdef UNICODE szAnsiAreaCode[0] = 0; WideCharToMultiByte( CP_ACP, 0, sParsedFaxAddr.szAreaCode, -1, szAnsiAreaCode, ARRAYSIZE(szAnsiAreaCode), NULL, NULL ); spv[ivalusrPR_AREA_CODE].Value.lpszA = szAnsiAreaCode; #else spv[ivalusrPR_AREA_CODE].Value.LPSZ = sParsedFaxAddr.szAreaCode; #endif /* * Note that we're using our entryID for our templateID. * This is a really simple way to implement templateIDs. * (See TID.C) */ spv[ivalusrPR_TEMPLATEID].ulPropTag = PR_TEMPLATEID; spv[ivalusrPR_TEMPLATEID].Value.bin.cb = SIZEOF(USR_ENTRYID); spv[ivalusrPR_TEMPLATEID].Value.bin.lpb = (LPBYTE) lpEntryID; /* * Set the default properties */ hr = lpPropData->lpVtbl->SetProps( lpPropData, cvalusrMax, spv, NULL ); if (HR_FAILED(hr)) { goto err; } /* * Although this object is basically read only, we wanted to show * an example of how the check-boxes and other controls on the * "Options" pane work. If we had set this objet to be read only, * the values behind those controls would have been static. */ (void)lpPropData->lpVtbl->HrSetObjAccess(lpPropData, IPROP_READWRITE); lpABUser->lpPropData = (LPMAPIPROP) lpPropData; InitializeCriticalSection(&lpABUser->cs); *lppMAPIPropEntry = (LPVOID) lpABUser; *lpulObjType = MAPI_MAILUSER; out: lpFreeBuff(lpbT); DebugTraceResult(HrNewSampUser, hr); return hr; err: if (lpPropData) lpPropData->lpVtbl->Release(lpPropData); lpFreeBuff(lpABUser); goto out; } /*************************************************** * * The actual ABContainer methods */ /* -------- * IUnknown */ /************************************************************************* * * - ABU_QueryInterface - * * This method is reused in TID.C, OOTID.C, and ABOOSER.C. Hence the * difference in checking of the 'this' pointer from other methods within * this object. */ STDMETHODIMP ABU_QueryInterface( LPABUSER lpABUser, REFIID lpiid, LPVOID FAR * lppNewObj ) { HRESULT hr = hrSuccess; /* Minimally check the lpABUer interface * Can't do any more extensive checking that this because this method is reused * in OOUSER.C. */ if (IsBadReadPtr(lpABUser, offsetof(ABUSER, lpVtbl)+SIZEOF(ABU_Vtbl *))) { hr = ResultFromScode(E_INVALIDARG); goto out; } if (IsBadReadPtr(lpABUser->lpVtbl, offsetof(ABU_Vtbl, QueryInterface)+SIZEOF(ABU_QueryInterface_METHOD *))) { hr = ResultFromScode(E_INVALIDARG); goto out; } if (ABU_QueryInterface != lpABUser->lpVtbl->QueryInterface) { hr = ResultFromScode(E_INVALIDARG); goto out; } /* Validate other parameters */ if (IsBadReadPtr(lpiid, (UINT) SIZEOF(IID))) { hr = ResultFromScode(E_INVALIDARG); goto out; } if (IsBadWritePtr(lppNewObj, SIZEOF(LPVOID))) { hr = ResultFromScode(E_INVALIDARG); goto out; } /* See if the requested interface is one of ours */ if ( memcmp(lpiid, &IID_IUnknown, SIZEOF(IID)) && memcmp(lpiid, &IID_IMAPIProp, SIZEOF(IID)) && memcmp(lpiid, &IID_IMailUser, SIZEOF(IID)) ) { *lppNewObj = NULL; /* OLE requires zeroing [out] parameter */ hr = ResultFromScode(E_NOINTERFACE); goto out; } /* We'll do this one. Bump the usage count and return a new pointer. */ EnterCriticalSection(&lpABUser->cs); ++lpABUser->lcInit; LeaveCriticalSection(&lpABUser->cs); *lppNewObj = lpABUser; out: DebugTraceResult(ABU_QueryInterface, hr); return hr; } /* * Use ROOTs - no need duplicating code * * ROOT_AddRef */ /************************************************** * - ABU_Release - * Decrement lpInit. * When lcInit == 0, free up the lpABUser structure * */ STDMETHODIMP_(ULONG) ABU_Release (LPABUSER lpABUser) { LONG lcInit; /* * Check to see if it's big enough to hold this object */ if (IsBadReadPtr(lpABUser, SIZEOF(ABUSER))) { /* * Not large enough */ return 1; } /* * Check to see that it's the correct vtbl */ if (lpABUser->lpVtbl != &vtblABU) { /* * Not my vtbl */ return 1; } EnterCriticalSection(&lpABUser->cs); lcInit = --lpABUser->lcInit; LeaveCriticalSection(&lpABUser->cs); if (lcInit == 0) { /* * Get rid of the lpPropData */ lpABUser->lpPropData->lpVtbl->Release(lpABUser->lpPropData); /* * Get rid of the country codes table */ if (lpABUser->lpTDatDDListBox) lpABUser->lpTDatDDListBox->lpVtbl->Release(lpABUser->lpTDatDDListBox); /* * Destroy the critical section for this object */ DeleteCriticalSection(&lpABUser->cs); /* * Set the vtbl to NULL. This way the client will find out * real fast if it's calling a method on a released object. That is, * the client will crash. Hopefully, this will happen during the * development stage of the client. */ lpABUser->lpVtbl = NULL; /* * Need to free the object */ lpABUser->lpFreeBuff( lpABUser ); return 0; } return lcInit; } /* --------- * IMAPIProp */ /* * GetLastError - use ROOTs * * */ /************************************************************************* * - ABU_OpenProperty - * * This is how we get the display table associated with this users * details screen. */ STDMETHODIMP ABU_OpenProperty( LPABUSER lpABUser, ULONG ulPropTag, LPCIID lpiid, ULONG ulInterfaceOptions, ULONG ulFlags, LPUNKNOWN * lppUnk ) { HRESULT hResult; /* * Check to see if it's big enough to hold this object */ if (IsBadReadPtr(lpABUser, SIZEOF(ABUSER))) { /* * Not large enough */ hResult = ResultFromScode(E_INVALIDARG); DebugTraceResult(ABU_OpenProperty, hResult); return hResult; } /* * Check to see that it's the correct vtbl */ if (lpABUser->lpVtbl != &vtblABU) { /* * Not my vtbl */ hResult = ResultFromScode(E_INVALIDARG); DebugTraceResult(ABU_OpenProperty, hResult); return hResult; } if (IsBadWritePtr(lppUnk, SIZEOF(LPUNKNOWN))) { /* * Got to be able to return an object */ hResult = ResultFromScode(E_INVALIDARG); DebugTraceResult(ABU_OpenProperty, hResult); return hResult; } if (IsBadReadPtr(lpiid, (UINT) SIZEOF(IID))) { /* * An interface ID is required for this call. */ hResult = ResultFromScode(E_INVALIDARG); DebugTraceResult(ABU_OpenProperty, hResult); return hResult; } if (ulFlags & ~(MAPI_CREATE|MAPI_MODIFY|MAPI_DEFERRED_ERRORS)) { hResult = ResultFromScode(MAPI_E_UNKNOWN_FLAGS); DebugTraceResult(ABU_OpenProperty, hResult); return hResult; } if (ulInterfaceOptions & ~MAPI_UNICODE ) { /* * Only the Unicode flag should be set for any of the objects that might * be returned from this object. */ hResult = ResultFromScode(MAPI_E_UNKNOWN_FLAGS); DebugTraceResult(ABU_OpenProperty, hResult); return hResult; } if ( ulInterfaceOptions & MAPI_UNICODE ) { hResult = ResultFromScode(MAPI_E_BAD_CHARWIDTH); DebugTraceResult(ABU_OpenProperty, hResult); return hResult; } if (ulFlags & MAPI_CREATE) { hResult = ResultFromScode(E_ACCESSDENIED); DebugTraceResult(ABU_OpenProperty, hResult); return hResult; } // If the caller is trying to open a table property and is not expecting a table interface.. switch (ulPropTag) { case PR_DETAILS_TABLE: case PR_DDLBX_COUNTRIES_TABLE: if (memcmp( lpiid, &IID_IMAPITable, SIZEOF(IID) )) { // ... we will abort right here hResult = ResultFromScode(MAPI_E_INTERFACE_NOT_SUPPORTED); goto out; } break; default: break; } EnterCriticalSection(&lpABUser->cs); /* Now lets handle the requested property */ switch (ulPropTag) { case PR_DETAILS_TABLE: /* Looking for the display table*/ /* Create a display table */ hResult = BuildDisplayTable( lpABUser->lpAllocBuff, lpABUser->lpAllocMore, lpABUser->lpFreeBuff, lpABUser->lpMalloc, lpABUser->hLibrary, ARRAYSIZE(rgdtpageUser), rgdtpageUser, 0, (LPMAPITABLE*)lppUnk, NULL ); // if error, will just report the hResult to the caller break; case PR_DDLBX_COUNTRIES_TABLE: // This table is not changing dynamically. No need to rebuild if already built. if (!lpABUser->lpTDatDDListBox) { hResult = HrBuildDDLBXCountriesTable(lpABUser); if (HR_FAILED(hResult)) goto out; } Assert(lpABUser->lpTDatDDListBox); /* Get a view from the TAD*/ hResult = lpABUser->lpTDatDDListBox->lpVtbl->HrGetView( lpABUser->lpTDatDDListBox, NULL, NULL, 0, (LPMAPITABLE *) lppUnk); break; default: hResult = ResultFromScode(MAPI_E_NO_SUPPORT); break; } out: LeaveCriticalSection(&lpABUser->cs); DebugTraceResult(ABU_OpenProperty, hResult); return hResult; } /********************************************************************** * * Private functions */ /**************************************************************************** FUNCTION: SortCountriesList PURPOSE: prepares (for qsort) and sorts a countries list returned by TAPI, by country name (TAPI returns a list sorted by the country ID) PARAMETERS: [in] lpLineCountryList - the list of countries retuned by TAPI [out] lpCountriesList - a sorted list country names/IDs structures ****************************************************************************/ void SortCountriesList( HINSTANCE hInst, LPLINECOUNTRYLIST lpLineCountryList, LPCOUNTRIESLIST lpCountriesList ) { LPLINECOUNTRYENTRY lprgLineCountryEntry = NULL; ULONG country; // // Create a "no country" entry // LoadString( hInst, IDS_NONE, lpCountriesList[0].szDisplayName, MAX_DISPLAY_NAME); lpCountriesList[0].dwValue = 0; // // Point to the first country in the structure // lprgLineCountryEntry = (LPLINECOUNTRYENTRY)((LPBYTE)(lpLineCountryList) + lpLineCountryList->dwCountryListOffset); // // Loop through LINECOUNTRYENTRY structures and add to the array // for (country=0; country < lpLineCountryList->dwNumCountries; country++) { if ((lprgLineCountryEntry[country].dwCountryNameSize != 0) && (lprgLineCountryEntry[country].dwCountryNameOffset != 0L)) { LPTSTR szCountryName = (LPTSTR)((LPBYTE) lpLineCountryList + lprgLineCountryEntry[country].dwCountryNameOffset); DWORD dwCountryID = lprgLineCountryEntry[country].dwCountryID; DWORD dwCountryCode = lprgLineCountryEntry[country].dwCountryCode; // // Create a "country-name (area-code)" string // #ifdef UNICODE _snwprintf( #else _snprintf( #endif lpCountriesList[country+1].szDisplayName, COUNTRY_NAME_SIZE, TEXT("%s (%d)"), szCountryName, dwCountryCode ); lpCountriesList[country+1].dwValue = dwCountryID; } } // // now sort the array by the country name // qsort( lpCountriesList, lpLineCountryList->dwNumCountries + 1, SIZEOF(COUNTRIESLIST), #ifdef UNICODE _wcsicmp #else _mbsicmp #endif ); } /**************************************************************************** FUNCTION: HrBuildDDLBXCountriesTable PURPOSE: builds a country codes table to use by MAPI to display the countries list box PARAMETERS: [in] lpABUser - the user object RETURNS: hResult ****************************************************************************/ enum { ivallbxPR_DISPLAY_NAME = 0, ivallbxPR_ENTRYID, ivallbxPR_DISPLAY_TYPE, ivallbxPR_COUNTRY_ID, cvallbxMax }; const SizedSPropTagArray(cvallbxMax, tagaColSetCountries) = { cvallbxMax, { PR_DISPLAY_NAME_A, PR_ENTRYID, PR_DISPLAY_TYPE, PR_COUNTRY_ID, } }; OPTIONS_ENTRYID OptionsEntryID= { {0, 0, 0, 0}, MUIDABMAWF, MAWF_VERSION, MAWF_UNKNOWN, 0 }; HRESULT HrBuildDDLBXCountriesTable(LPABUSER lpABUser) { SCODE sc; HRESULT hResult; SPropValue rgsPropValue[cvallbxMax]; SRow sRow; TCHAR szDisplay[100]; ULONG country; HINSTANCE hInst; LPLINECOUNTRYLIST lpLineCountryList = NULL; // TAPI LPLINECOUNTRYENTRY lprgLineCountryEntry = NULL; // TAPI LPCOUNTRIESLIST lpCountriesList = NULL; // Mine #ifdef UNICODE CHAR szAnsiDisplay[100]; #endif // get the instance, so I can load strings from the resource file hInst = lpABUser->hLibrary; /* Create a TaD*/ sc = CreateTable( (LPIID)&IID_IMAPITableData, lpABUser->lpAllocBuff, lpABUser->lpAllocMore, lpABUser->lpFreeBuff, lpABUser->lpMalloc, 0, PR_DISPLAY_NAME_A, (LPSPropTagArray)&tagaColSetCountries, &(lpABUser->lpTDatDDListBox) ); if (FAILED(sc)) { hResult = ResultFromScode(sc); goto out; } // constants sRow.cValues = cvallbxMax; sRow.lpProps = rgsPropValue; rgsPropValue[ivallbxPR_DISPLAY_NAME].ulPropTag = PR_DISPLAY_NAME_A; #ifdef UNICODE szAnsiDisplay[0] = 0; rgsPropValue[ivallbxPR_DISPLAY_NAME].Value.lpszA = szAnsiDisplay; #else szDisplay[0] = 0; rgsPropValue[ivallbxPR_DISPLAY_NAME].Value.lpszA = szDisplay; #endif /* * For this release of MAPI the following two properties are required * for all listboxes exposed in any details dialog. This requirement is * scheduled to be removed before ship */ rgsPropValue[ivallbxPR_ENTRYID].ulPropTag = PR_ENTRYID; rgsPropValue[ivallbxPR_ENTRYID].Value.bin.lpb = (LPBYTE) &OptionsEntryID; rgsPropValue[ivallbxPR_ENTRYID].Value.bin.cb = CBOPTIONS_ENTRYID; rgsPropValue[ivallbxPR_DISPLAY_TYPE].ulPropTag = PR_DISPLAY_TYPE; rgsPropValue[ivallbxPR_DISPLAY_TYPE].Value.l = 0; /* There are no defines for this yet */ rgsPropValue[ivallbxPR_COUNTRY_ID].ulPropTag = PR_COUNTRY_ID; /* * Create the country list */ // get the country info structure from TAPI if (!GetCountry(0, &lpLineCountryList)) goto out; // // allocate a buffer for the country names and IDs // the allocation here is a best guess and could be wrong // sc = lpABUser->lpAllocBuff( (lpLineCountryList->dwNumCountries+1) * sizeof(COUNTRIESLIST), (LPVOID *) & lpCountriesList); if (FAILED(sc)) { hResult = ResultFromScode (sc); goto out; } SortCountriesList(hInst, lpLineCountryList, lpCountriesList); // lpCountriesList now points to the sorted list of countries for (country=0; country < (lpLineCountryList->dwNumCountries + 1); country++) { OptionsEntryID.ulRowNumber = country; szDisplay[0] = 0; lstrcpy(szDisplay, lpCountriesList[country].szDisplayName); #ifdef UNICODE szAnsiDisplay[0] = 0; WideCharToMultiByte( CP_ACP, 0, szDisplay, -1, szAnsiDisplay, ARRAYSIZE(szAnsiDisplay), NULL, NULL ); #endif rgsPropValue[ivallbxPR_COUNTRY_ID].Value.ul = lpCountriesList[country].dwValue; hResult = lpABUser->lpTDatDDListBox->lpVtbl->HrModifyRow( lpABUser->lpTDatDDListBox, &sRow); if (HR_FAILED(hResult)) { /* * Mask errors here... * We want to do this because it's probibly still a valid * table data object that I can get views from. Most likely * just some of the rows will be missing... */ hResult = hrSuccess; break; } } /* * get rid of any warnings */ hResult = hrSuccess; out: // Free the buffer // Buffer was allocated by GetCountry using my alllocation function if (lpLineCountryList) LocalFree( lpLineCountryList ); lpABUser->lpFreeBuff(lpCountriesList); DebugTraceResult(HrBuildDDLBXCountriesTable, hResult); return hResult; } /******************************************************* * * The button Interface * *******************************************************/ // The General button methods ABUBUTT_Vtbl vtblABUBUTT = { ABUBUTT_QueryInterface, (ABUBUTT_AddRef_METHOD *) ROOT_AddRef, ABUBUTT_Release, (ABUBUTT_GetLastError_METHOD *) ROOT_GetLastError, ABUBUTT_Activate, ABUBUTT_GetState }; HRESULT HrNewABUserButton( LPMAPICONTROL * lppMAPICont, LPABLOGON lpABLogon, HINSTANCE hLibrary, LPALLOCATEBUFFER lpAllocBuff, LPALLOCATEMORE lpAllocMore, LPFREEBUFFER lpFreeBuff, LPMALLOC lpMalloc, ULONG ulPropTag ) { LPABUBUTT lpABUButt = NULL; SCODE scode; /* * Creates a the object behind the button */ if ((scode = lpAllocBuff( SIZEOF (ABUBUTT), (LPVOID *) & lpABUButt)) != SUCCESS_SUCCESS ) { DebugTraceSc(HrNewABUserButton, scode); return ResultFromScode (scode); } lpABUButt->lpVtbl = &vtblABUBUTT; lpABUButt->lcInit = 1; lpABUButt->hResult = hrSuccess; lpABUButt->idsLastError = 0; lpABUButt->hLibrary = hLibrary; lpABUButt->lpAllocBuff = lpAllocBuff; lpABUButt->lpAllocMore = lpAllocMore; lpABUButt->lpFreeBuff = lpFreeBuff; lpABUButt->lpMalloc = lpMalloc; lpABUButt->lpABLogon = lpABLogon; // So that I'll know later which button this object refers to // currently not used. Will use only if more than one button on the template lpABUButt->ulPropTag = ulPropTag; /* * I need my parent object to stay around while this object * does so that I can get to it in my Activate() method. * To do this just AddRef() it. */ // lpABC->lpVtbl->AddRef(lpABC); InitializeCriticalSection(&lpABUButt->cs); *lppMAPICont = (LPMAPICONTROL) lpABUButt; return hrSuccess; } /************************************************************************* * * - ABUBUTT_QueryInterface - * * Allows QI'ing to IUnknown and IMAPIControl. * */ STDMETHODIMP ABUBUTT_QueryInterface( LPABUBUTT lpABUButt, REFIID lpiid, LPVOID FAR * lppNewObj ) { HRESULT hResult = hrSuccess; /* Minimally validate the lpABUButt parameter */ if (IsBadReadPtr(lpABUButt, SIZEOF(ABUBUTT))) { hResult = ResultFromScode(E_INVALIDARG); goto out; } if (lpABUButt->lpVtbl != &vtblABUBUTT) { hResult = ResultFromScode(E_INVALIDARG); goto out; } /* Check the other parameters */ if (!lpiid || IsBadReadPtr(lpiid, (UINT) SIZEOF(IID))) { hResult = ResultFromScode(E_INVALIDARG); goto out; } if (IsBadWritePtr(lppNewObj, (UINT) SIZEOF(LPVOID))) { hResult = ResultFromScode(E_INVALIDARG); goto out; } /* See if the requested interface is one of ours */ if ( memcmp(lpiid, &IID_IUnknown, SIZEOF(IID)) && memcmp(lpiid, &IID_IMAPIControl, SIZEOF(IID)) ) { *lppNewObj = NULL; /* OLE requires zeroing [out] parameter */ hResult = ResultFromScode(E_NOINTERFACE); goto out; } /* We'll do this one. Bump the usage count and return a new pointer. */ EnterCriticalSection(&lpABUButt->cs); ++lpABUButt->lcInit; LeaveCriticalSection(&lpABUButt->cs); *lppNewObj = lpABUButt; out: DebugTraceResult(ABUBUTT_QueryInterface, hResult); return hResult; } /* - ABUBUTT_Release - * Releases and cleans up this object */ STDMETHODIMP_(ULONG) ABUBUTT_Release(LPABUBUTT lpABUButt) { long lcInit; /* Minimally validate the lpABUButt parameter */ if (IsBadReadPtr(lpABUButt, SIZEOF(ABUBUTT))) { return 1; } if (lpABUButt->lpVtbl != &vtblABUBUTT) { return 1; } EnterCriticalSection(&lpABUButt->cs); lcInit = --lpABUButt->lcInit; LeaveCriticalSection(&lpABUButt->cs); if (lcInit == 0) { /* * Release my parent */ // lpABUButt->lpABC->lpVtbl->Release(lpABUButt->lpABC); /* * Destroy the critical section for this object */ DeleteCriticalSection(&lpABUButt->cs); /* * Set the Jump table to NULL. This way the client will find out * real fast if it's calling a method on a released object. That is, * the client will crash. Hopefully, this will happen during the * development stage of the client. */ lpABUButt->lpVtbl = NULL; /* * Free the object */ lpABUButt->lpFreeBuff(lpABUButt); return 0; } return lcInit; } /************************************************************************* * * - ABUBUTT_Activate - * Called when the user presses the button * * */ STDMETHODIMP ABUBUTT_Activate( LPABUBUTT lpABUButt, ULONG ulFlags, ULONG ulUIParam ) { SCODE sc = SUCCESS_SUCCESS; switch (lpABUButt->ulPropTag) { default: DebugTraceSc(ABUBUTT_Activate, lpABUButt->ulPropTag); return ResultFromScode (E_FAIL); } return hrSuccess; } /************************************************************************* * * - ABUBUTT_GetState - * Called by the client to find out if the button is enabled or disabled. * * */ STDMETHODIMP ABUBUTT_GetState( LPABUBUTT lpABUButt, ULONG ulFlags, ULONG * lpulState ) { switch (lpABUButt->ulPropTag) { default: DebugTraceSc(ABUBUTT_GetState, lpABUButt->ulPropTag); return ResultFromScode (E_FAIL); } return hrSuccess; } #undef _FAXAB_ABUSER