XPSTATUS.C
/* 
 -  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 = NULL; 
    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: 
    if(lpxpl) 
    { 
        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; 
}