windows-nt/Source/XPSP1/NT/printscan/fax/exchange/ab/abuser.c
2020-09-26 16:20:57 +08:00

1447 lines
38 KiB
C

/***********************************************************************
*
* 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 <wchar.h>
#else
#include <stdio.h>
#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