SMHCFG.C

/* 
* S M H C F G . C
*
* Sample mail handling hook configuration
* Copyright 1992-95 Microsoft Corporation. All Rights Reserved.
*/

#include "_pch.h"

/*
* Configuration Properties
*
* sptConfigProps
*
* This is the set of properties that we expect to find in the
* provider's profile section.
*
* sptRule
*
* This is the set of properties that are expected to be found in
* each rule's profile section.
*/
const SizedSPropTagArray (cpMax, sptConfigProps) =
{
cpMax,
{
PR_PROFILE_NAME,
PR_SMH_FLAGS,
PR_SMH_RULES,
PR_SMH_RULE_NAMES,
PR_SMH_OOF_TEXT,
PR_SMH_OOF_ENABLED,
PR_SMH_OOF_RTF,
PR_SMH_SOUND_SCHEMES,
PR_SMH_REPFWD_SCHEMES,
PR_SMH_EXCLUSIONS
}
};
const SizedSPropTagArray (cpRLMax, sptRule) =
{
cpRLMax,
{
PR_DISPLAY_NAME,
PR_RULE_TYPE,
PR_RULE_DATA,
PR_RULE_FLAGS,
PR_RULE_TARGET_ENTRYID,
PR_RULE_TARGET_PATH,
PR_RULE_STORE_ENTRYID,
PR_RULE_STORE_DISPLAY_NAME,
PR_RULE_SOUND_LOPRI,
PR_RULE_SOUND_NORMAL,
PR_RULE_SOUND_HIPRI,
PR_RULE_FORWARD_RECIP,
PR_RULE_FORWARD_RECIP_ENTRYID,
PR_RULE_REP_FWD_RTF,
PR_RULE_REP_FWD_TEXT
}
};


/*
* Configuration event name
*/
const TCHAR lpszConfigEvt[] = "SMH_CONFIGURATION_EVENT";


/*
* HrOpenSingleProvider()
*
* Purpose:
*
* Opens the profile section of this service provider. This is done
* by opening the message service table and querying all rows.
* Since the SMH service contains only one provider, there is only
* one row in the table. This contains our profile section. Use
* the PR_PROFILE_UID to open the section.
*
* Arguments:
*
* lpadmin the IProfileAdmin object
* lppprof [OUT] on return contains the profile section obj
*
* Returns:
*
* (HRESULT)
* lppprof [OUT] contains the profile section object iff the call
* is successful.
*/
HRESULT
HrOpenSingleProvider (LPPROVIDERADMIN lpadmin, LPPROFSECT FAR * lppprof)
{
HRESULT hr;
LPMAPITABLE lptbl = NULL;
LPSRowSet lprws = NULL;
SPropTagArray sptProvider = {1, {PR_PROVIDER_UID}};
SRestriction res = {0};

res.rt = RES_EXIST;
res.res.resExist.ulPropTag = PR_PROVIDER_DLL_NAME;

hr = lpadmin->lpVtbl->GetProviderTable (lpadmin, 0, &lptbl);
if (!HR_FAILED (hr))
{
hr = lptbl->lpVtbl->SetColumns (lptbl, &sptProvider, 0L);
if (!HR_FAILED (hr))
{
hr = lptbl->lpVtbl->Restrict (lptbl, &res, 0);
if (!HR_FAILED (hr))
{
hr = lptbl->lpVtbl->QueryRows (lptbl, 10, 0, &lprws);
if (!HR_FAILED (hr) && lprws->cRows)
{
Assert (lprws->cRows == 1);
Assert (lprws->aRow[0].cValues == 1);
Assert (lprws->aRow[0].lpProps);
Assert (lprws->aRow[0].lpProps[0].ulPropTag == PR_PROVIDER_UID);
hr = lpadmin->lpVtbl->OpenProfileSection (lpadmin,
(LPMAPIUID)lprws->aRow[0].lpProps[0].Value.bin.lpb,
NULL,
MAPI_MODIFY,
lppprof);
MAPIFreeBuffer (lprws->aRow[0].lpProps);
}
MAPIFreeBuffer (lprws);
}
}
UlRelease (lptbl);
}
DebugTraceResult (HrOpenSingleProvider, hr);
return hr;
}


/*
* HrMergeValues()
*
* Purpose:
*
* Merges two property value arrays into one. By making a copy of
* the first and then adding/replacing props from the second.
*
* IMPORTANT: if there are any property values that are separately
* allocated with either MAPIAllocateBuffer() or MAPIAllocateMore(),
* then these property values must remain valid as long as the
* merged property array is expected to be valid.
*
* Returns:
*
* (HRESULT)
*/
HRESULT
HrMergeValues (ULONG cval1,
LPSPropValue lpval1,
ULONG cval2,
LPSPropValue lpval2,
LPALLOCATEBUFFER lpfnAlloc,
ULONG FAR * lpcvalMerged,
LPSPropValue FAR * lppvalMerged)
{
SCODE sc;
LPSPropValue lpval = NULL;
UINT cb;
UINT ip;

cb = (UINT)(cval1 + cval2) * sizeof(SPropValue);
sc = (*lpfnAlloc)(cb, &lpval);
if (!FAILED (sc))
{
/* Slurp the original data across */

memcpy (lpval, lpval1, (UINT)cval1 * sizeof(SPropValue));

/* Move the new stuff over */

while (cval2--)
{
for (ip = 0; ip < (UINT)cval1; ip++)
{
/* See if we match properties */

if (PROP_ID (lpval[ip].ulPropTag) == PROP_ID (lpval2[cval2].ulPropTag))
{
/* We matched, but are we a real property */

if (PROP_TYPE (lpval2[cval2].ulPropTag) != PT_ERROR)
lpval[ip] = lpval2[cval2];
break;
}
}
if (ip == cval1)
lpval[cval1++] = lpval2[cval2];
}
*lpcvalMerged = cval1;
*lppvalMerged = lpval;
}
DebugTraceSc (HrMergeValues(), sc);
return ResultFromScode (sc);
}


/*
* HrUpdateProfileFormat()
*
* Purpose:
*
* Converts the WMS level of SMH profile properties to post WMS
* levels. Namely rules are now stored via a pair of properties
* in the profile. PR_SMH_RULES has been changed to a multi-valued
* binary property and PR_SMH_RULE_NAMES contains the display names
* for the rules. The prevents the configuration functions from
* opening all profile sections to build the list of rules.
*
* Arguments:
*
* lpvOPSCtxt Context passed to OpenProfileSection()
* (a session or an profadmin object)
* lpfnOpenProfSect Pointer to OpenProfileSection()
* lpfnMore MAPIAllocateMore() function
* lpfnFree MAPIFreeBuffer() function
* lpvalNew propvalue array for the converted values
* lpvalOld propvalue of the old PR_SMH_RULES
*
* Returns:
*
* (HRESULT)
*/

HRESULT
HrUpdateProfileFormat (LPVOID lpvOPSCtxt,
LPOPENPROFSECT lpfnOpenProfSect,
LPALLOCATEMORE lpfnMore,
LPFREEBUFFER lpfnFree,
LPSPropValue lpvalNew,
LPSPropValue lpvalOld)
{
SCODE sc = S_OK;
LPMAPIUID lpmuid;
LPPROFSECT lpsec = NULL;
LPSBinary lpbin;
LPSPropValue lpval = NULL;
LPTSTR FAR * lppsz;
SizedSPropTagArray (2, spt) = {2, {PR_DISPLAY_NAME, PR_RULE_TYPE}};
UINT cb;
UINT crl;
UINT i;
ULONG cval;

DebugTrace ("SMH: updating profile to new format\n");

/* Make sure that the size is an even number of UIDs */

Assert ((lpvalOld->Value.bin.cb % sizeof(MAPIUID)) == 0);
crl = (UINT)lpvalOld->Value.bin.cb / sizeof(MAPIUID);

/* Allocate space for the two new properties */

cb = crl * sizeof(SBinary);
sc = (*lpfnMore) (cb, lpvalNew, &lpbin);
if (FAILED (sc))
goto ret;
memset (lpbin, 0, cb);

cb = crl * sizeof(LPTSTR);
sc = (*lpfnMore) (cb, lpvalNew, (LPVOID FAR *)&lppsz);
if (FAILED (sc))
goto ret;
memset (lppsz, 0, cb);

/* Iterate through the old list building the two new lists as we go */

lpmuid = (LPMAPIUID)lpvalOld->Value.bin.lpb;
for (i = 0; crl--; lpmuid++)
{
/* Open the old profile section and get PR_DISPLAY_NAME
* and PR_RULE_TYPE for conversion purposes. The type
* will be converted and reset into the profile section.
* Thus, completing the conversion for that section.
*
* The display name will be extracted and copied out to
* the external names property and will be saved out when
* all section conversions are completed
*/
if (!HR_FAILED ((*lpfnOpenProfSect) (lpvOPSCtxt, lpmuid, NULL, MAPI_MODIFY, &lpsec)) &&
!HR_FAILED (lpsec->lpVtbl->GetProps (lpsec, (LPSPropTagArray)&spt, 0, &cval, &lpval)) &&
!FAILED ((*lpfnMore) (sizeof(MAPIUID), lpvalNew, &lpbin[i].lpb)) &&
!FAILED ((*lpfnMore) (lstrlen (lpval[0].Value.LPSZ) + 1, lpvalNew, &lppsz[i])))
{
switch (lpval[1].Value.l)
{
case 1:
lpval[1].Value.l = RL_SUBJECT;
break;
case 2:
lpval[1].Value.l = RL_SENDER;
break;
case 4:
lpval[1].Value.l = RL_HAS_ATTACH;
break;
case 8:
lpval[1].Value.l = RL_BODY;
break;
case 16:
lpval[1].Value.l = RL_TO_RECIP;
break;
case 32:
lpval[1].Value.l = RL_CC_RECIP;
break;
case 64:
lpval[1].Value.l = RL_BCC_RECIP;
break;
case 128:
lpval[1].Value.l = RL_ANY_RECIP;
break;
case 256:
lpval[1].Value.l = RL_MSG_CLASS;
break;
}
if (!HR_FAILED (HrSetOneProp ((LPMAPIPROP)lpsec, &lpval[1])))
{
DebugTrace ("SMH: converting '%s' rule\n", lpval[0].Value.LPSZ);
lpbin[i].cb = sizeof(MAPIUID);
memcpy (lpbin[i].lpb, lpmuid, sizeof(MAPIUID));
lstrcpy (lppsz[i], lpval[0].Value.LPSZ);
i++;
}
}
(*lpfnFree) (lpval);
UlRelease (lpsec);
lpval = NULL;
lpsec = NULL;
}
lpvalNew[ipRules].ulPropTag = PR_SMH_RULES;
lpvalNew[ipRules].Value.MVbin.cValues = i;
lpvalNew[ipRules].Value.MVbin.lpbin = lpbin;
lpvalNew[ipNames].ulPropTag = PR_SMH_RULE_NAMES;
lpvalNew[ipNames].Value.MVSZ.cValues = i;
lpvalNew[ipNames].Value.MVSZ.LPPSZ = lppsz;

ret:

DebugTraceSc (HrUpdateProfileFormat(), sc);
return ResultFromScode (sc ? sc : ((i == crl) ? 0 : MAPI_W_ERRORS_RETURNED));
}


/*
* HrGetConfigEvent()
*
* Purpose:
*
* Gets the configuration event handle. The handle is used to
* signal logged in handlers that their configuration has been
* modified and, at the next reasonable chance, should be reloaded.
*
* Called at SMH init time only.
*
* Arguments:
*
* lphevt [OUT] contains the handle iff the call succeeds
*
* Returns:
*
* (HRESULT)
*/
#ifdef _WIN32
HRESULT
HrGetConfigEvent (HANDLE FAR * lphevt)
{
HANDLE hevt = NULL;

if (!(hevt = CreateEvent (NULL, TRUE, FALSE, lpszConfigEvt)) &&
!(hevt = OpenEvent (EVENT_MODIFY_STATE, FALSE, lpszConfigEvt)))
ResultFromScode (MAPI_E_NOT_ENOUGH_RESOURCES);

*lphevt = hevt;
return hrSuccess;
}
#endif /* _WIN32 */


/*
* SignalConfigChanged()
*
* Purpose:
*
* Sets the configuration event such that logged in hooks can update
* their configuration on the fly.
*
* Called from within the service entry when configuration changes
* are commited.
*/
#ifdef _WIN32
VOID
SignalConfigChanged (VOID)
{
HANDLE hevt = NULL;

if (hevt = OpenEvent (EVENT_MODIFY_STATE, FALSE, lpszConfigEvt))
{
SetEvent (hevt);
CloseHandle (hevt);
}
return;
}
#endif /* _WIN32 */


/*
* FConfigChanged()
*
* Purpose:
*
* Tests the configuration event such that logged in hooks can update
* their configuration on the fly if the configuration has changed.
*
* Called from within the SMH object at regualr intervals
*
* Returns:
*
* TRUE iff the config changed
*
*/
#ifdef _WIN32
BOOL
FConfigChanged (HANDLE hevt)
{
ULONG dw;

dw = WaitForSingleObject (hevt, 0);
Assert (dw != WAIT_ABANDONED);
return (dw == WAIT_OBJECT_0);
}
#endif /* _WIN32 */


/*
* SMH_ServiceEntry()
*
* Purpose:
*
* The ServiceEntry() function is the MAPI entry point to configure
* a service for use in a profile. The call can then bring up UI to
* ensure configuration of the SMH provider.
*
* Parameters:
*
* hinst DLL instance
* lpmalloc OLE style allocator (used by PropSheet())
* lpsup MAPI profile support object
* ulUIParam hwnd that is to be used as UI parent
* ulFlags configuration flags
* ulContext configuration action
* cval count of caller supplied properties
* lpval caller supplied properties to be configured
* lpadmin IProviderAdmin object
* lppmerr [OUT] extended error information
*
* Operation:
*
* The ServiceEntry() uses the IProviderAdmin object to open its
* profile section and retrieve the current set of properties. The
* caller supplied properties are then merged into the set of
* current properties.
*
* If either this set of properties is not sufficient for
* configuration or the caller specifically asked for configuration
* UI, then ServiceEntry() will make calls to bring up its config
* UI.
*
* ServiceEntry() recognizes several configuration flags. If
* SERVICE_UI_ALWAYS and/or SERVICE_UI_ALLOWED are set, UI is
* allowed and we be brought up if appropriate. Is
* MSG_SERVICE_UI_READ_ONLY is set, then the UI should not
* allow the configuration to be modified.
*
* The configuration contexts MSG_SERVICE_DELETE, MSG_SERVICE_INSTALL,
* and MSG_SERVICE_UNINSTALL are ignored and no action is taken.
* MSG_SERVICE_CONFIGURE and MSG_SERVICE_CREATE allow the caller to
* create or update the configuration properties in this providers
* profile section.
*
* SMH will not return extended information in the MAPIERROR in case
* of error
*/
HRESULT STDAPICALLTYPE
SMH_ServiceEntry(
HINSTANCE hinst,
LPMALLOC lpmalloc,
LPMAPISUP lpsup,
ULONG ulUIParam,
ULONG ulFlags,
ULONG ulContext,
ULONG cval,
LPSPropValue lpval,
LPPROVIDERADMIN lpadmin,
LPMAPIERROR FAR * lppmerr)
{
HRESULT hr = hrSuccess;
BOOL fUI = FALSE;
LPALLOCATEBUFFER lpfnAlloc = NULL;
LPALLOCATEMORE lpfnAllocMore = NULL;
LPFREEBUFFER lpfnFree = NULL;
LPMAPIUID lpmuid = NULL;
LPPROFSECT lpprof = NULL;
LPPROFSECT lpprofSvc = NULL;
LPSCD lpscd = NULL;
LPSPropValue lpvalCur = NULL;
LPSPropValue lpvalNew = NULL;
LPSPropValue lpvalOld;
SPropValue val;
UINT csec = 0;
UINT i;
ULONG cvalCur;
ULONG cvalNew;
ULONG ulMyFlags;

if ((ulContext == MSG_SERVICE_INSTALL) ||
(ulContext == MSG_SERVICE_UNINSTALL))
goto ret;

if ((ulContext != MSG_SERVICE_CONFIGURE) &&
(ulContext != MSG_SERVICE_CREATE) &&
(ulContext != MSG_SERVICE_DELETE))
{
hr = ResultFromScode (MAPI_E_NO_SUPPORT);
goto ret;
}

if (ulFlags & MAPI_UNICODE)
{
/* Unicode is not supported by SMH */

hr = ResultFromScode (MAPI_E_BAD_CHARWIDTH);
goto ret;
}

/* Find out our UI options */

fUI = !!(ulFlags & SERVICE_UI_ALWAYS);
ulMyFlags = (ulFlags & MSG_SERVICE_UI_READ_ONLY)
? UI_READONLY
: 0;

/* Get memory routines */

hr = lpsup->lpVtbl->GetMemAllocRoutines (lpsup,
&lpfnAlloc,
&lpfnAllocMore,
&lpfnFree);
if (HR_FAILED (hr))
goto ret;

/* Open the profile section */

hr = HrOpenSingleProvider (lpadmin, &lpprof);
if (HR_FAILED (hr))
goto ret;

/* Get the values already in the profile */

hr = lpprof->lpVtbl->GetProps (lpprof,
(LPSPropTagArray)&sptConfigProps,
0,
&cvalCur,
&lpvalCur);
if (HR_FAILED (hr))
goto ret;

/* Check that the rules are stored in the correct format */

if (lpvalCur[ipRules].ulPropTag != PR_SMH_RULES)
{
hr = HrGetOneProp ((LPMAPIPROP)lpprof,
CHANGE_PROP_TYPE(PR_SMH_RULES, PT_BINARY),
&lpvalOld);
if (!HR_FAILED (hr))
{
/* The rules are stored in the wrong format */

hr = HrUpdateProfileFormat (lpadmin,
lpadmin->lpVtbl->OpenProfileSection,
lpfnAllocMore,
lpfnFree,
lpvalCur,
lpvalOld);
(*lpfnFree) (lpvalOld);
if (HR_FAILED (hr))
goto ret;

/* Save out anything we got back */

lpprof->lpVtbl->SetProps (lpprof, cpMax, lpvalCur, NULL);
}
}

if (ulContext != MSG_SERVICE_DELETE)
{
/* Merge what was in the profile with what was passed in */

hr = HrMergeValues (cvalCur,
lpvalCur,
cval,
lpval,
lpfnAlloc,
&cvalNew,
&lpvalNew);
if (HR_FAILED (hr))
goto ret;

/* If we dont have all the props we need, then
* we will have to ask for them
*/
fUI = fUI || (lpvalNew[ipFlags].ulPropTag != PR_SMH_FLAGS);
if ((lpvalNew[ipFlags].ulPropTag != PR_SMH_FLAGS) &&
(!(ulFlags & (SERVICE_UI_ALLOWED | SERVICE_UI_ALWAYS)) ||
(ulFlags & MSG_SERVICE_UI_READ_ONLY)))
{
/* We need UI but can't have it. */

hr = ResultFromScode (MAPI_E_UNCONFIGURED);
goto ret;
}

if (fUI)
{
/* Do the config dialog */

if (!FAILED ((*lpfnAlloc) (sizeof(SCD), &lpscd)))
{
memset (lpscd, 0, sizeof(SCD));
lpscd->hinst = hinst;
lpscd->hwnd = (HWND)ulUIParam;
lpscd->lpfnAlloc = lpfnAlloc;
lpscd->lpfnAllocMore = lpfnAllocMore;
lpscd->lpfnFree = lpfnFree;
lpscd->lpmalloc = lpmalloc;
lpscd->lpval = lpvalNew;
lpscd->lpsec = lpprof;
lpscd->lpsup = lpsup;
lpscd->lpadmin = lpadmin;
lpscd->ulFlags = ulMyFlags;
hr = HrDisplayPropSheets (hinst, (HWND)ulUIParam, lpscd);
}
else
{
hr = ResultFromScode (MAPI_E_NOT_ENOUGH_MEMORY);
goto ret;
}
}

/* Open the service profile section and set PR_SERVICE_EXTRA_UIDS */

if (!HR_FAILED (lpadmin->lpVtbl->OpenProfileSection (lpadmin,
NULL,
NULL,
MAPI_MODIFY,
&lpprofSvc)))
{
/* PR_SERVICE_EXTRA_UIDS is composed of all additional
* sections used by SMH. By setting this value, we gain
* the ability to have the service copied and\or moved
* while having the additional sections move across with
* it.
*/
if (lpvalNew[ipRules].ulPropTag == PR_SMH_RULES)
csec += (UINT)lpvalNew[ipRules].Value.MVbin.cValues;

if (csec && !FAILED ((*lpfnAlloc) (csec * sizeof(MAPIUID), &lpmuid)))
{
for (i = 0; i < csec; i++)
memcpy (&lpmuid[i],
lpvalNew[ipRules].Value.MVbin.lpbin[i].lpb,
sizeof(MAPIUID));

val.ulPropTag = PR_SERVICE_EXTRA_UIDS;
val.Value.bin.cb = csec * sizeof(MAPIUID);
val.Value.bin.lpb = (LPBYTE)lpmuid;
lpprofSvc->lpVtbl->SetProps (lpprofSvc, 1, &val, NULL);
lpprofSvc->lpVtbl->SaveChanges (lpprofSvc, 0);
(*lpfnFree) (lpmuid);
}
UlRelease (lpprofSvc);
}
}

ret:

#ifdef _WIN32
if (!HR_FAILED (hr))
SignalConfigChanged ();
#endif

/* Cleanup what was left behind */

if (lpscd)
{
for (i = 0; i < lpscd->crl; i++)
{
(*lpfnFree) (lpscd->lppsz[i]);
(*lpfnFree) (lpscd->lpbin[i].lpb);
}
(*lpfnFree) (lpscd->lppsz);
(*lpfnFree) (lpscd->lpbin);
(*lpfnFree) (lpscd);
}

if (lpfnFree)
{
(*lpfnFree) (lpvalCur);
(*lpfnFree) (lpvalNew);
}

UlRelease (lpprof);
DebugTraceResult (SMH_ServiceEntry, hr);
return hr;
};