MSPLOGON.C

/* 
* M S P L O G O N . C
*
* Logon to a MAPI Sample Message Store.
*
* Copyright 1992-1995 Microsoft Corporation. All Rights Reserved.
*/

#include "msp.h"
#include "msprc.h"
#include <stddef.h>

#include "..\common\wrap3d.h"

#define MSL_CheckParameters(pobj, intf, method, arglist) \
OBJ_CheckParameters(pobj, intf, method, sizeof(MSL), &vtblMSL, arglist)
#define MSP_CheckParameters(pobj, intf, method, arglist) \
OBJ_CheckParameters(pobj, intf, method, sizeof(MSP), &vtblMSP, arglist)

#ifndef MB_SETFOREGROUND
#define MB_SETFOREGROUND 0
#endif

#define MSL_EnterCriticalSection(pmsl) OBJ_EnterCriticalSection((POBJ)pmsl)
#define MSL_LeaveCriticalSection(pmsl) OBJ_LeaveCriticalSection((POBJ)pmsl)

MSL_Vtbl vtblMSL =
{
(MSL_QueryInterface_METHOD *) OBJ_QueryInterface,
(MSL_AddRef_METHOD *) OBJ_AddRef,
MSL_Release,
(MSL_GetLastError_METHOD *) IMS_GetLastError,
MSL_Logoff,
(MSL_OpenEntry_METHOD *) IMS_OpenEntry,
(MSL_CompareEntryIDs_METHOD *) IMS_CompareEntryIDs,
(MSL_Advise_METHOD *) IMS_Advise,
(MSL_Unadvise_METHOD *) IMS_Unadvise,
MSL_OpenStatusEntry
};

MAPIUID uidProvider = SMPMS_UID_PROVIDER;

/* Manifest constants */

#define ACCTSIZE 16 /* Our choice for maximum password length. */

static SPropTagArray sptaPass =
{
1, {PR_SMS_PASSWORD}
};

TCHAR szPropFileName[] = TEXT("msgstore.prp");
TCHAR szFolderFileName[] = TEXT("recfldr.stg");
TCHAR szProviderDisplayName[] = TEXT("Sample Message Store");
TCHAR szDisplayPrefix[] = TEXT("Sample store at ");

/* property tags needed for service creation or configuration */
static SizedSPropTagArray(4,ptaConfig) = {4, { PR_SMS_PATH,
PR_SMS_PASSWORD,
PR_SMS_REMEMBER_PW,
PR_SMS_CREATE } };

/* property tags that should be removed from the profile after */
/* service creation or configuration */
static SizedSPropTagArray(2,ptaDel)= {2, { PR_SMS_REMEMBER_PW,
PR_SMS_CREATE } };

/* service parameter block */
typedef struct _ServicePBLK
{
PMSP pmsp;
LPPROFSECT pps;
LPMAPISUP psup;
ULONG ulUIParam;
ULONG ulContext;
ULONG ulSEFlags;
ULONG cvalsProf;
LPSPropValue pvalsProf;
ULONG cvalsClient;
LPSPropValue pvalsClient;
} SERVICEPBLK, FAR * PSERVICEPBLK;


#define LGNCRED_PATH ((ULONG) 0x00000001)
#define LGNCRED_PASS ((ULONG) 0x00000002)
#define LGNCRED_CREATE ((ULONG) 0x00000004)
#define LGNCRED_REMEMBER ((ULONG) 0x00000008)

/* Typedefs */

typedef struct _LGNCRED
{
ULONG ulFieldMask;
TCHAR szPath[MAX_PATH];
TCHAR szPass[ACCTSIZE];
BOOL fCreate;
BOOL fRemember;
BOOL fCancel;
LPVOID lpCtl3d;
} LGNCRED, * PLGNCRED;

typedef struct _DLGPSWD
{
TCHAR szPath[MAX_PATH];
TCHAR szPass[ACCTSIZE];
LPMAPISUP psup;
PLMR plmr;
BOOL fRemember;
BOOL fCancel;
LPVOID lpCtl3d;
} DLGPSWD, * PDLGPSWD;

/* Internal function prototypes */

static HRESULT HrOpenSingleProvider(LPPROVIDERADMIN padp,
LPPROFSECT FAR *lppProfSect);
static SCODE ScCreateOrConfigure(PSERVICEPBLK pspb);
static SCODE ScCreateStore(PSERVICEPBLK pspb, LPTSTR szPath,
LPTSTR szPass, PMSL * ppmsl, PIMS * ppims);
static BOOL FAlreadyOpenStore(PMSP pmsp, LPTSTR szPath, PIMS * ppims);

static HRESULT HrGetLogonDlgCred(HINSTANCE hInst, ULONG ulUIParam, LPTSTR szPath,
LPTSTR szPass, BOOL *pfCreate, BOOL *pfRemember);
STDAPI_(BOOL) LogonDlgProc(HWND hDlg, UINT message, WPARAM wParam,
LPARAM lParam);
static HRESULT HrGetPassword(HINSTANCE hInst, LPMAPISUP psup, PLMR plmr,
ULONG ulUIParam, LPTSTR szPath, LPTSTR szPass, BOOL *pfRemember);
STDAPI_(BOOL) PasswordDlgProc(HWND hDlg, UINT message, WPARAM wParam,
LPARAM lParam);
static HRESULT HrGetProfileCred(PLMR plmr, LPPROFSECT pps, LPTSTR szPass,
BOOL *pfFound);
static HRESULT HrSetProfileCred(LPPROFSECT pps, LPTSTR szPass, LPTSTR szPath,
BOOL fRemember, ULONG cbEntryID, LPBYTE lpEntryID, LPMAPIUID puidRK);
static HRESULT HrConfirmCred(LPTSTR szPath, LPTSTR szPass, LPMAPISUP psup,
PLMR plmr);
static HRESULT HrFilePathExists(LPTSTR szStorePath, LPTSTR szFileName);
static HRESULT HrCheckStoreIntegrity(LPTSTR szPath);
static BOOL FIsValidPath(LPTSTR szPath, BOOL fCreate);
static void DecodeSpoolSecurity(ULONG cbSpoolSecurity, LPBYTE pbSpoolSecurity,
LPTSTR szPath, LPTSTR szPass);
static HRESULT HrGetSpoolSecurity(LPTSTR szPath, LPTSTR szPass,
PLMR plmr, ULONG * lpcbSpoolSecurity, LPBYTE * lppbSpoolSecurity);
static HRESULT HrCheckAndOpenStore(PMSP pmsp, LPMAPISUP psup, BOOL fModify,
LPPROFSECT pps, LPTSTR szPath, LPTSTR szPass, BOOL fIsSpooler,
PMSL *ppmsl, PIMS *ppims);
static SCODE ScCreateMSL(PMSP pmsp, PMSL * ppmsl);
static BOOL FFindPropInPSPB(PSERVICEPBLK pspb, ULONG ulPT, LPSPropValue *ppval);
static SCODE ScGetFullFileName(PSERVICEPBLK pspb, BOOL *pfCreate,
LPSTR pszPath);

/*
* MSP_Logon
*
* Purpose:
* Logs in one user to the message store and returns a logon object
* and a message store object. Gets credentials through the
* profile or a dialog. Prompts if more information is needed.
*
* Arguments:
* pmsp MSPROVIDER object returned at INIT time.
* psup Pointer to MAPI Support object.
* ulUIParam Handle to parent window cast to a ULONG.
* szProfileName String with the name of profile.
* (UNICODE if MAPI_UNICODE flag set in ulFlags)
* cbEntryID Size of lpEntryID.
* lpEntryID Provider component of store resource EntryID.
* ulFlags Flags for access methods:
* MDB_WRITE Write access.
* MAPI_BEST_ACCESS Write access if possible.
* MDB_NO_DIALOG No logon dialog.
* MAPI_UNICODE If szProfileName UNICODE
* piid Identifies what interface is desired on the
* about-to-be-opened object.
* lpcbSpoolSecurity Address of the variable in which the
* provider returns size of *lppbSpoolSecurity.
* lppbSpoolSecurity Address to return validation data
* necessary for SpoolerLogon to
* piggyback off this logon session.
* lpulLowLevelError Possible low level error to return if
* error or MAPI_W_EXTENDED_WARNING is returned.
* pszMessage Error / Warning string on logon. If MAPI_UNICODE
* is set in ulFlags then it is UNICODE.
* ppmslogon Address to return a pointer to MSLOGON
* object for use by MAPI.DLL.
* ppmdb Address of a pointer to the message
* store object for use by the client.
*
* Returns:
* HRESULT
*
* Side effects:
* May put up logon screen to select/open store if
* MDB_NO_DIALOG is not set. For store creation, Logon
* creates "hidden" files for MDB management.
*
* Errors:
* MAPI_E_NOT_ENOUGH_MEMORY Could not allocate memory.
* MAPI_E_CALL_FAILED Failure creating a new store.
* MAPI_E_CALL_FAILED Windowing system failure trying
* to display logon UI.
* MAPI_E_CORRUPT_STORE One of the necessary store
* files is missing.
* MAPI_E_LOGON_FAILED Insufficient or bad credentials.
*/
STDMETHODIMP
MSP_Logon(PMSP pmsp, LPMAPISUP psup, ULONG ulUIParam, LPSTR szProfileName,
ULONG cbEntryID, LPENTRYID lpEntryID, ULONG ulFlags, LPCIID piid,
ULONG * lpcbSpoolSecurity, LPBYTE * lppbSpoolSecurity,
LPMAPIERROR * lppMapiError, LPMSLOGON * ppmslogon, LPMDB * ppmdb)
{
HRESULT hr = hrSuccess;
HRESULT hrT;
BOOL fRemember = FALSE; /* Remember password in profile? */
BOOL fFoundPassword; /* TRUE if the password was in the profile */
PIMS pims = NULL; /* Internal IMsgStore implementation */
PMSL pmsl = NULL; /* logon object to give to MAPI */
TCHAR szPath[MAX_PATH]; /* Path to store root */
TCHAR szPass[ACCTSIZE]; /* Store account password */
PEID peid = (PEID) lpEntryID;
LPPROFSECT pps = NULL; /* our profile section */

MSP_CheckParameters(
pmsp,
IMSProvider,
Logon,
(pmsp,
psup,
ulUIParam,
szProfileName,
cbEntryID,
lpEntryID,
ulFlags,
piid,
lpcbSpoolSecurity,
lppbSpoolSecurity,
lppMapiError,
ppmslogon,
ppmdb));

#ifdef VALIDATE
if (ulFlags & MAPI_UNICODE)
return ResultFromScode(MAPI_E_BAD_CHARWIDTH);
#endif

MSP_EnterCriticalSection(pmsp);

AssertSz(!pmsp->fInvalid, "This MSProvider has already been shutdown");

*lpcbSpoolSecurity = 0L;
*lppbSpoolSecurity = NULL;
*lppMapiError = NULL;
*ppmslogon = NULL;
*ppmdb = NULL;

if (piid && !FQueryInterface(OT_MSGSTORE, piid))
{
hr = ResultFromScode(E_NOINTERFACE);
goto exit;
}

hr = psup->lpVtbl->OpenProfileSection(psup, NULL, MAPI_MODIFY, &pps);
if (hr != hrSuccess)
goto exit;

/* Get the full path to the store out of the EntryID */

if (peid == NULL)
{
hr = ResultFromScode(MAPI_E_UNCONFIGURED);
goto exit;
}
else if (FIsInvalidEID(cbEntryID, peid, NULL))
{
hr = ResultFromScode(MAPI_E_INVALID_PARAMETER);
goto exit;
}

lstrcpy(szPath, peid->szPath);

DebugTrace("\nSMS: MSP_Logon %s\n\n", szPath);

/* If we can piggyback on an existing open message store on this */
/* session then do so. If we piggyback, then don't return a logon */
/* object or addref the support object. */

if (FAlreadyOpenStore(pmsp, szPath, &pims))
{
DebugTrace("\nSMS: Piggybacking client logon\n");
Assert(hr == hrSuccess);
goto exit;
}

/* Get store credentials from profile. */

hr = HrGetProfileCred(&pmsp->lmr, pps, szPass, &fFoundPassword);
if (hr != hrSuccess)
goto exit;

/* If the password was in the profile, then the user wanted us to */
/* remember it (that's the only way it would be in the profile). */

if (fFoundPassword)
fRemember = TRUE;

while (TRUE)
{
SCODE sc;

hr = HrCheckAndOpenStore(pmsp, psup,
!!(ulFlags & (MDB_WRITE | MAPI_BEST_ACCESS)), pps, szPath,
szPass, FALSE, &pmsl, &pims);
if (hr == hrSuccess)
break;

sc = GetScode(hr);

/* Preset hr to the value that will cause MAPI to bring up our */
/* config UI */

hr = ResultFromScode(MAPI_E_UNCONFIGURED);

if (!(ulFlags & MDB_NO_DIALOG))
{
if (sc == MAPI_E_LOGON_FAILED)
{
hr = HrGetPassword(pmsp->hInst, psup, &pmsp->lmr, ulUIParam,
szPath, szPass, &fRemember);
}
else if (sc == MAPI_E_NOT_FOUND)
{
MessageBox(NULL, "The path to the message store is invalid.",
"Sample Message Store",
MB_OK | MB_TASKMODAL | MB_SETFOREGROUND);
}
}

if (hr != hrSuccess)
goto exit;
}

/* Complete regular logon tasks. */

/* Update saved credentials if they have changed. */

hrT = HrSetProfileCred(pps, szPass, szPath, fRemember,
pims->eidStore.cb, pims->eidStore.lpb, &pims->uidResource);

if (hrT == hrSuccess)
hrT = pps->lpVtbl->SaveChanges(pps, 0);

/* Make the profile section permanent. */
if (hrT == hrSuccess)
hrT = psup->lpVtbl->ModifyProfile(psup, 0);

hr = psup->lpVtbl->SetProviderUID(psup, &pims->uidResource, 0L);
if (hr != hrSuccess)
goto exit;

hr = HrSetupPrivateNotifications(pims);
if (hr != hrSuccess)
goto exit;

/* This call sets [out] parameters, and should therefore be the last */
/* call that could fail. */
hr = HrGetSpoolSecurity(szPath, szPass, &pims->lmr,
lpcbSpoolSecurity, lppbSpoolSecurity);

if (hr != hrSuccess)
goto exit;

/* Add this new store to the list of open stores. */
OBJ_Enqueue((POBJ) pims, (POBJ) pmsp);

UlAddRef(psup); /* I'm keeping a reference */

Assert(hr == hrSuccess); /* no error */

exit:
if (hr == hrSuccess)
{
/* Fill in [out] parameters. */
*ppmdb = (LPMDB) pims;
*ppmslogon = (LPMSLOGON) pmsl;
}
else
{
UlRelease(pims);

/* Releasing the pmsl must come after releasing the pims. */
UlRelease(pmsl);

Assert(*lpcbSpoolSecurity == 0L);
Assert(*lppbSpoolSecurity == NULL);
Assert(*lppMapiError == NULL);
Assert(*ppmslogon == NULL);
Assert(*ppmdb == NULL);
}

UlRelease(pps);

MSP_LeaveCriticalSection(pmsp);

DebugTraceResult(MSP_Logon, hr);
return HrCheckHr(hr, IMSProvider_Logon);
}

/*
* MSP_SpoolerLogon
*
* Purpose:
* Logs in the Spooler to this message store.
*
* Arguments:
* psup Pointer to MAPI Support object.
* ulUIParam HWND of parent window
* szProfileName Name of profile
* cbEntryID Size of lpEntryID.
* lpEntryID Provider component of store resource
* EntryID.
* ulFlags Flags. See Logon above, except
* MDB_NO_DIALOG is normally (always?)
* set on this call.
* piid IID of desired interface for ppmdb
* cbSpoolSecurity Size of data in lpbSpoolSecurity.
* lpbSpoolSecurity Validation data necessary for
* SpoolerLogon to complete.
* lpulLowLevelError Possible low level error to return if
* error or MAPI_W_EXTENDED_WARNING is returned.
* pszMessage Error / Warning string on logon. If MAPI_UNICODE
* is set in ulFlags then it is UNICODE.
* ppmslogon Address to return a pointer to MSLOGON
* object for use by MAPI.DLL.
* ppmdb Address of a pointer to the Spooler
* Message Store Object.
*
* Returns:
* HRESULT
*
* Side effects:
* May put up logon screen to select/open store if
* MDB_NO_DIALOG is not set. Calls HrNewIMS to create the
* message store object, which allocates memory.
*
* Errors:
* MAPI_E_NO_MEMORY Could not allocate memory.
* MAPI_E_LOGON_FAILED Bad credentials
*/
STDMETHODIMP
MSP_SpoolerLogon(PMSP pmsp, LPMAPISUP psup, ULONG ulUIParam,
LPTSTR szProfileName, ULONG cbEntryID, LPENTRYID lpEntryID,
ULONG ulFlags, LPCIID piid, ULONG cbSpoolSecurity,
LPBYTE lpbSpoolSecurity, LPMAPIERROR * lppMapiError,
LPMSLOGON * ppmslogon, LPMDB * ppmdb)
{
TCHAR szPass[ACCTSIZE]; /* Store account password */
TCHAR szPath[MAX_PATH]; /* path to store root */
HRESULT hr = hrSuccess;
PIMS pims = NULL;
PMSL pmsl = NULL;
LPPROFSECT pps = NULL; /* our profile section */

MSP_CheckParameters(
pmsp,
IMSProvider,
SpoolerLogon,
(pmsp,
psup,
ulUIParam,
szProfileName,
cbEntryID,
lpEntryID,
ulFlags,
piid,
cbSpoolSecurity,
lpbSpoolSecurity,
lppMapiError,
ppmslogon,
ppmdb));

#ifdef VALIDATE
if (ulFlags & MAPI_UNICODE)
return ResultFromScode(MAPI_E_BAD_CHARWIDTH);
#endif

MSP_EnterCriticalSection(pmsp);

AssertSz(!pmsp->fInvalid, "This MSProvider has already been shutdown");

*lppMapiError = NULL;
*ppmslogon = NULL;
*ppmdb = NULL;

if (piid && !FQueryInterface(OT_MSGSTORE, piid))
{
hr = ResultFromScode(MAPI_E_INTERFACE_NOT_SUPPORTED);
goto exit;
}

hr = psup->lpVtbl->OpenProfileSection(psup, NULL, MAPI_MODIFY, &pps);
if (hr != hrSuccess)
goto exit;

DecodeSpoolSecurity(cbSpoolSecurity, lpbSpoolSecurity, szPath, szPass);

DebugTrace("\nSMS: MSP_SpoolerLogon %s\n\n",szPath);

/* If we can piggyback on an existing open message store */
/* on this session then do so. If we piggyback, then don't return */
/* a logon object or addref the support object. */

if (FAlreadyOpenStore(pmsp, szPath, &pims))
{
DebugTrace("\nSMS: Piggybacking spooler logon\n");
goto exit;
}


hr = HrCheckAndOpenStore(pmsp, psup, TRUE, pps, szPath, szPass, TRUE,
&pmsl, &pims);
if (hr == hrSuccess)
hr = psup->lpVtbl->SetProviderUID(psup, &pims->uidResource, 0L);
if (hr == hrSuccess)
hr = HrSetupPrivateNotifications(pims);

if (hr == hrSuccess)
{
/* Add this new store to the list of open stores. */
OBJ_Enqueue((POBJ) pims, (POBJ) pmsp);
UlAddRef(psup);
}

exit:
if (hr == hrSuccess)
{
*ppmdb = (LPMDB) pims;
*ppmslogon = (LPMSLOGON) pmsl;
}

UlRelease(pps);

MSP_LeaveCriticalSection(pmsp);

DebugTraceResult(MSP_SpoolerLogon, hr);
return hr;
}

/*
- MSL_Release
-
*/

STDMETHODIMP_(ULONG) MSL_Release(PMSL pmsl)
{
LONG cRef;

MSL_EnterCriticalSection(pmsl);

AssertSz1(pmsl->cRef > 0, "MSL_Release(): Bogus cRef (%08lX)", pmsl->cRef);

cRef = --pmsl->cRef;

MSL_LeaveCriticalSection(pmsl);

if (cRef == 0)
{
Assert(pmsl->pims == NULL);
DeleteCriticalSection(&pmsl->cs);
LMFree(&pmsl->lmr, pmsl);
}

return (cRef);
}

/*
* MSL_Logoff()
*/

STDMETHODIMP MSL_Logoff(PMSL pmsl, ULONG * pulFlags)
{
MSL_CheckParameters(
pmsl,
IMSLogon,
Logoff,
(pmsl,
pulFlags));

MSL_EnterCriticalSection(pmsl);

AssertSz(!pmsl->fInvalid, "Logon object already logged off");

if (pmsl->pims)
OBJ_Destroy((POBJ) pmsl->pims);
else
MSL_LeaveCriticalSection(pmsl);

#ifdef DEBUG
pmsl->fInvalid = TRUE;
#endif

return (hrSuccess);
}

/*
* MSL_OpenStatusEntry()
*
* MAPI has determined that the client call to OpenStatusEntry
* is for OUR row in the status table. Open the Status Object
* appropriate for our store.
*
* This object is logically part of the store, and can be made
* invalid when the store (either the MSLogon or LPMDB object,
* at our choice) goes away.
*/

STDMETHODIMP MSL_OpenStatusEntry(PMSL pmsl, LPCIID lpiid, ULONG ulFlags,
ULONG * lpulObjType, LPVOID * lppEntry)
{
MSL_CheckParameters(
pmsl,
IMSLogon,
OpenStatusEntry,
(pmsl,
lpiid,
ulFlags,
lpulObjType,
lppEntry));

AssertSz(!pmsl->fInvalid, "Logon object already logged off");

DebugTraceSc(MSL_OpenStatusEntry, MAPI_E_NO_SUPPORT);
return ResultFromScode(MAPI_E_NO_SUPPORT);
}

STDAPI SMSServiceEntry(HINSTANCE hInst, LPMALLOC lpMalloc, LPMAPISUP psup,
ULONG ulUIParam, ULONG ulSEFlags, ULONG ulContext, ULONG cvals,
LPSPropValue pvals, LPPROVIDERADMIN lpAdminProviders,
LPMAPIERROR FAR *lppMapiError)
{
HRESULT hr;
LPALLOCATEBUFFER lpAllocBuff;
LPALLOCATEMORE lpAllocMore;
LPFREEBUFFER lpFreeBuff = NULL;
PMSP pmsp = NULL;
LPPROFSECT pps = NULL;
SERVICEPBLK spb;
ULONG ulProvVer;
ULONG cvalsProf;
LPSPropValue pvalsProf = NULL;

/* check the array of profile section objects */
if (IsBadReadPtr(psup, sizeof(LPMAPISUP))
|| (cvals && IsBadReadPtr(pvals, (UINT)(sizeof(SPropValue) * cvals))))
return ResultFromScode(MAPI_E_INVALID_PARAMETER);

if (ulSEFlags & ~(MAPI_UNICODE
| MSG_SERVICE_UI_READ_ONLY
| SERVICE_UI_ALWAYS /* Same value as UI_SERVICE */
| SERVICE_UI_ALLOWED /* New */
| UI_CURRENT_PROVIDER_FIRST
| SERVICE_LOGON_FAILED))
return ResultFromScode(MAPI_E_UNKNOWN_FLAGS);

if (ulSEFlags & MAPI_UNICODE)
return ResultFromScode(MAPI_E_BAD_CHARWIDTH);

if ( (ulContext == MSG_SERVICE_INSTALL)
|| (ulContext == MSG_SERVICE_UNINSTALL)
|| (ulContext == MSG_SERVICE_DELETE))
return (hrSuccess);

if (ulContext != MSG_SERVICE_CONFIGURE && ulContext != MSG_SERVICE_CREATE)
return (ResultFromScode(MAPI_E_NO_SUPPORT));

/* get the profile section for the provider */
hr = HrOpenSingleProvider(lpAdminProviders, &pps);
if (hr != hrSuccess)
goto err;

/* Get the memory allocation routines we'll be needing. */
hr = psup->lpVtbl->GetMemAllocRoutines(psup, &lpAllocBuff, &lpAllocMore,
&lpFreeBuff);
if (hr != hrSuccess)
goto err;

hr = MSProviderInit(hInst, lpMalloc, lpAllocBuff, lpAllocMore,
lpFreeBuff, 0, CURRENT_SPI_VERSION, &ulProvVer,
&(LPMSPROVIDER)pmsp);
if (hr != hrSuccess)
goto err;

/* get the properties out of the profile section that we will be */
/* be needing */

hr = pps->lpVtbl->GetProps(pps, (LPSPropTagArray) &ptaConfig, 0,
&cvalsProf, &pvalsProf);
if (HR_FAILED(hr))
goto err;

Assert (PROP_ID(pvalsProf[0].ulPropTag) == PROP_ID(PR_SMS_PATH));
Assert (PROP_ID(pvalsProf[1].ulPropTag) == PROP_ID(PR_SMS_PASSWORD));
Assert (PROP_ID(pvalsProf[2].ulPropTag) == PROP_ID(PR_SMS_REMEMBER_PW));
Assert (PROP_ID(pvalsProf[3].ulPropTag) == PROP_ID(PR_SMS_CREATE));

/* initialize the service parameter block */
spb.pmsp = pmsp;
spb.pps = pps;
spb.psup = psup;
spb.ulUIParam = ulUIParam;
spb.ulSEFlags = ulSEFlags;
spb.ulContext = ulContext;
spb.cvalsProf = cvalsProf;
spb.pvalsProf = pvalsProf;
spb.cvalsClient = cvals;
spb.pvalsClient = pvals;

hr = ResultFromScode(ScCreateOrConfigure(&spb));

err:
if (lpFreeBuff)
(*lpFreeBuff)(pvalsProf);

UlRelease(pmsp);
UlRelease(pps);

DebugTraceResult(SMSServiceEntry, hr);
return hr;
}

/*
* Module internal functions
*/

static HRESULT
HrOpenSingleProvider(LPPROVIDERADMIN padp, LPPROFSECT FAR *lppProfSect)
{
HRESULT hr;
LPMAPITABLE pmt = NULL;
LPSRowSet prws = NULL;
LPSPropValue pval;
SPropTagArray ptaProvUID = {1, {PR_PROVIDER_UID}};

hr = padp->lpVtbl->GetProviderTable(padp, 0, &pmt);
if (hr)
goto err;

hr = pmt->lpVtbl->SetColumns(pmt, &ptaProvUID, 0L);
if (hr)
goto err;

hr = pmt->lpVtbl->QueryRows(pmt, 1, 0, &prws);
if (hr || prws == NULL || prws->cRows == 0)
{
hr = ResultFromScode(MAPI_E_NOT_FOUND);
goto err;
}

Assert(prws->aRow[0].cValues == 1);
pval = prws->aRow[0].lpProps;
if (!pval || pval->ulPropTag != PR_PROVIDER_UID)
{
/* The property we need is missing. Error. */
hr = ResultFromScode(MAPI_E_NOT_FOUND);
goto err;
}

/* Found the right property; use it to open our section */
hr = padp->lpVtbl->OpenProfileSection(padp,
(LPMAPIUID) pval->Value.bin.lpb, NULL, MAPI_MODIFY, lppProfSect);

err:
FreeProws(prws);
UlRelease(pmt);

DebugTraceResult(HrOpenSingleProvider, hr);
return hr;
}

static SCODE ScCreateOrConfigure(PSERVICEPBLK pspb)
{
SCODE sc = S_OK;
LPSPropValue pval;
LPSTR pszFileTitle;
CHAR szPath[MAX_PATH];
CHAR szPass[ACCTSIZE];
LPPROFSECT pps = pspb->pps;
BOOL fNeedUI = FALSE;
BOOL fAllowUI = FALSE;
BOOL fCreate = FALSE;
BOOL fRemember = FALSE;

/* Check for UI */

if (pspb->ulSEFlags & SERVICE_UI_ALWAYS)
fAllowUI = fNeedUI = TRUE;
else
if (pspb->ulSEFlags & SERVICE_UI_ALLOWED)
fAllowUI = TRUE;

if (FFindPropInPSPB(pspb, PR_SMS_PASSWORD, &pval))
{
ULONG cbPass;

/* Passwords must fit in ACCTSIZE bytes. Truncate the password if */
/* it's too long. */

cbPass = lstrlenA(pval->Value.lpszA) + 1;
if (cbPass > ACCTSIZE)
cbPass = ACCTSIZE;

memcpy(szPass, pval->Value.lpszA, (UINT) cbPass);
szPass[cbPass - 1] = '\0';

/* We found the password. Since we found it, assume that the */
/* user wants us to remember it. Only if PR_SMS_REMEMBER_PW is */
/* present and FALSE will we remove the password. */

fRemember = TRUE;
}
else
szPass[0] = '\0';

if (FFindPropInPSPB(pspb, PR_SMS_CREATE, &pval))
fCreate = pval->Value.b;

if (FFindPropInPSPB(pspb, PR_SMS_REMEMBER_PW, &pval))
fRemember = pval->Value.b;

/* try to find the path in either the properties passed in, or */
/* the properties in the profile */

if ( FFindPropInPSPB(pspb, PR_SMS_PATH, &pval)
&& GetFullPathName(pval->Value.lpszA, MAX_PATH, szPath,
&pszFileTitle))
{
DWORD dwFA;

Assert (*(pszFileTitle - 1) == '\\');

dwFA = GetFileAttributes(szPath);

/* If the file isn't there, and we aren't creating, then error. */
/* If the file is there, and we are creating, then error. */

if (dwFA == 0xFFFFFFFF && fCreate == FALSE)
{
sc = MAPI_E_NOT_FOUND;
fNeedUI = TRUE;
}
else if (dwFA != 0xFFFFFFFF && fCreate == TRUE)
{
sc = MAPI_E_NO_ACCESS;
fNeedUI = TRUE;
}
}
else
{
szPath[0] = '\0';
sc = MAPI_E_NOT_FOUND;
fNeedUI = TRUE;
}

while (TRUE)
{
if (sc == S_OK)
{
PMSL pmsl;
PIMS pims;

/* Verify that everything works before we exit. */

if (fCreate)
sc = ScCreateStore(pspb, szPath, szPass, &pmsl, &pims);
else
sc = GetScode(HrCheckAndOpenStore(pspb->pmsp, pspb->psup,
TRUE, pspb->pps, szPath, szPass, FALSE, &pmsl, &pims));
if (sc)
fNeedUI = TRUE;
else
{
HRESULT hrT;

/* delete the unnecessary properties from the profile */
pps->lpVtbl->DeleteProps(pps, (LPSPropTagArray)&ptaDel,
NULL);

/* Update saved credentials if they have changed. */

hrT = HrSetProfileCred(pps, szPass, szPath, fRemember,
pims->eidStore.cb, pims->eidStore.lpb,
&pims->uidResource);

if (hrT == hrSuccess)
hrT = pps->lpVtbl->SaveChanges(pps, 0);

/* Can't make the profile section permanent because */
/* the support object we are given for config doesn't */
/* support ModifyProfile. Make it permanent during Logon. */

/* Addref the support object because we kept a reference */
/* in pims, and releasing pims will release psup. We want */
/* the refcount on psup to be the same at config exit as */
/* it was when we were called. */

UlAddRef(pspb->psup);


UlRelease(pims);
UlRelease(pmsl);

if (!fNeedUI)
goto ret; /* SUCCESS! */
}
}

if (fNeedUI && !fAllowUI)
{
sc = MAPI_E_UNCONFIGURED;
goto ret;
}

/* Do the UI */

sc = GetScode(HrGetLogonDlgCred(pspb->pmsp->hInst, pspb->ulUIParam,
szPath, szPass, &fCreate, &fRemember));

if (sc != S_OK)
goto ret;

fNeedUI = FALSE;
}

ret:
DebugTraceSc(ScCreateOrConfigure, sc);
return sc;
}

/*
- ScCreateStore
-
* Create a Sample Store out on disk.
*
* Returns the pims which points to the store, and the
* pmsl which points to the MSLogon object.
*/
static SCODE ScCreateStore(PSERVICEPBLK pspb, LPTSTR szPath,
LPTSTR szPass, PMSL * ppmsl, PIMS * ppims)
{
HRESULT hr = hrSuccess;
SCODE sc = S_OK;
PRFS prfs = NULL; /* Struct for receive folder settings */
BOOL fCreateDir = FALSE; /* Was new directory created? */
BOOL fCreateProp = FALSE; /* Was new MsgStore props file created? */
BOOL fCreateFld = FALSE; /* Was new root folder created? */
BOOL fCreateRFS = FALSE; /* Was receive folder file created? */
PIMS pims = NULL;
PMSL pmsl = NULL;

/* Create the directory */

if (!CreateDirectory(szPath, NULL))
{
sc = MAPI_E_CALL_FAILED;
goto exit;
}

fCreateDir = TRUE;

/* Create file to hold receive folder settings */

hr = OpenRFS(szPath, szFolderFileName, RFS_CREATE, &prfs);
if (hr != hrSuccess)
{
sc = GetScode(hr);
goto exit;
}

fCreateRFS = TRUE;

/* Create the MSLogon object. */

sc = ScCreateMSL(pspb->pmsp, &pmsl);
if (sc != S_OK)
goto exit;

/* Create instance, creates Message Store props file as well */

hr = HrNewIMS(szPath, szPropFileName, pspb->pmsp, pmsl, prfs,
pspb->pps, pspb->psup, TRUE, &pims);
if (hr != hrSuccess)
{
sc = GetScode(hr);
goto exit;
}

OBJ_SetFlag(pims, OBJF_MODIFY);
pmsl->pims = pims;

fCreateProp = TRUE;

hr = HrInitIMSProps(pims, szPass);
if (hr != hrSuccess)
{
sc = GetScode(hr);
goto exit;
}

/* Create file to hold root folder properties */

{
PEID peidTmp = NULL;

hr = HrCreateFolderStorage(NULL, FOLDER_ROOT, "", "",
FALSE, pims, &peidTmp);

if (hr != hrSuccess)
{
sc = GetScode(hr);
goto exit;
}

/* We can throw away the returned EntryID because we know */
/* how to construct the EntryID for the root folder when */
/* we need to open it (see pims->OpenEntry()). */

LMFree(&pspb->pmsp->lmr, peidTmp);
}

fCreateFld = TRUE;

*ppmsl = pmsl;
*ppims = pims;

exit:
if (sc != S_OK)
{
TCHAR rgch[MAX_PATH];

if (pims)
{
FreeNull(pims->szStorePath);
LMFree(&pspb->pmsp->lmr, pims->eidStore.lpb);
LMFree(&pspb->pmsp->lmr, pims);
}
else if (prfs)
{
/* If we have an RFS but NO IMS, close RFS manually. */
NFSideAssertSz(CloseRFS(prfs) == hrSuccess, "RFS not closed");
}

if ( pmsl )
{
pmsl->pims = NULL;

/* Releasing the pmsl must come after releasing the IMS. */
UlRelease(pmsl);
}

/* We need to use state flags to know how much work to undo */
/* on a create because otherwise we may accidentally erase */
/* files that we did not create (because they were created */
/* by another Sample Store creation in a different session). */

if (fCreateProp == TRUE)
{
if (FAppendPathNoMem(szPath, szPropFileName, MAX_PATH, rgch))
(void)DeleteFile(rgch);
else
TrapSz("Can't clean up create #1!");
}

if (fCreateRFS == TRUE)
{
if (FAppendPathNoMem(szPath, szFolderFileName, MAX_PATH, rgch))
(void)DeleteFile(rgch);
else
TrapSz("Can't clean up create #2!");
}

if (fCreateFld == TRUE)
{
if (FAppendPathNoMem(szPath, szPropertyFileName, MAX_PATH, rgch))
(void)DeleteFile(rgch);
else
TrapSz("Can't clean up create #3!");
}

if (fCreateDir == TRUE)
(void)RemoveDirectory(szPath);
}

DebugTraceSc(ScCreateStore, sc);
return sc;
}

/*
* FAlreadyOpenStore
*
* Purpose:
* Searches the list of stores open in this MAPI session and
* returns the MS object of a store that matches the path
* that's passed in.
*
* Arguments:
* pmsp Pointer to the Message Store Provider (MAPI Session
* context). This is where the list of open stores
* lives.
* szPath String containing full path to the store to open.
* ppims Location in which to return the address of a
* message store object already open (only valid if
* the function returns TRUE).
*
* Returns:
* BOOL. Will be TRUE if the message store is already open,
* FALSE if not.
*
* Side effects:
* None.
*
* Errors:
* None.
*/
static BOOL FAlreadyOpenStore(PMSP pmsp, LPTSTR szPath, PIMS * ppims)
{
PIMS pimsPrev = NULL;

for (pimsPrev = (PIMS) pmsp->pobjHead; pimsPrev;
pimsPrev = (PIMS) pimsPrev->pobjNext)
{
if (!lstrcmpi(pimsPrev->szStorePath, szPath))
{
OBJ_EnterCriticalSection(pimsPrev);

if(!OBJ_TestFlag(pimsPrev, MSF_BEINGDESTROYED))
{
*ppims = pimsPrev;
UlAddRef(pimsPrev);

OBJ_LeaveCriticalSection(pimsPrev);
return TRUE;
}

DebugTrace("SMS: Attempt to OpenMsgStore at %s while a another IMsgStore\n"
"object opened on this directory is being released\n", szPath);

OBJ_LeaveCriticalSection(pimsPrev);
}
}

return FALSE;
}

/*
* HrGetPassword
*
* Purpose:
* Get the password for a store interactively by putting up a dialog.
*
* Parameters:
* hInst [in] Instance handle of my DLL
* psup [in] Pointer to the mapi support object.
* plmr [in] Pointer to the memory allocation routines.
* ulUIParam [in] Window handle cast to ULONG.
* szPath [in] The pathname to the top of the message store.
* szPass [out] Pointer to a buffer in which to place the password.
* pfRemember [out] Address of boolean: should the password be saved
* in profile?
*
* Returns:
* HRESULT
*
* Side effects:
* Puts up password dialog.
*
* Errors:
* None.
*/
static HRESULT HrGetPassword(HINSTANCE hInst, LPMAPISUP psup, PLMR plmr,
ULONG ulUIParam, LPTSTR szPath, LPTSTR szPass, BOOL *pfRemember)
{
HRESULT hr = hrSuccess;
DLGPSWD dlgpswd;

NFAssertSz(szPass, "Bad szPass");

lstrcpy(dlgpswd.szPath, szPath);
dlgpswd.psup = psup;
dlgpswd.plmr = plmr;
dlgpswd.fRemember = FALSE;
dlgpswd.fCancel = FALSE;
dlgpswd.lpCtl3d = CTL3D_Initialize(hInst);

if (DialogBoxParam(hInst, TEXT("PSWDDIALOGBOX"), (HWND) ulUIParam,
(DLGPROC) PasswordDlgProc, (LPARAM) &dlgpswd) == -1)
{
hr = ResultFromScode(MAPI_E_CALL_FAILED);
goto exit;
}

if (dlgpswd.fCancel)
{
hr = ResultFromScode(MAPI_E_USER_CANCEL);
goto exit;
}

if (szPass)
lstrcpy(szPass, dlgpswd.szPass);

if (pfRemember)
*pfRemember = dlgpswd.fRemember;

Assert(hr == hrSuccess);

exit:
CTL3D_Uninitialize(dlgpswd.lpCtl3d);
DebugTraceResult(HrGetPassword, hr);
return hr;
}

/*
* PasswordDlgProc
*
* Purpose:
* Dialog procedure to handle password dialog events.
*
* Parameters:
* hDlg Handle to the dialog.
* message Message to be handled.
* wParam Button ID if button is pressed.
* lParam Context parameter from dialog creation (see
* HrGetLogonDlgCred).
*
* Returns:
* BOOL. TRUE if handled, FALSE if not
*
* Side effects:
* None.
*
* Errors:
* None.
*/
STDAPI_(BOOL) PasswordDlgProc(HWND hDlg, UINT message, WPARAM wParam,
LPARAM lParam)
{
PDLGPSWD pdlgpswd;
BOOL fHandled = FALSE;

AssertSz(hDlg, "Bad hDlg");

switch (message)
{
case WM_INITDIALOG:
{
SetWindowLong(hDlg, DWL_USER, lParam);
pdlgpswd = (PDLGPSWD) lParam;

CTL3D_Subclass(pdlgpswd->lpCtl3d, hDlg, CTL3D_ALL);

#ifdef _WIN32
SetForegroundWindow(hDlg);
#endif
BringWindowToTop(hDlg);

SetDlgItemText(hDlg, TEXT_PATH, pdlgpswd->szPath);

pdlgpswd->fRemember = FALSE;

fHandled = TRUE;
break;
}

case WM_COMMAND:
{
pdlgpswd = (PDLGPSWD) GetWindowLong(hDlg, DWL_USER);

switch (LOWORD(wParam))
{
case IDOK:
{
GetDlgItemText(hDlg, IDE_ACCOUNT_PASSWORD,
pdlgpswd->szPass, ACCTSIZE * sizeof(TCHAR));

if (HrConfirmCred(pdlgpswd->szPath, pdlgpswd->szPass,
pdlgpswd->psup, pdlgpswd->plmr) != hrSuccess)
{
/* put up a message box */
MessageBox(hDlg, "Password is incorrect.",
"Sample Store Password",
MB_OK | MB_APPLMODAL | MB_SETFOREGROUND);

/* highlight the password field */
SetFocus(hDlg);
break;
}

pdlgpswd->fRemember =
IsDlgButtonChecked(hDlg, IDC_REMEMBER);

EndDialog(hDlg, 0);
fHandled = TRUE;
break;
}
case IDCANCEL:
{
pdlgpswd->fCancel = TRUE;
EndDialog(hDlg, 0);
fHandled = TRUE;
break;
}
default:
{
fHandled = FALSE;
break;
}
}
}

default:
{
fHandled = FALSE;
break;
}
}

return fHandled;
}

/*
* HrGetLogonDlgCred
*
* Purpose:
* Obtain credentials interactively for opening a store by
* putting up a dialog.
*
* Parameters:
* hInst Instance of my DLL
* ulUIParam Window handle cast to ULONG.
* szPath Address of buffer in which to place the path to
* the root directory.
* szPass Address of buffer in which to place the logon
* Account Password.
* pfCreate Address of boolean: should store be created?
* pfRemember Address of boolean: should password be saved in profile?
*
* Returns:
* HRESULT
*
* Side effects:
* Puts up dialog.
*
* Errors:
* None.
*/
static HRESULT HrGetLogonDlgCred(HINSTANCE hInst, ULONG ulUIParam,
LPTSTR szPath, LPTSTR szPass, BOOL *pfCreate, BOOL *pfRemember)
{
HRESULT hr = hrSuccess;
LGNCRED lgncred;

NFAssertSz(szPass, "Bad szPass");

lstrcpy(lgncred.szPath, szPath);
lstrcpy(lgncred.szPass, szPass);
lgncred.fCreate = FALSE;
lgncred.fRemember = *pfRemember;
lgncred.fCancel = FALSE;
lgncred.ulFieldMask = (szPath ? LGNCRED_PATH : 0L)
| (szPass ? LGNCRED_PASS : 0L)
| (pfCreate ? LGNCRED_CREATE : 0L)
| LGNCRED_REMEMBER;

lgncred.lpCtl3d = CTL3D_Initialize(hInst);

if (DialogBoxParam(hInst, TEXT("LOGONDIALOGBOX"), (HWND) ulUIParam,
(DLGPROC) LogonDlgProc, (LPARAM) &lgncred) == -1)
{
hr = ResultFromScode(MAPI_E_CALL_FAILED);
goto exit;
}

if (lgncred.fCancel)
{
hr = ResultFromScode(MAPI_E_USER_CANCEL);
goto exit;
}

if (szPath)
{
DWORD dwPathLength = 0L;
LPTSTR szPathName = NULL;

dwPathLength = GetFullPathName(lgncred.szPath, MAX_PATH,
szPath, &szPathName);
if (!dwPathLength || dwPathLength > MAX_PATH)
{
hr = ResultFromScode(MAPI_E_UNCONFIGURED);
goto exit;
}
}

if (szPass)
lstrcpy(szPass, lgncred.szPass);

if (pfCreate)
*pfCreate = lgncred.fCreate;

*pfRemember = lgncred.fRemember;

Assert(hr == hrSuccess);

exit:
CTL3D_Uninitialize(lgncred.lpCtl3d);

DebugTraceResult(HrGetLogonDlgCred, hr);
return hr;
}

/*
* LogonDlgProc
*
* Purpose:
* Dialog procedure to handle logon events.
*
* Parameters:
* hDlg Handle to the dialog.
* message Message to be handled.
* wParam Button ID if button is pressed.
* lParam Context parameter from dialog creation (see
* HrGetLogonDlgCred).
*
* Returns:
* BOOL. TRUE if handled, FALSE if not
*
* Side effects:
* None.
*
* Errors:
* None.
*/
STDAPI_(BOOL) LogonDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
PLGNCRED plgncred;
BOOL fHandled = FALSE;

AssertSz(hDlg, "Bad hDlg");

switch (message)
{
case WM_INITDIALOG:
{
SetWindowLong(hDlg, DWL_USER, lParam);
plgncred = (PLGNCRED) lParam;

CTL3D_Subclass(plgncred->lpCtl3d, hDlg, CTL3D_ALL);

#ifdef _WIN32
SetForegroundWindow(hDlg);
#endif
BringWindowToTop(hDlg);

if (plgncred->ulFieldMask & LGNCRED_PATH)
SetDlgItemText(hDlg, IDE_ROOT_PATH, plgncred->szPath);
else
EnableWindow(GetDlgItem(hDlg, IDE_ROOT_PATH), FALSE);

if (plgncred->ulFieldMask & LGNCRED_PASS)
SetDlgItemText(hDlg, IDE_ACCOUNT_PASSWORD, plgncred->szPass);
else
EnableWindow(GetDlgItem(hDlg, IDE_ACCOUNT_PASSWORD), FALSE);

if (!(plgncred->ulFieldMask & LGNCRED_CREATE))
EnableWindow(GetDlgItem(hDlg, IDC_CREATE), FALSE);

if (plgncred->ulFieldMask & LGNCRED_REMEMBER)
CheckDlgButton(hDlg, IDC_REMEMBER, plgncred->fRemember);
else
EnableWindow(GetDlgItem(hDlg, IDC_REMEMBER), FALSE);

fHandled = TRUE;
break;
}

case WM_COMMAND:
{
plgncred = (PLGNCRED) GetWindowLong(hDlg, DWL_USER);

switch (LOWORD(wParam))
{
case IDOK:
{
if (plgncred->ulFieldMask & LGNCRED_CREATE)
{
plgncred->fCreate =
IsDlgButtonChecked(hDlg, IDC_CREATE);
}

if (plgncred->ulFieldMask & LGNCRED_PATH)
{
GetDlgItemText(hDlg, IDE_ROOT_PATH,
plgncred->szPath, MAX_PATH * sizeof(TCHAR));
}

/* check if this is a valid path */
if (!FIsValidPath(plgncred->szPath, plgncred->fCreate))
{
/* put up a message box */
MessageBox(hDlg, (plgncred->fCreate) ?
"Root directory can not be created." :
"Path to root directory not found.",
"Message Store Logon", MB_OK | MB_APPLMODAL | MB_SETFOREGROUND);

/* highlight the path field */
SetFocus(hDlg);
break;
}

if (plgncred->ulFieldMask & LGNCRED_PASS)
{
GetDlgItemText(hDlg, IDE_ACCOUNT_PASSWORD,
plgncred->szPass, ACCTSIZE * sizeof(TCHAR));
}

if (plgncred->ulFieldMask & LGNCRED_REMEMBER)
{
plgncred->fRemember =
IsDlgButtonChecked(hDlg, IDC_REMEMBER);
}

EndDialog(hDlg, 0);
fHandled = TRUE;
break;
}
case IDCANCEL:
{
plgncred->fCancel = TRUE;
EndDialog(hDlg, 0);
fHandled = TRUE;
break;
}
default:
{
fHandled = FALSE;
break;
}
}
}

default:
{
fHandled = FALSE;
break;
}
}

return fHandled;
}

/*
* HrGetProfileCred
*
* Purpose:
* Obtain password programmatically by reading the
* appropriate property out of the profile. If the
* property isn't there, don't fail the call. Return
* a NULL for the password, and clear the *pfFound flag.
*
* Parameters:
* plmr Pointer to the MAPI linked memory routines
* psup Pointer to the MAPI support object (from which
* we can get the current profile section).
* szPass Address of buffer in which to place the Logon
* Account Password.
* pfFound TRUE if the password was found in the profile.
* FALSE if the password wasn't in the profile.
*
* Returns:
* HRESULT
*
* Errors:
* None.
*/
static HRESULT
HrGetProfileCred(PLMR plmr, LPPROFSECT pps, LPTSTR szPass, BOOL *pfFound)
{
HRESULT hr;
LPSPropValue pval = NULL;

AssertSz(szPass, "Bad szPass");
AssertSz(pps, "No profile section");

hr = HrGetOneProp((LPMAPIPROP) pps, PR_SMS_PASSWORD, &pval);

if (hr != hrSuccess)
{
*pfFound = FALSE;
szPass[0] = (TCHAR) 0;
hr = hrSuccess;
}
else
{
lstrcpy(szPass, pval[0].Value.LPSZ);
*pfFound = TRUE;
}

LMFree(plmr, pval);

DebugTraceResult(HrGetProfileCred, hr);
return hr;
}

/*
* HrSetProfileCred
*
* Purpose:
* Save credential information in the profile for future
* programmatic opening.
*
* Parameters:
* pps Pointer to the profile section.
* szPass Logon Account Password.
* szPath Path name of root of the store
* fRemember TRUE means save the password in the store.
* cbEntryID Number of bytes in lpEntryID
* lpEntryID Pointer to Wrapped Store EntryID
* puidRK pointer to PR_RECORD_KEY data (always &pims->uidResource)
*
* Returns:
* HRESULT
*
* Side effects:
* None.
*
* Errors:
* None.
*/
static HRESULT HrSetProfileCred(LPPROFSECT pps, LPTSTR szPass, LPTSTR szPath,
BOOL fRemember, ULONG cbEntryID, LPBYTE lpEntryID, LPMAPIUID puidRK)
{
HRESULT hr = hrSuccess;
LPTSTR szDisplayName = NULL;
SPropValue rgProps[7];
ULONG cValues;

AssertSz(pps, "Bad profile section");
AssertSz(szPass, "Bad szPass");
AssertSz(lpEntryID, "Bad lpEntryID");

/* fill in the Display Name */
hr = HrAlloc((lstrlen(szDisplayPrefix) + lstrlen(szPath) + 1)
* sizeof(TCHAR), &szDisplayName);
if (hr != hrSuccess)
goto hr_err;

lstrcpy(szDisplayName, szDisplayPrefix);
lstrcat(szDisplayName, szPath);

rgProps[0].ulPropTag = PR_PROVIDER_DISPLAY;
rgProps[0].Value.LPSZ = szProviderDisplayName;

rgProps[1].ulPropTag = PR_DISPLAY_NAME;
rgProps[1].Value.LPSZ = szDisplayName;

rgProps[2].ulPropTag = PR_ENTRYID;
rgProps[2].Value.bin.cb = cbEntryID;
rgProps[2].Value.bin.lpb = lpEntryID;

rgProps[3].ulPropTag = PR_SMS_PATH;
rgProps[3].Value.LPSZ = szPath;

rgProps[4].ulPropTag = PR_MDB_PROVIDER;
rgProps[4].Value.bin.cb = sizeof(MAPIUID);
rgProps[4].Value.bin.lpb = (LPBYTE) &uidProvider;

rgProps[5].ulPropTag = PR_RECORD_KEY;
rgProps[5].Value.bin.cb = sizeof(MAPIUID);
rgProps[5].Value.bin.lpb = (LPBYTE) puidRK;

cValues = 6;

if (fRemember)
{
rgProps[6].ulPropTag = PR_SMS_PASSWORD;
rgProps[6].Value.LPSZ = szPass;
++cValues;
}
else
{
/* If the user doesn't want to remember the password, then we */
/* should try to remove it from the profile section. */

(void) pps->lpVtbl->DeleteProps(pps, &sptaPass, NULL);
}

hr = pps->lpVtbl->SetProps(pps, cValues, rgProps, NULL);
if (hr != hrSuccess)
goto hr_err;

hr_err:
FreeNull(szDisplayName);

DebugTraceResult(HrSetProfileCred, hr);
return hr;
}

/*
* HrConfirmCred
*
* Purpose:
* Check user's store opening credentials against the account
* information in the store.
*
* Arguments:
* szPath Path to "root" directory of store.
* szPass Logon Account Password.
* psup Pointer to a MAPI Support object (passed to IMsgOnIStg).
* plmr Pointer to the linked mem alloc routines.
*
* Returns:
* HRESULT
*
* Side effects:
* None.
*/
static HRESULT HrConfirmCred(LPTSTR szPath, LPTSTR szPass, LPMAPISUP psup,
PLMR plmr)
{
HRESULT hr;
LPMESSAGE lpmsg = NULL;
LPSPropValue pval = NULL;
LPTSTR szT = NULL;

AssertSz(szPath, "Bad szPath");
AssertSz(szPass, "Bad szPass");

/* Open the message store props file as an IMessage instance. */

hr = HrAppendPath(szPath, szPropFileName, &szT);
if (hr != hrSuccess)
goto hr_err;

hr = HrOpenIMSPropsFileRetry(NULL, szT, plmr, psup, FALSE, &lpmsg);
if (hr != hrSuccess)
goto hr_err;

/* Get password from the message store props file */
/* and compare against the function parameter. */

hr = HrGetOneProp((LPMAPIPROP) lpmsg, PR_SMS_PASSWORD, &pval);

if (hr != hrSuccess)
goto hr_err;

if (lstrcmp(szPass, pval[0].Value.LPSZ))
hr = ResultFromScode(MAPI_E_LOGON_FAILED);

hr_err:
FreeNull(szT);
UlRelease(lpmsg);
LMFree(plmr, pval);

DebugTraceResult(HrConfirmCred, hr);
return hr;
}

/*
* HrFilePathExists
*
* Purpose:
* Helper function for HrCheckStoreIntegrity. This function creates a
* full pathname to the file given, and attempts to find it on disk.
*
* Returns:
* HRESULT
*
* If the file exists, and no other errors occur, the function returns
* success. If the file doesn't exist, the function returns
* MAPI_E_CORRUPT_STORE. If the file exists, but the find can't be closed,
* the function returns MAPI_E_LOGON_FAILED.
*/
static HRESULT HrFilePathExists(LPTSTR szStorePath, LPTSTR szFileName)
{
LPTSTR szT = NULL;
WIN32_FIND_DATA filedata;
HANDLE hFindFile;
HRESULT hr;

hr = HrAppendPath(szStorePath, szFileName, &szT);
if (hr != hrSuccess)
goto exit;

hFindFile = FindFirstFile(szT, &filedata);
if (hFindFile == INVALID_HANDLE_VALUE)
{
hr = ResultFromScode(MAPI_E_CORRUPT_STORE);
goto exit;
}

if (!FindClose(hFindFile))
hr = ResultFromScode(MAPI_E_LOGON_FAILED);

exit:
FreeNull(szT);
return hr;
}

/*
* HrCheckStoreIntegrity
*
* Purpose:
* Quick check to see that all the necessary store components
* exist in the proper place (store root). The check of file
* attributes assumes that OLE created its docfiles with
* normal file attributes.
*
* Arguments:
* szPath Path to "root" directory of store.
*
* Returns:
* HRESULT
*
* Side effects:
* None.
*
* Errors:
* MAPI_E_CORRUPT_STORE A necessary store component was
* missing.
*/
static HRESULT HrCheckStoreIntegrity(LPTSTR szStorePath)
{
HRESULT hr;

AssertSz(!IsBadStringPtr(szStorePath, (UINT) -1), "Bad szStorePath");

/* Check for the message store properties file */

hr = HrFilePathExists(szStorePath, szPropFileName);
if (hr != hrSuccess)
goto hr_err;

/* Check for the root folder properties file */

hr = HrFilePathExists(szStorePath, szPropertyFileName);
if (hr != hrSuccess)
goto hr_err;

/* Check for the receive folder settings storage file */

hr = HrFilePathExists(szStorePath, szFolderFileName);

hr_err:
DebugTraceResult(HrCheckStoreIntegrity, hr);
return hr;
}

/*
* FIsValidPath
*
* Purpose:
* Returns TRUE if the path in szPath is a valid path to
* to an existing directory if fCreate is FALSE. Or the
* specified directory can be created if fCreate is TRUE.
*
* Arguments:
* szPath Path to "root" directory of store.
* fCreate TRUE if directory is to be created.
*
* Returns:
* BOOL
*
* Side effects:
* None.
*
*/
static BOOL FIsValidPath(LPTSTR szPath, BOOL fCreate)
{
LPTSTR szLastSlash = NULL; /* pointer to last slash in szPath */
DWORD dwAttributes;

if (*szPath == '\0')
return FALSE;

/* if creating a root folder, the folder to check is the parent */
/* of the one in szPath */
if (fCreate)
{
/* must not already exist */
if (GetFileAttributes(szPath) != -1)
return FALSE;

/* parent directory must exist */
szLastSlash = SzFindLastCh(szPath, '\\');
if (szLastSlash && szLastSlash != szPath &&
*(szLastSlash - 1) != ':')
{
*szLastSlash = '\0';
}
else
{
/* creating a directory at the root level */
return TRUE;
}
}

/* get the attributes of the directory that must exist */
dwAttributes = GetFileAttributes(szPath);

/* restore szPath */
if (szLastSlash)
*szLastSlash = '\\';

return ((dwAttributes != -1) && (dwAttributes & FILE_ATTRIBUTE_DIRECTORY)) ?
TRUE : FALSE;
}

/*
* HrGetSpoolSecurity
*
* Purpose
* return the spooler security information for use in SpoolerLogon
*
* Parameters
* szPath path to root of store
* szPass store account password
* plmr pointer to the MAPI linked allocation routines
* lpcbSpoolSecurity byte count of the security information
* lppbSpoolSecurity security information
*/
static HRESULT HrGetSpoolSecurity(LPTSTR szPath, LPTSTR szPass,
PLMR plmr, ULONG * lpcbSpoolSecurity, LPBYTE * lppbSpoolSecurity)
{
ULONG cbPath = Cbtszsize(szPath);
ULONG cbPass = Cbtszsize(szPass);
SCODE sc = S_OK;

/* allocate memory for the security information */
*lpcbSpoolSecurity = cbPass + cbPath;
sc = LMAlloc(plmr, *lpcbSpoolSecurity, lppbSpoolSecurity);
if (sc)
{
*lpcbSpoolSecurity = 0;
DebugTraceSc(HrGetSpoolSecurity, sc);
return ResultFromScode(sc);
}

/* fill in the spooler security information */
if (cbPath)
memcpy(*lppbSpoolSecurity, szPath, (UINT) cbPath);
if (cbPass)
memcpy(*lppbSpoolSecurity + (UINT) cbPath, szPass, (UINT) cbPass);

return hrSuccess;
}

/*
* DecodeSpoolSecurity
*
* Purpose
* return the spooler security information for use in SpoolerLogon

* 
* Parameters
* cbSpoolSecurity byte count of the security information
* pbSpoolSecurity security information
* szPath pointer to variable for path to root of store
* szPass poitner to varaible for store account password
*/
static void DecodeSpoolSecurity(ULONG cbSpoolSecurity, LPBYTE pbSpoolSecurity,
LPTSTR szPath, LPTSTR szPass)
{
ULONG cbPath = 0;

lstrcpy(szPath, pbSpoolSecurity);
cbPath = Cbtszsize(szPath);

lstrcpy(szPass, pbSpoolSecurity + cbPath);

return;
}

/*
* HrCheckAndOpenStore
*
* Purpose
* check the integrity of store specified in szPath and return
* an open store if it's OK
*
* Parameters
* pmsp pointer to our MSProvider object
* psup mapi support object
* fModify TRUE if write access desired
* pps pointer to our profile section
* szPath path name to root of the store
* szPass password to store
* fIsSpooler TRUE if this is a spooler logon
* ppmsl pointer to variable to hold MSLogon object
* ppims pointer to variable to hold the opened store object
*/
static HRESULT HrCheckAndOpenStore(PMSP pmsp, LPMAPISUP psup, BOOL fModify,
LPPROFSECT pps, LPTSTR szPath, LPTSTR szPass, BOOL fIsSpooler,
PMSL *ppmsl, PIMS *ppims)
{
HRESULT hr;
PRFS prfs = NULL;
PMSL pmsl = NULL;
PIMS pims = NULL;
SCODE sc;

*ppmsl = NULL;

hr = HrConfirmCred(szPath, szPass, psup, &pmsp->lmr);
if (hr != hrSuccess)
goto exit;

hr = HrCheckStoreIntegrity(szPath);
if (hr != hrSuccess)
goto exit;

hr = OpenRFS(szPath, szFolderFileName, 0, &prfs);
if (hr != hrSuccess)
goto exit;

/* Create MSLogon object */

sc = ScCreateMSL(pmsp, &pmsl);
if (sc != S_OK)
{
hr = ResultFromScode(sc);
goto exit;
}

/* Create Message Store object */

hr = HrNewIMS(szPath, szPropFileName, pmsp, pmsl, prfs, pps, psup,
FALSE, &pims);
if (hr != hrSuccess)
goto exit;

if (fIsSpooler)
OBJ_SetFlag(pims, MSF_SPOOLER);

if (fModify)
OBJ_SetFlag(pims, OBJF_MODIFY);

pmsl->pims = pims;

*ppims = pims;
*ppmsl = pmsl;

exit:
if (hr != hrSuccess)
{
/* If we have an RFS but failed to create the MS, close RFS manually. */
if (prfs)
NFSideAssertSz(CloseRFS(prfs) == hrSuccess, "RFS not closed");

UlRelease(pmsl);
pmsl = NULL;
}

DebugTraceResult(HrCheckAndOpenStore, hr);
return hr;
}

/*
* ScCreateMSL
*
* Purpose
* returns a new logon object
*
* Parameters
* pmsp A pointer to the MS Provider object.
* ppmsl Location in which to return the address of the
* newly allocated MSL object.
*
* Returns:
* SCODE
*
* Side effects:
* None.
*
* Errors:
* MAPI_E_NOT_ENOUGH_MEMORY Could not allocate space for
* the MSL object.
*/
static SCODE ScCreateMSL(PMSP pmsp, PMSL *ppmsl)
{
PMSL pmsl;
SCODE sc;

sc = LMAllocZ(&pmsp->lmr, sizeof(MSL), &pmsl);

if (sc == S_OK)
{
OBJ_Initialize(pmsl, &vtblMSL, OT_MSLOGON, NULL, &pmsl->cs);

InitializeCriticalSection(&pmsl->cs);

pmsl->lmr = pmsp->lmr;

*ppmsl = pmsl;
}

DebugTraceSc(ScCreateMSL, sc);
return sc;
}

/*
* FFindPropInPSPB
*
* Purpose:
* Look for the given property in the PSPB structure. Search the
* client prop array before the profile prop array.
*
* Arguments:
* pspb pointer to the service parameter block
* ulPT the property tag to look for
* ppval location to place the pointer to the pval
*
* Returns:
*/
static BOOL FFindPropInPSPB(PSERVICEPBLK pspb, ULONG ulPT, LPSPropValue *ppval)
{
LPSPropValue pval;

/* First, look in the client's array. If it's not there, look for */
/* it in the profile array. */

pval = PpropFindProp(pspb->pvalsClient, pspb->cvalsClient, ulPT);

if (!pval)
pval = PpropFindProp(pspb->pvalsProf, pspb->cvalsProf, ulPT);

if (pval)
{
*ppval = pval;
return TRUE;
}

return FALSE;
}

/*
* ScGetFullFileName
*
* Purpose:
* Look for the file path and name in the property list given. If
* found, convert it to a full (non-relative) path name, and check for
* the existence of the directory. Note that we return values even
* when an error occurs.
*
* Arguments:
*
* Returns:
*/
static SCODE ScGetFullFileName(PSERVICEPBLK pspb, BOOL *pfCreate,
LPSTR pszPath)
{
SCODE sc = S_OK;
LPSTR pszFileTitle;
LPSPropValue pval;
BOOL fCreate = FALSE;

*pszPath = '\0';
*pfCreate = FALSE;

/* get the path to the file */
if ( FFindPropInPSPB(pspb, PR_SMS_PATH, &pval)
&& GetFullPathName(pval->Value.lpszA, MAX_PATH, pszPath,
&pszFileTitle))
{
DWORD dwFA;
LPSPropValue pvalT;

if (FFindPropInPSPB(pspb, PR_SMS_CREATE, &pvalT))
*pfCreate = pvalT->Value.b;

Assert (*(pszFileTitle - 1) == '\\');

dwFA = GetFileAttributes(pszPath);

/* If the file isn't there, and we aren't creating, then error. */
/* If the file is there, and we are creating, then error. */

if (dwFA == 0xFFFFFFFF && *pfCreate == FALSE)
sc = MAPI_E_NOT_FOUND;
else if (dwFA != 0xFFFFFFFF && *pfCreate == TRUE)
sc = MAPI_E_NO_ACCESS;
}
else
{
*pszPath = '\0';
sc = MAPI_E_NOT_FOUND;
}

DebugTraceSc(ScGetFullFileName, sc);
return sc;
}