/*
 -  X P S T A T U S . C
 -
 *  Purpose:
 *      Sample transport provider status interface code.  This module
 *      contains the following Transport SPI entry points:
 *
 *          OpenStatusEntry()
 *
 *      The Status Object methods implemented in this module are:
 *
 *          QueryInterface,
 *          AddRef,
 *          Release,
 *          GetLastError,
 *          GetProps,
 *          GetPropList,
 *          SettingsDialog,
 *          FlushQueues,
 *
 *      Additional support functions found here:
 *
 *          HrBuildTransportStatus
 *          HrUpdateTransportStatus
 *          NewSOB
 *          HrLoadStatusString
 *          MapScodeSz
 *
 *  Copyright 1992-1995 Microsoft Corporation.  All Rights Reserved.
 */

//$BUG  Invalid flags should return MAPI_E_UNKNOWN_FLAGS!

#include "xppch.h"
#include "xpresrc.h"


#define MAX_STRING          8192
#define MAX_RESRC_STRING    256

/* Declared in xpbase.c */

extern sptLogonArray;

/*  SOB IMAPIProp jump table */

SOB_Vtbl vtblSOB =
{
    SOB_QueryInterface,
    SOB_AddRef,
    SOB_Release,
    SOB_GetLastError,
    SOB_SaveChanges,
    SOB_GetProps,
    SOB_GetPropList,
    SOB_OpenProperty,
    SOB_SetProps,
    SOB_DeleteProps,
    SOB_CopyTo,
    SOB_CopyProps,
    SOB_GetNamesFromIDs,
    SOB_GetIDsFromNames,
    SOB_ValidateState,
    SOB_SettingsDialog,
    SOB_ChangePassword,
    SOB_FlushQueues,
};

/*  Static properties. In this case the array of Property Tags available
    from the Status object if opened.  The PR_RESOURCE_PATH must be last
    because we will not tell the client about this property if it isn't
    set in the Logon dialog.  Being last makes GetPropList() easier to do. */

/* Number of columns in Status row. */

#define NUM_STATUS_ROW_PROPS 10

const static SizedSPropTagArray(NUM_STATUS_ROW_PROPS, sptaStatusRow) =
{
    NUM_STATUS_ROW_PROPS,
    {
        PR_RESOURCE_METHODS,
        PR_PROVIDER_DISPLAY,
        PR_DISPLAY_NAME,
        PR_IDENTITY_DISPLAY,
        PR_IDENTITY_ENTRYID,
        PR_IDENTITY_SEARCH_KEY,
        PR_STATUS_CODE,
        PR_STATUS_STRING,
        PR_OBJECT_TYPE,
        PR_RESOURCE_PATH
    }
};

/* List of IID's we support on open/query */

#define N_IID 3
static const LPIID lpStatusFamilyIID[N_IID] =
{
    (LPIID) &IID_IUnknown,      /* IUnknown is everyone's parent */
    (LPIID) &IID_IMAPIProp,     /* IMAPIProp follows from this   */
    (LPIID) &IID_IMAPIStatus    /* My actual interface ID        */
};

/* Local code */

static HRESULT NewSOB(LPCIID lpInterface,
    ULONG ulOpenFlags,
    LPXPL lpxpl,
    ULONG * lpulObjType,
    LPSOB * lppSOB);

static HRESULT HrLoadStatusString(LPXPL lpxpl,
    LPVOID lpvParent,
    LPTSTR * lppsz);


/*
 -  HrBuildTransportStatus
 -
 *  Purpose:
 *      Called by TransportLogon to build the Status Table entry for the
 *      Sample Transport Provider.
 *
 *  Parameters:
 *      lpxpl               The current session structure.
 *      ulFlags             0 or STATUSROW_UPDATE
 *
 *  Returns:
 *      (HRESULT)           Errors encountered if any.
 *      (Status Row)        Contains properties from session
 *
 *  Operation:
 *      This one's relatively simple: build a property value array based on
 *      data in the session structure, and call (*lpMAPISup)->ModifyStatusRow
 *      to register the row in the table.
 */

HRESULT
HrBuildTransportStatus(LPXPL lpxpl, ULONG ulFlags)
{
    SCODE sc = 0;
    LPSPropValue lpPropArray = NULL;
    LPMAPISUP lpMAPISup = lpxpl->lpMAPISup;
    LPSPropValue pPropValT;
    LPVOID lpvT;
    LPVOID lpvT1;
    LPVOID lpvT2;
    LPTSTR lpszStatus = NULL;
    ULONG ulT;

    /*  Allocate initial property array now. */

    sc = lpxpl->AllocateBuffer(sizeof(SPropValue) * (NUM_STATUS_ROW_PROPS - 1), (LPVOID *) &lpPropArray);

    if (sc)
    {
        DebugTraceSc(Status Row Allocation, sc);
        goto ret;
    }

    /*  Store the properties into the status row */

    pPropValT = lpPropArray;

    /*  1. Transport's Display Name. */

    pPropValT->ulPropTag = PR_PROVIDER_DISPLAY;
    pPropValT->Value.LPSZ = (LPTSTR) MYDISPLAYNAME;
    pPropValT++;

    /*  2. Extra methods on status object */

    pPropValT->ulPropTag = PR_RESOURCE_METHODS;
    pPropValT->Value.ul = lpxpl->ulResourceMethods;
    pPropValT++;

    /*  3. Display Name associated with session. Use email address. */

    lpvT1 = ArrayIndex (PR_SAMPLE_DISPLAY_NAME, lpxpl->lpPropArray).Value.LPSZ;
    lpvT2 = ArrayIndex (PR_SAMPLE_EMAIL_ADDRESS, lpxpl->lpPropArray).Value.LPSZ;
    ulT = (lstrlen((LPCTSTR) lpvT1)+lstrlen((LPCTSTR) lpvT2)+4)*sizeof (TCHAR);

    sc = lpxpl->AllocateMore(ulT, (LPVOID) lpPropArray, &lpvT);

    if (sc)
    {
        DebugTraceSc(Session Display Name allocation, sc);
        goto ret;
    }
    wsprintf((LPTSTR) lpvT, TEXT("%s [%s]"), (LPTSTR) lpvT1, (LPTSTR) lpvT2);
    pPropValT->ulPropTag = PR_DISPLAY_NAME;
    pPropValT->Value.LPSZ = (LPTSTR) lpvT;
    pPropValT++;

    /*  4. User's Display Name. */

    Assert(lpxpl->lpMyIDArray);
    *pPropValT = lpxpl->lpMyIDArray[1];

    Assert(pPropValT->ulPropTag == PR_SENDER_NAME);
    Assert(!IsBadStringPtr(pPropValT->Value.LPSZ, MAX_STRING));

    pPropValT->ulPropTag = PR_IDENTITY_DISPLAY;
    pPropValT++;

    /*  5. User Entry-ID. */

    Assert(lpxpl->lpMyIDArray);
    *pPropValT = lpxpl->lpMyIDArray[0];

    Assert(pPropValT->ulPropTag == PR_SENDER_ENTRYID);
    Assert(pPropValT->Value.bin.cb);
    Assert(!IsBadReadPtr(pPropValT->Value.bin.lpb, (UINT) pPropValT->Value.bin.cb));

    pPropValT->ulPropTag = PR_IDENTITY_ENTRYID;
    pPropValT++;

    /*  6. User Search Key. */

    Assert(lpxpl->lpMyIDArray);
    *pPropValT = lpxpl->lpMyIDArray[2];

    Assert(pPropValT->ulPropTag == PR_SENDER_SEARCH_KEY);
    Assert(pPropValT->Value.bin.cb);
    Assert(!IsBadReadPtr(pPropValT->Value.bin.lpb, (UINT) pPropValT->Value.bin.cb));

    pPropValT->ulPropTag = PR_IDENTITY_SEARCH_KEY;
    pPropValT++;

    /*  7. Code. Online/Offline, Send/Receive, Uploading/Downloading. */

    pPropValT->ulPropTag = PR_STATUS_CODE;
    pPropValT->Value.ul = lpxpl->ulTransportStatus;
    pPropValT++;

    /*  8. Status String based on Status Code. */

    if (HrLoadStatusString(lpxpl, lpPropArray, &lpszStatus))
    {
        pPropValT->ulPropTag = PROP_TAG(PT_ERROR, PROP_ID(PR_STATUS_STRING));
        pPropValT->Value.err = MAPI_E_NOT_FOUND;
    }
    else
    {
        pPropValT->ulPropTag = PR_STATUS_STRING;
        pPropValT->Value.LPSZ = lpszStatus;
    }
    pPropValT++;

    /*  9. Resource Path == WGAP Directory. */

    lpvT = (LPVOID) ArrayIndex(PR_SAMPLE_DIRECTORY, lpxpl->lpPropArray).Value.LPSZ;

    if (lstrlen((LPCTSTR) lpvT))
    {
        pPropValT->ulPropTag = PR_RESOURCE_PATH;
        pPropValT->Value.LPSZ = (LPTSTR) lpvT;
        pPropValT++;
    }

    /*  Status Row is built. Register it. */

    sc = GetScode(lpMAPISup->lpVtbl->ModifyStatusRow(lpMAPISup,
            (pPropValT - lpPropArray), lpPropArray, ulFlags));

    if (FAILED(sc))
        DebugTrace("ModifyStatusRow failed.\n");

ret:
    /*  Free the allocated memory */

    lpxpl->FreeBuffer(lpPropArray);

    DebugTraceSc(HrBuildTransportStatus, sc);
    return ResultFromScode(sc);
}


/*
 -  HrUpdateTransportStatus
 -
 *  Purpose:
 *      Called by Transport code to update the PR_STATUS_CODE property in the
 *      Status Table row for the Sample Transport Provider.
 *
 *  Parameters:
 *      lpxpl               The current session structure.
 *      ulFlags             Flags. Not currently used.
 *
 *  Returns:
 *      (HRESULT)           Errors encountered if any.
 *      (Status Row)        PR_STATUS_CODE and PR_STATUS_STRING updated
 *
 *  Operation:
 *      Transport should already have updated lpxpl->ulTransportStatus
 *      before calling here.  So all this routine does is construct a
 *      notification structure for a status object modification and call
 *      (*lpMAPISup)->ModifyStatusRow() to update the table.  If there is a
 *      string available in the StringTable, then our cProps goes to 2 and
 *      we assign the string to the 2nd element of rgProps.
 */

HRESULT
HrUpdateTransportStatus(LPXPL lpxpl, ULONG ulFlags)
{
    HRESULT hResult;
    ULONG cProps = 2;
    SPropValue rgProps[2];
    LPMAPISUP lpMAPISup = lpxpl->lpMAPISup;
    LPTSTR lpszStatus = NULL;

    /*  Store the new Transport Provider Status Code. */

    rgProps[0].ulPropTag = PR_STATUS_CODE;
    rgProps[0].Value.ul = lpxpl->ulTransportStatus;

    /* Set the Status String according to ulStatus */

    if (HrLoadStatusString(lpxpl, NULL, &lpszStatus))
    {
        rgProps[1].ulPropTag = PROP_TAG(PT_ERROR, PROP_ID(PR_STATUS_STRING));
        rgProps[1].Value.err = MAPI_E_NOT_FOUND;
    }
    else
    {
        rgProps[1].ulPropTag = PR_STATUS_STRING;
        rgProps[1].Value.LPSZ = lpszStatus;
    }

    /*  OK. Notify the Spooler. It will tell MAPI. */

    hResult = lpMAPISup->lpVtbl->ModifyStatusRow(lpMAPISup,
            cProps, rgProps, STATUSROW_UPDATE);

    lpxpl->FreeBuffer(lpszStatus);
    
    DebugTraceResult(ModifyStatusRow, hResult);
    return hResult;
}


/*
 -  OpenStatusEntry
 -
 *  Purpose:
 *      Called by Spooler to service client OpenEntry request.
 *
 *  Parameters:
 *      lpiid               Interface identifier.
 *      ulFlags             Open flags. The only doc'ed flag
 *                          is MAPI_MODIFY, which we don't support.
 *      lpulObjType         Pointer to a unsigned long into which
 *                          we are to store the type of the
 *                          object we've just opened.
 *      lppEntry            Points to a variable into which we
 *                          may store the object pointer.
 *
 *  Returns:
 *      (HRESULT)           E_INVALIDARG if the session
 *                          pointer isn't valid, or any other
 *                          parameter not to our liking,
 *                          or any other errors we encounter.
 *      *lpulObjType        MAPI_STATUS if we open the entry,
 *                          unchanged if not
 *      *lppEntry           Pointer to object if we open the
 *                          entry, unchanged if not
 *
 *  Operation:
 *      Validate parameters. Call NewSOB() to create the object, returning
 *      object type and object pointer into user-supplied locations.
 */

STDMETHODIMP
XPL_OpenStatusEntry(LPXPL lpxpl,
    LPCIID lpiid,
    ULONG ulFlags,
    ULONG * lpulObjType,
    LPMAPISTATUS * lppEntry)
{
    HRESULT hResult;
#ifndef MAC
    LPXPP lpxpp;
#endif

    /* Need to do weak session validation to do this */

    if (IsBadWritePtr(lpxpl, sizeof(XPL)) ||
        IsBadWritePtr((lpxpp = lpxpl->lpxppParent), sizeof(XPP)) ||
        (lpiid != NULL && IsBadReadPtr(lpiid, sizeof(IID))) ||
        (IsBadWritePtr(lpulObjType, sizeof(ULONG))) ||
        (IsBadWritePtr(lppEntry, sizeof(LPMAPISTATUS))))
    {
        DebugTraceSc(OpenStatusEntry, E_INVALIDARG);
        return ResultFromScode(E_INVALIDARG);
    }

    /* Get the Critical Section */

    EnterCriticalSection(&lpxpp->csTransport);

    /*  Validate the user's parameters always */

    /*  Invalid parameter checking: 1) make sure the passed session
        is still valid; 2) lpiid should either be null or point
        to a piece of memory at least the size of a iid; 3) lpulObjType
        must point to a writable piece of memory the size of a ulong;
        4) lppEntry must point to enough writable memory to store a
        LPMAPISTATUS. */

    if (!FIsValidSession(lpxpl))
    {
        hResult = ResultFromScode(E_INVALIDARG);
        DebugTrace("Invalid Logon object.\n");
        goto ret;
    }

    /*  We don't support MAPI_MODIFY and no other flags are spec'ed. */

    if (ulFlags & ~MAPI_MODIFY)
    {
        hResult = ResultFromScode(MAPI_E_UNKNOWN_FLAGS);
        DebugTrace("Unknown Flags.\n");
        goto ret;
    }

    if (ulFlags & MAPI_MODIFY)
    {
        hResult = ResultFromScode(MAPI_E_NO_ACCESS);
        DebugTrace("XP Support Object doesn't support Modify access.\n");
        goto ret;
    }

    /*  The argument list looks good to us. Now, if we already have opened
        a status object on this logon context, we'll just use QueryInterface
        to get a new copy of the object... */

    if (lpxpl->lpXPStatus)
    {
        hResult = lpxpl->lpXPStatus->lpVtbl->QueryInterface(lpxpl->lpXPStatus,
                (lpiid ? lpiid : &IID_IMAPIStatus), lppEntry);
                
        if (HR_FAILED(hResult))
            DebugTrace("QueryInterface failed.\n");
        else
            *lpulObjType = MAPI_STATUS;
    }
    else
    {
        /*  Or if we don't already have an object, create it, saving a
            copy in the logon context. */

        hResult = NewSOB(lpiid, ulFlags, lpxpl, 
                lpulObjType, (LPSOB *) lppEntry);
                
        if (HR_FAILED(hResult))
            DebugTrace("NewSOB failed.\n");
        else
            lpxpl->lpXPStatus = *lppEntry;
    }

ret:
    /*  Release the critical section. */

    LeaveCriticalSection(&lpxpp->csTransport);

    /*  Errors returned from this routine are always in sc. So if
        sc is zero we return 0. If it's nonzero we return a hResult
        built here from sc. */

    DebugTraceResult(OpenStatusEntry, hResult);
    return hResult;
}


/*
 -  NewSOB
 -
 *  Purpose:
 *      Called from OpenStatusEntry to create a Status Object.
 *
 *  Parameters:
 *      lpInterface         If non-null must be IID_IMAPIStatus
 *      ulOpenFlags         Open flags. The only doc'ed flag
 *                          is MAPI_MODIFY, which we don't support.
 *      lpxpl               The handle of the session for which
 *                          we want to open a Status object. In
 *                          Spooler context, a pointer to the
 *                          session data structure.
 *      lpulObjType         Pointer to a unsigned long into which
 *                          we are to store the type of the
 *                          object we've just opened.
 *      lppSOB              Points to a variable into which we
 *                          may store the object pointer.
 *
 * Returns:
 *      (HRESULT)           MAPI_E_NO_SUPPORT if an
 *                          interface is specified, or any
 *                          other errors we encounter.
 *      *lpulObjType        MAPI_STATUS if we open the entry,
 *                          unchanged if not
 *      *lppEntry           Pointer to object if we open the
 *                          entry, unchanged if not
 *
 *  Operation:
 *      Allocates the memory for the object, initializes its data
 *      members, plugs in the jump table and returns the appropriate
 *      stuff to the caller.
 */

static HRESULT
NewSOB(LPCIID lpInterface,
    ULONG ulOpenFlags,
    LPXPL lpxpl,
    ULONG * lpulObjType,
    LPSOB * lppSOB)
{
    SCODE sc;
    LPMAPISUP lpMAPISup = lpxpl->lpMAPISup;
    LPSOB lpSOB = NULL;
    ULONG i;

    *lpulObjType = 0;
    *lppSOB = NULL;

    /*  Make sure no interface (or ours) was specified. */

    if (lpInterface)
    {
        for (i = 0; i < N_IID; i++)
        {
            if (!memcmp(lpInterface, lpStatusFamilyIID[i], sizeof(IID)))
                break;
        }
    }

    if (i == N_IID)
    {
        DebugTraceSc(NewSOB, E_INVALIDARG);
        return ResultFromScode(E_INVALIDARG);
    }

    /*  Allocate space for the SOB structure */

    sc = lpxpl->AllocateBuffer(sizeof(SOB), (LPVOID *) &lpSOB);

    if (sc)
    {
        DebugTrace("Allocation of Status Object failed.\n");
        return ResultFromScode(sc);
    }

    /*  Fill in the data members and the jump table. */

    lpSOB->lpVtbl = &vtblSOB;
    lpSOB->lcInit = 1;
    lpSOB->lpsobMyAddress = lpSOB;

    lpSOB->hrLastError = 0;
    lpSOB->ulOpenFlags = ulOpenFlags;
    lpSOB->AllocateBuffer = lpxpl->AllocateBuffer;
    lpSOB->AllocateMore = lpxpl->AllocateMore;
    lpSOB->FreeBuffer = lpxpl->FreeBuffer;
    lpSOB->lpMAPISup = lpMAPISup;
    lpSOB->lpxpl = lpxpl;

    /*  Return the newly constructed object to the caller. */

    *lpulObjType = MAPI_STATUS;
    *lppSOB = lpSOB;

    return 0;
}


/*
 -  HrLoadStatusString
 -
 *  Purpose:
 *      Retrieves a Status String from the resource StringTable
 *      based on ulStatus code passed in.
 *
 *  Parameters:
 *      lpxpl               The Transport Logon session
 *      lpvParent           If non-null we chain the memory to this block
 *      lppsz               Place to copy Status String to.
 *
 * Returns:
 *      hr                  Indicating Success/Failure
 *
 */

static HRESULT
HrLoadStatusString(LPXPL lpxpl, LPVOID lpvParent, LPTSTR *lppsz)
{
    SCODE sc = S_OK;
    UINT ids;
    ULONG cb;
    ULONG ulStatus = lpxpl->ulTransportStatus;
    TCHAR szStatus[MAX_RESRC_STRING];

    /* Determine the IDS of the status.  Since the status' are bit fields
       of ulStatus, we apply this hierarcy to determine the correct string. */
    
    if (ulStatus & STATUS_INBOUND_ACTIVE)
        ids = IDS_STATUS_UPLOADING;
    else if (ulStatus & STATUS_OUTBOUND_ACTIVE)
        ids = IDS_STATUS_DOWNLOADING;
    else if (ulStatus & STATUS_INBOUND_FLUSH)
        ids = IDS_STATUS_INFLUSHING;
    else if (ulStatus & STATUS_OUTBOUND_FLUSH)
        ids = IDS_STATUS_OUTFLUSHING;
    else if ((ulStatus & STATUS_AVAILABLE) &&
            ((ulStatus & STATUS_INBOUND_ENABLED) ||
            (ulStatus & STATUS_OUTBOUND_ENABLED)))
        ids = IDS_STATUS_ONLINE;
    else if (ulStatus & STATUS_AVAILABLE)
        ids = IDS_STATUS_AVAILABLE;
    else
        ids = IDS_STATUS_OFFLINE;

    /* Attempt to load the resource into our automatic variable. */
    
    cb = LoadString(lpxpl->lpxppParent->hInst, ids, szStatus, MAX_RESRC_STRING);
    
    if (!cb)
    {
        sc = MAPI_E_CALL_FAILED;
        DebugTrace("LoadString failed in HrLoadStatusString.\n");
        goto ret;
    }

    /* We'll get the exact size of the string and put it on the heap.
       The caller had the luxury of specifying a parent block to chain
       this allocation to. */
    
    cb = (cb + 1) * sizeof(TCHAR);

    if (lpvParent)  
        sc = lpxpl->AllocateMore(cb, lpvParent, (LPVOID *)lppsz);
    else    
        sc = lpxpl->AllocateBuffer(cb, (LPVOID *)lppsz);
    
    if (FAILED(sc))
    {
        DebugTrace("Allocation failed in HrLoadStatusString.\n");
        goto ret;
    }
    
    lstrcpy(*lppsz, szStatus);

ret:
    DebugTraceSc(HrLoadStatusString, sc);
    return ResultFromScode(sc);
}


/*
 -  IMAPISTATUS::QueryInterface
 -
 *  Purpose:
 *      Standard IUnknown method.
 *
 *  Parameters:
 *      lpObject            Pointer to object
 *      lpiid               New interface to Query to
 *      lppNewObj           Where to store pointer to new object
 *
 *  Returns:
 *      (SCODE)             E_INVALIDARG if the input object
 *                          doesn't look like a SOB, if the
 *                          IID isn't readable or lppNewObj
 *                          isn't writable; E_NOINTERFACE
 *                          if we don't know the IID.
 *
 *  Operation:
 *      Validate parameters. See if the caller wants IUnknown, IMAPIProp or
 *      IStatus. If so, increment the usage count and return a new object.
 */

STDMETHODIMP
SOB_QueryInterface(LPSOB lpSOB,
    REFIID lpiid,
    LPVOID * lppNewObj)
{
    ULONG i;

    /*  Validate the parameters: 1) Does it seem to be an object?
        2) is the refcount nonzero? 3) Is there enough there for
        an interface ID? 4) Is there enough there for a new object? */

    if ((IsBadWritePtr(lpSOB, sizeof(SOB))) ||
        (lpSOB->lcInit == 0) ||
        (lpSOB->lpsobMyAddress != lpSOB) ||
        (IsBadReadPtr(lpiid, sizeof(IID))) ||
        (IsBadWritePtr(lppNewObj, sizeof(LPSOB))))
    {
        DebugTraceSc(SOB_QueryInterface, E_INVALIDARG);
        return ResultFromScode(E_INVALIDARG);
    }

    /*  See if the requested interface is one of ours */

    for (i = 0; i < N_IID; i++)
    {
        if (!memcmp(lpiid, lpStatusFamilyIID[i], sizeof(IID)))
            break;
    }

    /*  If we didn't find the interface, get out now. */

    if (i == N_IID)
    {
        /* OLE requires zeroing [out] parameters */
        *lppNewObj = NULL;
        DebugTraceSc(SOB_QueryInterface, E_NOINTERFACE);
        return ResultFromScode(E_NOINTERFACE);
    }

    /*  We'll do this one. Bump the usage count and return a new pointer. */

    ++lpSOB->lcInit;
    *lppNewObj = lpSOB;

    return hrSuccess;

}


/*
 -  IMAPISTATUS::AddRef
 -
 *  Purpose:
 *      Increment reference count if nonzero.
 *
 *  Parameters:
 *      lpObject            Pointer to object (should be SOB)
 *
 *  Returns:
 *      (ULONG)             Current reference count or zero if
 *                          it doesn't seem to be SOB.
 *
 *  Operation:
 *      Make sure it looks like a SOB, and if so, bump the reference count
 *      and return the result to the caller.
 */

STDMETHODIMP_(ULONG)
SOB_AddRef(LPSOB lpSOB)
{
    /*  If it doesn't seem to be an object or refcount is zero, return zero */

    if (IsBadWritePtr(lpSOB, sizeof(SOB)) ||
        lpSOB->lcInit == 0 ||
        lpSOB->lpsobMyAddress != lpSOB)
        return 0;

    return ++lpSOB->lcInit;

}


/*
 -  IMAPISTATUS::Release
 -
 *  Purpose:
 *      Decrement lcInit. If it's zero, release the object.
 *
 *  Parameters:
 *      lpObject            Pointer to object (should be SOB)
 *
 *  Returns:
 *      (ULONG)             Current reference count or zero if
 *                          it doesn't seem to be SOB.
 *
 *  Operation:
 *      Make sure it looks like a SOB, and if so, decrement the reference
 *      count. If the count is now zero, deallocate the object.
 *      Return the reference count to the caller.
 */

STDMETHODIMP_(ULONG)
SOB_Release(LPSOB lpSOB)
{
    /*  If it doesn't seem to be an object or refcount is zero, return zero */

    if (IsBadWritePtr(lpSOB, sizeof(SOB)) ||
        lpSOB->lcInit == 0 ||
        lpSOB->lpsobMyAddress != lpSOB)
        return 0;

    --lpSOB->lcInit;

    if (lpSOB->lcInit == 0)
    {
        DebugTrace("SOB::Release() freeing SOB.\n");

        /* Unlink the status object from the logon object */

        if (FIsValidSession(lpSOB->lpxpl))
            lpSOB->lpxpl->lpXPStatus = NULL;

        lpSOB->lpVtbl = NULL;
        (*lpSOB->FreeBuffer) (lpSOB);
        return 0;
    }

    return lpSOB->lcInit;
}


/*
 -  IMAPISTATUS::GetLastError
 -
 *  Purpose:
 *      Returns a string associated with the last HRESULT returned
 *      by the SOB object.
 *
 *  Parameters:
 *      lpObject            Pointer to object (should be SOB)
 *      hError              HRESULT that caller is interested in
 *      ulFlags             Ignored.
 *      lppszMessage        Pointer to where message pointer should
 *                          be copied.
 *
 *  Returns:
 *      (HRESULT)           E_INVALIDARG if object doesn't look
 *                          like a SOB or if any parameters look
 *                          bad; other errors if any; 0 if successful.
 *      *lppszMessage       Pointer to error message if any, else NULL
 *
 *  Operation:
 *      Confirm the parameters. Compare the HRESULT to our last saved one. If
 *      they match, copy the string into a new buffer and store the pointer
 *      into the location provided by the caller.
 */

STDMETHODIMP
SOB_GetLastError(LPSOB lpSOB,
    HRESULT hError,
    ULONG ulFlags,
    LPMAPIERROR * lppMapiError )
{
    HRESULT hResult = hrSuccess;
    SCODE   sc;
    LPTSTR  pszMessage  = NULL;

    /*  Validate the object and return pointer */

    *lppMapiError = NULL;
    
    if (IsBadWritePtr(lpSOB, sizeof(SOB)) ||
        (lpSOB->lcInit == 0) ||
        (lpSOB->lpsobMyAddress != lpSOB) ||
        (IsBadWritePtr(lppMapiError, sizeof(LPMAPIERROR))))
    {
        DebugTraceSc(SOB_GetLastError, E_INVALIDARG);
        return ResultFromScode(E_INVALIDARG);
    }

    if ( ulFlags & ~MAPI_UNICODE )
    {
        return ResultFromScode( MAPI_E_UNKNOWN_FLAGS );
    }
    
    if ( ulFlags & MAPI_UNICODE )
    {
        return ResultFromScode( MAPI_E_BAD_CHARWIDTH );
    }
    
    /*  See if we have the message the caller wants. If so,
        make a copy and pass it back. */

    if ((hError != 0) && (hError == lpSOB->hrLastError))
    {
        sc = lpSOB->AllocateBuffer( sizeof( MAPIERROR ), lppMapiError );
        if ( FAILED( sc ) )
        {
            hResult = ResultFromScode( sc );
            goto ret;
        }
        
        memset( *lppMapiError, 0, sizeof( MAPIERROR ) );
        
        (*lppMapiError)->ulVersion = MAPI_ERROR_VERSION;    
    
        hResult = MapScodeSz(GetScode(hError), lpSOB->lpxpl, &pszMessage);
        if ( HR_FAILED( hResult ) )
            goto ret;
        
        sc = lpSOB->AllocateMore( Cbtszsize( pszMessage ), *lppMapiError, 
                &(*lppMapiError)->lpszError );
        if ( FAILED( sc ) )     
        {
            hResult = ResultFromScode( sc );
            goto ret;
        }
        
        lstrcpy( (*lppMapiError)->lpszError, pszMessage );
    }

ret:
    if ( hResult )
    {
        lpSOB->FreeBuffer( *lppMapiError );
        *lppMapiError = NULL;
    }
        
    lpSOB->FreeBuffer( pszMessage );
    return hResult;
}


/*
 -  IMAPISTATUS::GetProps
 -
 *  Purpose:
 *      Returns the properties listed in the lpPropTagArray.
 *
 *  Parameters:
 *      lpObject            Pointer to object (should be SOB)
 *      lpPropTagArray      List of tags for which values are desired
 *      ulFlags             UNICODE / String8
 *      lpcValues           Pointer: where to store count of values
 *      lppPropArray        Pointer: where to store pointer to values
 *
 *  Returns:
 *      (HRESULT)           E_INVALIDARG if object doesn't look like
 *                          a SOB or if any of the parameters look
 *                          bad; other errors if any; 0 if successful.
 *      *lpcValues          Contains a property count if successful
 *      *lppPropArray       Undefined if not successful; NULL if
 *                          no properties; points to array of
 *                          properties if any.
 *
 *  Operation:
 *      Confirm the parameters. If lpPropTagArray is NULL, use our canned
 *      property list.
 *      Walk through the list, and give the caller any properties in the
 *      list which we can provide. Return count in *lpcValues and pointer to
 *      the array in *lppPropArray.
 */

STDMETHODIMP
SOB_GetProps(LPSOB lpSOB,
    LPSPropTagArray lpPropTagArray,
    ULONG ulFlags,
    ULONG * lpcValues,
    LPSPropValue * lppPropArray)
{
    SCODE sc = 0;
    HRESULT hResult = 0;
    ULONG ulT = 0;
    LPXPL lpxpl;
    HINSTANCE hInst;
    LPSPropValue lpPropArray = NULL;
    LPSPropValue lpMyIDArray = NULL;
    LPSPropTagArray lpspta = NULL;
    ULONG cb;
    LPTSTR lpsz;
    LPSPropValue lpPropT;
    BOOL fGotMemErrors = FALSE;
    BOOL fMyPTA = TRUE;
    BOOL fNoResourcePath = FALSE;

    /*  Validate the object and the return pointers */

    if (IsBadWritePtr(lpSOB, sizeof(SOB)) ||
        lpSOB->lcInit == 0 ||
        lpSOB->lpsobMyAddress != lpSOB)
    {
        hResult = ResultFromScode(E_INVALIDARG);
        goto ret;
    }

    /* Validate the Flags */
    
    if ( ulFlags & ~(MAPI_UNICODE) )
    {
        hResult = ResultFromScode( MAPI_E_UNKNOWN_FLAGS );
        goto ret;
    }
    
    if ( ulFlags & MAPI_UNICODE )
    {
        hResult = ResultFromScode( MAPI_E_BAD_CHARWIDTH );
        goto ret;
    }
    
    /*  Validate the return pointers */

    if ((IsBadWritePtr(lpcValues, sizeof(ULONG))) ||
        (IsBadWritePtr(lppPropArray, sizeof(LPSPropValue))))
    {
        hResult = ResultFromScode(E_INVALIDARG);
        goto ret;
    }

    /*  Validate the passed property tag array if any */

    if (lpPropTagArray)
    {
        if ((IsBadReadPtr(lpPropTagArray, CbNewSPropTagArray(0))) ||
            (lpPropTagArray->cValues == 0) ||
            (IsBadReadPtr(&lpPropTagArray->aulPropTag[0],
                    (UINT) (lpPropTagArray->cValues * sizeof(ULONG)))))
        {
            hResult = ResultFromScode(E_INVALIDARG);
            goto ret;
        }
        fMyPTA = FALSE;
        lpspta = lpPropTagArray;
    }
    else
    {
        /*  An array wasn't passed. Use ours. */

        lpspta = (LPSPropTagArray) &sptaStatusRow;
    }

    /*  Parameters have passed muster. Create the property array. */

    lpxpl = lpSOB->lpxpl;
    hInst = lpxpl->lpxppParent->hInst;

    sc = ScCopySessionProps(lpxpl, &lpPropArray, &lpMyIDArray);

    if (FAILED(sc))
        goto ret;

    sc = (*lpSOB->AllocateBuffer) (sizeof(SPropValue) * (lpspta->cValues), (LPVOID *) lppPropArray);
    if (sc)
    {
        hResult = ResultFromScode(sc);
        lpSOB->hrLastError = hResult;
        goto ret;
    }

    /*  Count will always equal input number since we'll either get a
        property value or a PT_ERROR. */

    for (ulT = 0; ulT < lpspta->cValues; ulT++)
    {
        switch (lpspta->aulPropTag[ulT])
        {
        case PR_RESOURCE_METHODS:
            (*lppPropArray)[ulT].ulPropTag = PR_RESOURCE_METHODS;
            (*lppPropArray)[ulT].Value.ul = lpxpl->ulResourceMethods;
            break;

        case PR_PROVIDER_DISPLAY:
            (*lppPropArray)[ulT].ulPropTag = PR_PROVIDER_DISPLAY;
            (*lppPropArray)[ulT].Value.LPSZ = (LPTSTR)MYDISPLAYNAME;
            break;

        case PR_DISPLAY_NAME:
            lpsz = ArrayIndex(PR_SAMPLE_EMAIL_ADDRESS, lpPropArray).Value.LPSZ;
            cb = sizeof(TCHAR) * (lstrlen(lpsz) + 1);

            sc = lpSOB->AllocateMore(cb, (LPVOID) *lppPropArray,
                    (LPVOID *) &((*lppPropArray)[ulT].Value.LPSZ));
                
            if (sc)
            {
                fGotMemErrors = TRUE;
                (*lppPropArray)[ulT].ulPropTag = PROP_TAG(PT_ERROR, PROP_ID(lpspta->aulPropTag[ulT]));
                (*lppPropArray)[ulT].Value.err = sc;
            }
            else
            {
                (*lppPropArray)[ulT].ulPropTag = PR_DISPLAY_NAME;
                lstrcpy((*lppPropArray)[ulT].Value.LPSZ, lpsz);
            }
            break;

        case PR_IDENTITY_DISPLAY:
            lpPropT = &(lpMyIDArray[1]);
            Assert(lpPropT);
            Assert(lpPropT->ulPropTag == PR_SENDER_NAME);

            cb = sizeof(TCHAR) * (lstrlen(lpPropT->Value.LPSZ) + 1);

            sc = lpSOB->AllocateMore(cb, (LPVOID) * lppPropArray,
                    (LPVOID *) &((*lppPropArray)[ulT].Value.LPSZ));

            if (sc)
            {
                fGotMemErrors = TRUE;
                (*lppPropArray)[ulT].ulPropTag = PROP_TAG(PT_ERROR, PROP_ID(lpspta->aulPropTag[ulT]));
                (*lppPropArray)[ulT].Value.err = sc;
            }
            else
            {
                (*lppPropArray)[ulT].ulPropTag = PR_IDENTITY_DISPLAY;
                lstrcpy((*lppPropArray)[ulT].Value.LPSZ, lpPropT->Value.LPSZ);
            }
            break;

        case PR_IDENTITY_ENTRYID:
            lpPropT = &(lpMyIDArray[0]);
            Assert(lpPropT);
            Assert(lpPropT->ulPropTag == PR_SENDER_ENTRYID);

            cb = lpPropT->Value.bin.cb;
            Assert(cb);
            Assert(!IsBadReadPtr(lpPropT->Value.bin.lpb, (UINT)cb));

            sc = lpSOB->AllocateMore(cb, (LPVOID) *lppPropArray,
                    (LPVOID *) &((*lppPropArray)[ulT].Value.bin.lpb));
                    
            if (sc)
            {
                fGotMemErrors = TRUE;
                (*lppPropArray)[ulT].ulPropTag = PROP_TAG(PT_ERROR, PROP_ID(lpspta->aulPropTag[ulT]));
                (*lppPropArray)[ulT].Value.err = sc;
            }
            else
            {
                (*lppPropArray)[ulT].ulPropTag = PR_IDENTITY_ENTRYID;
                (*lppPropArray)[ulT].Value.bin.cb = cb;
                if (cb)
                    memcpy((*lppPropArray)[ulT].Value.bin.lpb,
                            lpPropT->Value.bin.lpb, (UINT)cb);
            }
            break;

        case PR_IDENTITY_SEARCH_KEY:
            lpPropT = &(lpMyIDArray[2]);
            Assert(lpPropT);
            Assert(lpPropT->ulPropTag == PR_SENDER_SEARCH_KEY);

            cb = lpPropT->Value.bin.cb;
            Assert(cb);
            Assert(!IsBadReadPtr(lpPropT->Value.bin.lpb, (UINT)cb));

            sc = lpSOB->AllocateMore(cb, (LPVOID) *lppPropArray,
                    (LPVOID *) &((*lppPropArray)[ulT].Value.bin.lpb));
                    
            if (sc)
            {
                fGotMemErrors = TRUE;
                (*lppPropArray)[ulT].ulPropTag = PROP_TAG(PT_ERROR, PROP_ID(lpspta->aulPropTag[ulT]));
                (*lppPropArray)[ulT].Value.err = sc;
            }
            else
            {
                (*lppPropArray)[ulT].ulPropTag = PR_IDENTITY_SEARCH_KEY;
                (*lppPropArray)[ulT].Value.bin.cb = cb;
                if (cb)
                    memcpy((*lppPropArray)[ulT].Value.bin.lpb,
                            lpPropT->Value.bin.lpb, (UINT)cb);
            }
            break;

        case PR_STATUS_CODE:
            (*lppPropArray)[ulT].ulPropTag = PR_STATUS_CODE;
            (*lppPropArray)[ulT].Value.ul = lpxpl->ulTransportStatus;
            break;

        case PR_STATUS_STRING:
            lpsz = NULL;

            if (sc = GetScode(HrLoadStatusString(lpxpl, *lppPropArray, &lpsz)))
            {
                fGotMemErrors = TRUE;
                (*lppPropArray)[ulT].ulPropTag = PROP_TAG(PT_ERROR, PROP_ID(lpspta->aulPropTag[ulT]));
                (*lppPropArray)[ulT].Value.err = sc;
            }
            else
            {
                (*lppPropArray)[ulT].ulPropTag = PR_STATUS_STRING;
                (*lppPropArray)[ulT].Value.LPSZ = lpsz;
            }
            break;

        case PR_RESOURCE_PATH:
            lpsz = (LPVOID) ArrayIndex(PR_SAMPLE_DIRECTORY, lpPropArray).Value.LPSZ;
            Assert(lpsz);

            cb = (lstrlen(lpsz) + 1) * sizeof(TCHAR);

            if (cb == sizeof(TCHAR))
            {
                if (fMyPTA)
                {
                    fNoResourcePath = TRUE;
                }
                else
                {
                    (*lppPropArray)[ulT].ulPropTag = PROP_TAG(PT_ERROR, PROP_ID(PR_RESOURCE_PATH));
                    (*lppPropArray)[ulT].Value.err = MAPI_E_NOT_FOUND;
                }
                break;
            }

            sc = (*lpSOB->AllocateMore) (cb, (LPVOID) * lppPropArray, (LPVOID *) &((*lppPropArray)[ulT].Value.LPSZ));

            if (sc)
            {
                fGotMemErrors = TRUE;
                (*lppPropArray)[ulT].ulPropTag = PROP_TAG(PT_ERROR, PROP_ID(PR_RESOURCE_PATH));
                (*lppPropArray)[ulT].Value.err = sc;
            }
            else
            {
                (*lppPropArray)[ulT].ulPropTag = PR_RESOURCE_PATH;
                lstrcpy((*lppPropArray)[ulT].Value.LPSZ, lpsz);
            }
            break;

        case PR_OBJECT_TYPE:
            (*lppPropArray)[ulT].ulPropTag = PR_OBJECT_TYPE;
            (*lppPropArray)[ulT].Value.ul = MAPI_STATUS;
            break;

        default:
            (*lppPropArray)[ulT].ulPropTag = PROP_TAG(PT_ERROR, PROP_ID(lpspta->aulPropTag[ulT]));
            (*lppPropArray)[ulT].Value.err = MAPI_E_NOT_FOUND;

            hResult = ResultFromScode(MAPI_W_ERRORS_RETURNED);
            lpSOB->hrLastError = hResult;
            break;
        }
    }

    /*  Store number of properties we're returning, i.e., the number
        asked for (with possible errors). */

    if (fMyPTA && fNoResourcePath)
        *lpcValues = lpspta->cValues - 1;
    else    
        *lpcValues = lpspta->cValues;

    if (fGotMemErrors)
    {
        hResult = ResultFromScode(MAPI_W_ERRORS_RETURNED);
        lpSOB->hrLastError = hResult;
    }

ret:
    lpxpl->FreeBuffer(lpPropArray);
    lpxpl->FreeBuffer(lpMyIDArray);

    DebugTraceResult(SOB_GetProps, hResult);
    return hResult;
}


/*
 -  IMAPISTATUS::GetPropList
 -
 *  Purpose:
 *
 *  Parameters:
 *      lpObject            Pointer to object (should be SOB)
 *      ulFlags             UNICODE / String8
 *      lppPropTagArray     Pointer: where to store pointer to tags
 *
 *  Returns:
 *      (HRESULT)           E_INVALIDARG if object doesn't look like a
 *                          SOB or if any of the parameters look bad;
 *                          other errors if any; 0 if successful.
 *      *lppPropArray       Undefined if not successful; points to
 *                          array of property tags otherwise.
 *
 *  Operation:
 *      Validate parameters; Create enough memory for out property tag list,
 *      copy the list into it and do so. Return the new list into the caller's
 *      pointer.
 */

STDMETHODIMP
SOB_GetPropList(LPSOB lpSOB,
    ULONG ulFlags,
    LPSPropTagArray * lppPropTagArray)
{
    SCODE sc;
    HRESULT hResult = 0;
    LPXPL lpxpl;
    ULONG ulArraySize;
    ULONG cActualProps;

    /* Validate the Flags */
    
    if ( ulFlags & ~(MAPI_UNICODE) )
    {
        hResult = ResultFromScode( MAPI_E_UNKNOWN_FLAGS );
        goto ret;
    }
    
    if ( ulFlags & MAPI_UNICODE )
    {
        hResult = ResultFromScode( MAPI_E_BAD_CHARWIDTH );
        goto ret;
    }
    
    /*  Validate the object and the return pointers */

    if ((IsBadWritePtr(lpSOB, sizeof(SOB))) ||
        (lpSOB->lcInit == 0) ||
        (lpSOB->lpsobMyAddress != lpSOB) ||
        (IsBadWritePtr(lppPropTagArray, sizeof(LPSPropTagArray))))
    {
        hResult = ResultFromScode(E_INVALIDARG);
        goto ret;
    }

    lpxpl = lpSOB->lpxpl;

    /* If WGAP Directory is empty, don't tell them about this property. */
    cActualProps = sptaStatusRow.cValues;

    if (lstrlen((LPTSTR) ArrayIndex(PR_SAMPLE_DIRECTORY, lpxpl->lpPropArray).Value.LPSZ))
        ulArraySize = CbNewSPropTagArray(sptaStatusRow.cValues);
    else
    {
        /* Adjust for removing a prop tag */
        cActualProps--;
        ulArraySize = CbNewSPropTagArray(sptaStatusRow.cValues - 1);
    }

    /*  Allocate the required amount of memory */

    sc = (*lpSOB->AllocateBuffer) (ulArraySize, (LPVOID *) lppPropTagArray);

    if (sc)
    {
        hResult = ResultFromScode(sc);
        lpSOB->hrLastError = hResult;
        goto ret;
    }

    /*  Copy the contents of our canned property tag array into the buffer */

    if (ulArraySize)
    {
        memcpy(*lppPropTagArray, &sptaStatusRow, (size_t) ulArraySize);
        (*lppPropTagArray)->cValues = cActualProps;
    }

ret:
    DebugTraceResult(SOB_GetPropList, hResult);
    return hResult;
}


/*
 -  SOB_SettingsDialog
 -
 *  Purpose:
 *      This routine will invoke the Transports Logon dialog to allow
 *      the user to change the Logon properties.  The lpPropArray on the
 *      current session will be updated.
 *
 *  Parameters:
 *      lpSOB               The status object for this transport session
 *      ulUIParam           The HWnd of the caller (may be null)
 *      ulFlags             If UI_READONLY will not update the lpPropArray
 *
 *  Returns:
 *      hResult             Indicating success/failure.
 *
 *  Operation:
 *
 */

STDMETHODIMP
SOB_SettingsDialog(LPSOB lpSOB,
    ULONG ulUIParam,
    ULONG ulFlags)
{
    HRESULT hResult = hrSuccess;
    SCODE sc = S_OK;
    LPSPropValue lpMyIDArray = NULL;
    LPVOID lpvT, lpvT2;
    ULONG ulCount = MAX_LOGON_PROPERTIES - TEMP_LOGON_PROPERTIES;
    ULONG ulT;
    LPXPL lpxpl = lpSOB->lpxpl;
    LPSPropValue lpPropArray = NULL;
    LPMAPISUP lpMAPISup = lpxpl->lpMAPISup;
    LPPROFSECT lpProfileObj = NULL;
    XPDLG XPDialog;
    BOOL fNeedUI = TRUE;
    BOOL fInCS = FALSE;

    if ((IsBadReadPtr(lpSOB, sizeof(SOB))) ||
        (lpSOB->lcInit == 0) ||
        (lpSOB->lpsobMyAddress != lpSOB))
    {
        hResult = ResultFromScode(E_INVALIDARG);
        goto ret;
    }

    if (ulFlags & ~UI_READONLY)
    {
        hResult = ResultFromScode(E_INVALIDARG);
        goto ret;
    }

    /* Get the Critical Section */

    EnterCriticalSection(&(lpxpl->lpxppParent->csTransport));

    fInCS = TRUE;

    sc = ScCopySessionProps(lpxpl, &lpPropArray, NULL);

    if (FAILED(sc))
        goto ret;

    /* Fill in the logon UI structure */

    XPDialog.hInst = lpxpl->lpxppParent->hInst;
    XPDialog.hwnd = (HWND) ulUIParam;
    XPDialog.lppPropArray = &lpPropArray;
    XPDialog.lpPTArray = (LPSPropTagArray) &sptLogonArray;
    XPDialog.AllocateBuffer = lpxpl->AllocateBuffer;
    XPDialog.AllocateMore = lpxpl->AllocateMore;
    XPDialog.FreeBuffer = lpxpl->FreeBuffer;
    XPDialog.lpMalloc = lpxpl->lpxppParent->lpMalloc;
    XPDialog.lpMAPISup = lpxpl->lpMAPISup;
    XPDialog.fLogon = FALSE;
    XPDialog.ulFlags = ulFlags;

    while (fNeedUI)
    {
        sc = ScDoLogonDlg(&XPDialog);

        if (FAILED(sc))
        {
            DebugTraceSc(Logon UI activation, sc);
            goto ret;
        }

        if (lpPropArray)
        {
            /* Got a prop array, make sure everything in it is good */

            for (ulT = 0; ulT < ulCount; ulT++)
            {
                if (PROP_TYPE((lpPropArray)[ulT].ulPropTag) == PT_ERROR)
                {
                    DebugTrace("Property %x not available.\n", PROP_ID((lpPropArray)[ulT].ulPropTag));
                    sc = MAPI_E_NO_ACCESS;
                }
            }
        }
        else
            sc = MAPI_E_NO_ACCESS;

        if (sc)
        {
            DebugTraceSc(Logon UI returning, sc);
            goto ret;
        }

        /* Do some simple validation of the Logon Props */

        sc = ScCheckLogonProps(&XPDialog, TRUE);

        if (sc == MAPI_E_USER_CANCEL)
            goto ret;
        else if (sc == MAPI_E_INVALID_PARAMETER)
            continue;
        else
            fNeedUI = FALSE;

        /* If we get here, everything is fine and we can proceed. But first
           we should write the properties out if the user is willing. */

        ulT = ArrayIndex(PR_SAMPLE_FLAGS, lpPropArray).Value.ul;

        if ((ulT & PR_SAMPLE_FLAG_SAVE_DATA))
        {
            /* Try to open our profile. */

            hResult = lpMAPISup->lpVtbl->OpenProfileSection(lpMAPISup,
                (LPMAPIUID) NULL, MAPI_MODIFY, &lpProfileObj);

            if (HR_FAILED(hResult))
            {
              DebugTraceResult(MAPISUP: :OpenProfileSection, hResult);
                goto ret;
            }

            hResult = lpProfileObj->lpVtbl->SetProps(lpProfileObj,
                ulCount, lpPropArray, NULL);

            if (HR_FAILED(hResult))
            {
              DebugTraceResult(PROFSECT: :SetProps, hResult);
                goto ret;
            }
        }
    }

    /* Allocate initial property array for transport ID. */

    sc = (*lpxpl->AllocateBuffer) (sizeof(SPropValue) * NUM_SENDER_PROPS,
        (LPVOID *) &lpMyIDArray);

    if (sc)
    {
        DebugTraceSc(Sender ID property array allocation, sc);
        goto ret;
    }

    memset(lpMyIDArray, 0, sizeof(SPropValue) * NUM_SENDER_PROPS);

    /* Create the One-Off directly into the property value structure. */

    lpMyIDArray[0].ulPropTag = PR_SENDER_ENTRYID;

    hResult = lpMAPISup->lpVtbl->CreateOneOff(lpMAPISup,
        ArrayIndex(PR_SAMPLE_DISPLAY_NAME, lpPropArray).Value.LPSZ,
        ArrayIndex(PR_SAMPLE_EMAIL_ADDR_TYPE, lpPropArray).Value.LPSZ,
        ArrayIndex(PR_SAMPLE_EMAIL_ADDRESS, lpPropArray).Value.LPSZ,
        fMapiUnicode,
        &lpMyIDArray[0].Value.bin.cb,
        (LPENTRYID *) &lpMyIDArray[0].Value.bin.lpb);

    if (hResult)
    {
      DebugTraceResult(MAPISUP: :CreateOneOff, hResult);
        lpMyIDArray[0].Value.bin.lpb = NULL;
        goto ret;
    }

    /* Create the PR_SENDER_NAME property value. */

    lpvT2 = ArrayIndex(PR_SAMPLE_DISPLAY_NAME, lpPropArray).Value.LPSZ;
    ulT = lstrlen((LPCTSTR) lpvT2) + 1;

    sc = (*lpxpl->AllocateMore) (ulT, (LPVOID) lpMyIDArray,
        (LPVOID *) &lpvT);

    if (sc)
    {
        DebugTraceSc(User Display Name allocation, sc);
        goto ret;
    }

    lstrcpy((LPTSTR) lpvT, (LPCTSTR) lpvT2);

    lpMyIDArray[1].ulPropTag = PR_SENDER_NAME;
    lpMyIDArray[1].Value.LPSZ = (LPTSTR) lpvT;

    /* Create the PR_SENDER_SEARCH_KEY value. */

    lpMyIDArray[2].ulPropTag = PR_SENDER_SEARCH_KEY;

    /* Size of property = type plus colon plus address plus null. */

    ulT = 2 + lstrlen(ArrayIndex(PR_SAMPLE_EMAIL_ADDR_TYPE, lpPropArray).Value.LPSZ) +
        lstrlen(ArrayIndex(PR_SAMPLE_EMAIL_ADDRESS, lpPropArray).Value.LPSZ);

    sc = (*lpxpl->AllocateMore) (ulT, (LPVOID) lpMyIDArray,
        (LPVOID *) &lpvT);

    if (sc)
    {
        DebugTraceSc(User Search Key allocation, sc);
        goto ret;
    }

    /* PR_SENDER_SEARCH_KEY is "TYPE:ADDRESS" folded to uppercase. */

    wsprintf((LPTSTR) lpvT, TEXT("%s:%s"),
        (ArrayIndex(PR_SAMPLE_EMAIL_ADDR_TYPE, lpPropArray).Value.LPSZ),
        (ArrayIndex(PR_SAMPLE_EMAIL_ADDRESS, lpPropArray).Value.LPSZ));

    CharUpperBuff((LPTSTR) lpvT, (UINT)-- ulT);

    lpMyIDArray[2].Value.bin.cb = sizeof(TCHAR) * (1 + lstrlen((LPTSTR) lpvT));
    lpMyIDArray[2].Value.bin.lpb = lpvT;

    /* Replace the original PropArray with the new one. */

    lpxpl->FreeBuffer(lpxpl->lpPropArray);
    lpxpl->lpPropArray = lpPropArray;

    /* Now, free the original User Display Name and Entry-ID */

    if (lpxpl->lpMyIDArray)
    {
        (*lpxpl->FreeBuffer) (lpxpl->lpMyIDArray[0].Value.bin.lpb);
        (*lpxpl->FreeBuffer) (lpxpl->lpMyIDArray);
    }

    lpxpl->lpMyIDArray = lpMyIDArray;

    hResult = HrBuildTransportStatus(lpxpl, STATUSROW_UPDATE);

ret:
    /* Release the Critical Section. */

    if (fInCS)
        LeaveCriticalSection(&(lpxpl->lpxppParent->csTransport));

    UlRelease(lpProfileObj);

    if (lpPropArray && (lpxpl->lpPropArray != lpPropArray))
        lpxpl->FreeBuffer(lpPropArray);

    if (lpMyIDArray && (lpxpl->lpMyIDArray != lpMyIDArray))
    {
        (*lpxpl->FreeBuffer) (lpMyIDArray[0].Value.bin.lpb);
        (*lpxpl->FreeBuffer) (lpMyIDArray);
    }

    if (!hResult && sc)
        hResult = ResultFromScode(sc);

    DebugTraceResult(SOB_SettingsDialog, hResult);
    return hResult;
}


/*
 -  SOB_FlushQueues
 -
 *  Purpose:
 *      Logon object method used by Spooler if FlushQueues is called on
 *      the spooler status object.
 *
 *  Parameters:
 *      lpSOB               This pointer for Status Object
 *      ulUIParam           Window handle
 *      cbTargetTransport   Count of bytes in Entryid. Zero.
 *      lpTargetTransport   Entryid of transport. NULL.
 *      ulFlags
 *
 *  Returns:
 *      (HRESULT)           E_INVALIDARG if object doesn't
 *                          look like a XPL; MAPI_E_NO_SUPPORT
 *                          otherwise.
 *
 *  Operation:
 *      Validate the object pointer.
 */

STDMETHODIMP
SOB_FlushQueues(LPSOB lpSOB,
    ULONG ulUIParam,
    ULONG cbTargetTransport,
    LPENTRYID lpTargetTransport,
    ULONG ulFlags)
{
    HRESULT hResult;

    if ((IsBadReadPtr(lpSOB, sizeof(SOB))) ||
        (lpSOB->lcInit == 0) ||
        (lpSOB->lpsobMyAddress != lpSOB))
    {
        return ResultFromScode(E_INVALIDARG);
    }

    if (ulFlags & FLUSH_UPLOAD)
        lpSOB->lpxpl->ulTransportStatus |= STATUS_OUTBOUND_FLUSH;
    if (ulFlags & FLUSH_DOWNLOAD)
        lpSOB->lpxpl->ulTransportStatus |= STATUS_INBOUND_FLUSH;

    hResult = HrUpdateTransportStatus(lpSOB->lpxpl, 0L);
    return hResult;
}


/*
 -  Unimplemented functions. Stubbed to give access or NYI.
 -
 */

STDMETHODIMP
SOB_SaveChanges(LPSOB lpSOB,
    ULONG ulFlags)
{
    /*  Do parameter validation */

    if (IsBadReadPtr(lpSOB, sizeof(SOB)) ||
        (lpSOB->lcInit == 0) ||
        (lpSOB->lpsobMyAddress != lpSOB) ||
        (ulFlags & ~(KEEP_OPEN_READWRITE | KEEP_OPEN_READONLY | FORCE_SAVE)))
    {
        DebugTraceSc(SOB_SaveChanges, E_INVALIDARG);
        return ResultFromScode(E_INVALIDARG);
    }
    
    DebugTraceSc(SOB_SaveChanges, MAPI_E_NO_ACCESS);
    return (ResultFromScode(MAPI_E_NO_ACCESS));
}

STDMETHODIMP
SOB_OpenProperty(LPSOB lpSOB,
    ULONG ulPropTag,
    LPCIID lpiid,
    ULONG ulInterfaceOptions,
    ULONG ulFlags,
    LPUNKNOWN * lppUnk)
{
    /*  Do parameter validation */

    if (IsBadReadPtr(lpSOB, sizeof(SOB)) ||
        (lpSOB->lcInit == 0) ||
        (lpSOB->lpsobMyAddress != lpSOB) ||
        (!lpiid || IsBadReadPtr(lpiid, sizeof(IID))) ||
        (ulFlags & ~(MAPI_CREATE | MAPI_MODIFY | MAPI_DEFERRED_ERRORS)) ||
        (!lppUnk || IsBadWritePtr(lppUnk, sizeof(LPVOID))))
    {
        DebugTraceSc(SOB_OpenProperty, E_INVALIDARG);
        return ResultFromScode(E_INVALIDARG);
    }
    
    if ( ulInterfaceOptions & ~(MAPI_UNICODE) )
    {
        return ResultFromScode( MAPI_E_UNKNOWN_FLAGS );
    }
    
    if ( ulInterfaceOptions & MAPI_UNICODE )
    {
        return ResultFromScode( MAPI_E_UNKNOWN_FLAGS );
    }
    
    DebugTraceSc(SOB_OpenProperty, MAPI_E_NO_SUPPORT);
    return (ResultFromScode(MAPI_E_NO_SUPPORT));
}

STDMETHODIMP
SOB_SetProps(LPSOB lpSOB,
    ULONG cValues,
    LPSPropValue lpPropArray,
    LPSPropProblemArray * lppProblems)
{
    /*  Do parameter validation */

    if (IsBadWritePtr(lpSOB, sizeof(SOB)) ||
        (lpSOB->lcInit == 0) ||
        (lpSOB->lpsobMyAddress != lpSOB) ||
        (cValues && IsBadReadPtr(lpPropArray, (UINT)cValues*sizeof(SPropValue))) ||
        (IsBadWritePtr(lppProblems, sizeof(LPSPropProblemArray))))
    {
        DebugTraceSc(SOB_SetProps, E_INVALIDARG);
        return ResultFromScode(E_INVALIDARG);
    }

    DebugTraceSc(SOB_SetProps, MAPI_E_NO_ACCESS);
    return (ResultFromScode(MAPI_E_NO_ACCESS));
}

STDMETHODIMP
SOB_DeleteProps(LPSOB lpSOB,
    LPSPropTagArray lpPropTagArray,
    LPSPropProblemArray * lppProblems)
{
    /*  Do parameter validation */

    if (IsBadWritePtr(lpSOB, sizeof(SOB)) ||
        (lpSOB->lcInit == 0) ||
        (lpSOB->lpsobMyAddress != lpSOB) ||
        (!lpPropTagArray) ||
        (IsBadReadPtr(lpPropTagArray, (UINT)lpPropTagArray->cValues*sizeof(ULONG))) ||
        (IsBadWritePtr(lppProblems, sizeof(LPSPropProblemArray))))
    {
        DebugTraceSc(SOB_DeleteProps, E_INVALIDARG);
        return ResultFromScode(E_INVALIDARG);
    }

    DebugTraceSc(SOB_DeleteProps, MAPI_E_NO_ACCESS);
    return (ResultFromScode(MAPI_E_NO_ACCESS));
}

STDMETHODIMP
SOB_CopyTo(LPSOB lpSOB,
    ULONG ciidExclude,
    LPCIID rgiidExclude,
    LPSPropTagArray lpExcludeProps,
    ULONG ulUIParam,
    LPMAPIPROGRESS lpProgress,
    LPCIID lpInterface,
    LPVOID lpDestObj,
    ULONG ulFlags,
    LPSPropProblemArray * lppProblems)
{
    /*  Do parameter validation */

    if (IsBadWritePtr(lpSOB, sizeof(SOB)) ||
        (lpSOB->lcInit == 0) ||
        (lpSOB->lpsobMyAddress != lpSOB) ||
        (ciidExclude && (!rgiidExclude || IsBadReadPtr(rgiidExclude, (UINT)ciidExclude*sizeof(IID)))) ||
        (lpExcludeProps && IsBadReadPtr(lpExcludeProps, (UINT)lpExcludeProps->cValues*sizeof(ULONG))) ||
        (lpProgress && IsBadReadPtr(lpProgress, sizeof(LPMAPIPROGRESS))) ||
        (!lpInterface || IsBadReadPtr(lpInterface, sizeof(IID))) ||
        (IsBadReadPtr(lpDestObj, sizeof(LPVOID))) ||
        (ulFlags & ~(MAPI_MOVE | MAPI_NOREPLACE | MAPI_DIALOG | MAPI_DECLINE_OK)) ||
        (IsBadWritePtr(lppProblems, sizeof(LPSPropProblemArray))))
    {
        DebugTraceSc(SOB_CopyTo, E_INVALIDARG);
        return ResultFromScode(E_INVALIDARG);
    }

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

STDMETHODIMP
SOB_CopyProps(LPSOB lpSOB,
    LPSPropTagArray lpIncludeProps,
    ULONG ulUIParam,
    LPMAPIPROGRESS lpProgress,
    LPCIID lpInterface,
    LPVOID lpDestObj,
    ULONG ulFlags,
    LPSPropProblemArray * lppProblems)
{
    /*  Do parameter validation */

    if (IsBadWritePtr(lpSOB, sizeof(SOB)) ||
        (lpSOB->lcInit == 0) ||
        (lpSOB->lpsobMyAddress != lpSOB) ||
        (lpIncludeProps && IsBadReadPtr(lpIncludeProps, (UINT)lpIncludeProps->cValues*sizeof(ULONG))) ||
        (lpProgress && IsBadReadPtr(lpProgress, sizeof(LPMAPIPROGRESS))) ||
        (!lpInterface || IsBadReadPtr(lpInterface, sizeof(IID))) ||
        (IsBadReadPtr(lpDestObj, sizeof(LPVOID))) ||
        (ulFlags & ~(MAPI_MOVE | MAPI_NOREPLACE | MAPI_DIALOG | MAPI_DECLINE_OK)) ||
        (IsBadWritePtr(lppProblems, sizeof(LPSPropProblemArray))))
    {
        DebugTraceSc(SOB_CopyProps, E_INVALIDARG);
        return ResultFromScode(E_INVALIDARG);
    }

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

STDMETHODIMP
SOB_GetNamesFromIDs(LPSOB lpSOB,
    LPSPropTagArray * lppPropTags,
    LPGUID lpPropSet,
    ULONG ulFlags,
    ULONG * lpcPropNames,
    LPMAPINAMEID * * lpppPropNames)
{
    /*  Do parameter validation */

    if (IsBadWritePtr(lpSOB, sizeof(SOB)) ||
        (lpSOB->lcInit == 0) ||
        (lpSOB->lpsobMyAddress != lpSOB) ||
        (IsBadReadPtr(lppPropTags, sizeof(LPSPropTagArray))) ||
        (IsBadReadPtr(lpPropSet, sizeof(GUID))) ||
        (IsBadWritePtr(lpcPropNames, sizeof(ULONG))) ||
        (IsBadWritePtr(lpppPropNames, sizeof(LPVOID))))
    {
        DebugTraceSc(SOB_GetNamesFromIDs, E_INVALIDARG);
        return ResultFromScode(E_INVALIDARG);
    }

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

STDMETHODIMP
SOB_GetIDsFromNames(LPSOB lpSOB,
    ULONG cPropNames,
    LPMAPINAMEID * lppPropNames,
    ULONG ulFlags,
    LPSPropTagArray * lppPropTags)
{
    /*  Do parameter validation */

    if (IsBadWritePtr(lpSOB, sizeof(SOB)) ||
        (lpSOB->lcInit == 0) ||
        (lpSOB->lpsobMyAddress != lpSOB) ||
        (!lppPropNames || IsBadReadPtr(lppPropNames, (UINT)cPropNames*sizeof(LPMAPINAMEID))) ||
        (IsBadReadPtr(lppPropTags, sizeof(LPSPropTagArray))))
    {
        DebugTraceSc(SOB_GetIDsFromNames, E_INVALIDARG);
        return ResultFromScode(E_INVALIDARG);
    }

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

STDMETHODIMP
SOB_ChangePassword(LPSOB lpSOB,
    LPTSTR lpOldPass,
    LPTSTR lpNewPass,
    ULONG ulFlags)
{
    /*  Do parameter validation */

    if (IsBadWritePtr(lpSOB, sizeof(SOB)) ||
        (lpSOB->lcInit == 0) ||
        (lpSOB->lpsobMyAddress != lpSOB) ||
        (IsBadStringPtr(lpOldPass, MAX_STRING)) ||
        (IsBadStringPtr(lpNewPass, MAX_STRING)) ||
        (ulFlags & ~MAPI_UNICODE))
    {
        DebugTraceSc(SOB_ChangePassword, E_INVALIDARG);
        return ResultFromScode(E_INVALIDARG);
    }
    
    if ( ulFlags & MAPI_UNICODE )
    {
        DebugTraceSc(SOB_ChangePassword, MAPI_E_BAD_CHARWIDTH);
        return ResultFromScode( MAPI_E_BAD_CHARWIDTH );
    }
              
    DebugTraceSc(SOB_ChangePassword, MAPI_E_NO_SUPPORT);
    return (ResultFromScode(MAPI_E_NO_SUPPORT));
}

STDMETHODIMP
SOB_ValidateState(LPSOB lpSOB,
    ULONG ulUIParam,
    ULONG ulFlags)
{
    ULONG ulFlagMask =  REFRESH_XP_HEADER_CACHE | \
                        PROCESS_XP_HEADER_CACHE | \
                        FORCE_XP_CONNECT | \
                        FORCE_XP_DISCONNECT | \
                        SUPPRESS_UI;
                        
    /*  Do parameter validation */

    if (IsBadWritePtr(lpSOB, sizeof(SOB)) ||
        (lpSOB->lcInit == 0) ||
        (lpSOB->lpsobMyAddress != lpSOB) ||
        (ulFlags & (~(ulFlagMask))))
    {
        DebugTraceSc(SOB_ValidateState, E_INVALIDARG);
        return ResultFromScode(E_INVALIDARG);
    }

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


/* The following array maps a string identifier (IDS) to status code  */
/* (SCODE).  The order of SCODEs in the array has an external         */
/* dependency:  the order of elements in the array is dictated by     */
/* the IDS definitions in xpresrc.h.  This implicit association must  */
/* be maintained for the strings associated with string identifiers   */
/* to make sense.  Thus, if either this structure or the rc.h defines */
/* changes, the other must change to match it.                        */

SCODE mpIdsScode[] =
{
    SUCCESS_SUCCESS,
    MAPI_E_BUSY,
    MAPI_E_CALL_FAILED,
    MAPI_E_INVALID_PARAMETER,
    MAPI_E_NO_ACCESS,
    MAPI_E_NO_SUPPORT,
    MAPI_E_NOT_FOUND,
    MAPI_E_UNKNOWN_FLAGS,
    MAPI_E_VERSION,
    MAPI_E_NOT_ENOUGH_MEMORY,
    MAPI_W_ERRORS_RETURNED
};


/*
 *  MapScodeSz
 *
 *  Purpose:
 *      Look up an SCODE in a mapping of IDS <-> SCODE to find its
 *      associated informational string and return it (with memory
 *      allocated by this function) to the caller.
 *
 *  Arguments:
 *      sc          The SCODE to look up.
 *      lpxpl       Pointer to the Transport Logon object
 *      lppszError  Location in which to place an address to a
 *                  newly allocated buffer containing the
 *                  informational string associated with scArg.
 *
 *  Returns:
 *      HRESULT
 *
 *  Errors:
 *      MAPI_E_NO_MEMORY    Could not allocate space for
 *                          the return string.
 */

HRESULT
MapScodeSz(SCODE scArg, LPXPL lpxpl, LPTSTR * lppszError)
{
    HRESULT hr = hrSuccess;
    SCODE sc = SUCCESS_SUCCESS;
    ULONG cb;
    UINT ui;
    UINT ids;
    UINT uiMax = sizeof mpIdsScode / sizeof mpIdsScode[0];
    TCHAR rgch[512];

    if (!lppszError || IsBadWritePtr(lppszError, sizeof(LPVOID)))
    {
        DebugTrace("Bad lppszError in MapScodeSz\n");
        return ResultFromScode(E_INVALIDARG);
    }
    
    *lppszError = NULL;

    /* Linear search in mpIdsScode for sc. */

    for (ui = 0; ui < uiMax; ui++)
        if (mpIdsScode[ui] == scArg)
            break;

    if (ui == uiMax)
        ids = IDS_UNKNOWN_ERROR;
    else
        ids = ui + LIB_ERRORS;

    if (!LoadString(lpxpl->lpxppParent->hInst, ids, rgch, sizeof(rgch)))
    {
        hr = ResultFromScode(MAPI_E_NOT_ENOUGH_MEMORY);
        DebugTraceResult(LoadString failed in MapScodeSz, hr);
        goto ret;
    }

    /* Allocate memory for return variable and set it */

    cb = (lstrlen(rgch) + 1) * sizeof(TCHAR);
    sc = lpxpl->AllocateBuffer(cb, (LPVOID *) lppszError);

    if (sc != SUCCESS_SUCCESS)
    {
        hr = ResultFromScode(sc);
        DebugTraceResult(AllocateBuffer failed in MapScodeSz, hr);
        goto ret;
    }

    lstrcpy(*lppszError, rgch);

ret:
    DebugTraceResult(MapScodeSz, hr);
    return hr;
}
