//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
/*++


Module Name:

    scutil.cxx

Abstract:

    Small client utilities


--*/
#include <sc.hxx>

#include <mq.h>
#include <scapi.h>

#include <scfile.hxx>
#include <scutil.hxx>

#include <scpacket.hxx>
#include <scqueue.hxx>
#include <scqman.hxx>
#include <srmpDefs.hxx>

#include <mq.h>
#include <mqformat.h>

#include <pkfuncs.h>
#include <service.h>
#include <bcrypt.h>

void scutil_uuidgen (GUID *pguid) {
    if (gMachine->CeGenerateGUID && (gMachine->CeGenerateGUID (pguid) == 0))
        return;

    static int iserial = 0;

    SYSTEMTIME st;
    GetLocalTime (&st);

    SystemTimeToFileTime (&st, (FILETIME *)pguid->Data4);
    pguid->Data1 = ++iserial;

    BCryptGenRandom(NULL, (unsigned char *)&pguid->Data2, sizeof(pguid->Data2), BCRYPT_USE_SYSTEM_PREFERRED_RNG);
    BCryptGenRandom(NULL, (unsigned char *)&pguid->Data3, sizeof(pguid->Data3), BCRYPT_USE_SYSTEM_PREFERRED_RNG);
}

unsigned int scutil_now (void) {
    SYSTEMTIME st;
    GetSystemTime (&st);

    LARGE_INTEGER ft;
    SystemTimeToFileTime (&st, (FILETIME *)&ft);

    ft.QuadPart -= 0x019db1ded53e8000;
    ft.QuadPart /= 10000000;

    return ft.LowPart;
}

static WCHAR *scutil_MachineJournalQueue (void) {
    WCHAR szMJQ[_MAX_PATH];
    if (FAILED(StringCchPrintfW(szMJQ, _MAX_PATH, L"MACHINE=" SC_GUID_FORMAT L";JOURNAL", SC_GUID_ELEMENTS((&gMachine->guid)))))
        return NULL;
    return svsutil_wcsdup (szMJQ);
}

static WCHAR *scutil_DeadLetterQueue (void) {
    WCHAR szDLQ[_MAX_PATH];
    if (FAILED(StringCchPrintfW(szDLQ, _MAX_PATH, L"MACHINE=" SC_GUID_FORMAT L";DEADLETTER", SC_GUID_ELEMENTS((&gMachine->guid)))))
        return NULL;

    return svsutil_wcsdup (szDLQ);
}

/*
int scutil_IsDeadLetterFormatName (WCHAR *a_lpszFormatName) {
    if (_wcsnicmp (a_lpszFormatName, MSMQ_SC_FORMAT_MACHINE, SVSUTIL_CONSTSTRLEN (MSMQ_SC_FORMAT_MACHINE)) != 0)
        return FALSE;

    WCHAR *p = wcsrchr (a_lpszFormatName, L';');
    if ((! p) || (_wcsicmp (p, MSMQ_SC_FORMAT_DEADLT) != 0))
        return FALSE;

    WCHAR *lpTrueDLQ = scutil_DeadLetterQueue ();
    int iRes = _wcsicmp (a_lpszFormatName, lpTrueDLQ) == 0;
    g_funcFree  (lpTrueDLQ, g_pvFreeData);
    return iRes;
}

*/

//
//    Pathname is of a form machine[\\private$]\\filename
//
//    Only private and direct formats are supported, so insist on them.
//
WCHAR    *scutil_MakeFormatName (WCHAR *a_lpszPathName, ScQueueParms *a_pqp) {
    SVSUTIL_ASSERT_PREFAST (a_lpszPathName && a_pqp);
    
    SVSUTIL_ASSERT (a_pqp->bIsIncoming);
    SVSUTIL_ASSERT (a_pqp->bFormatType == SCFILE_QP_FORMAT_OS);

    if (a_pqp->bIsDeadLetter)
        return scutil_DeadLetterQueue ();

    if (a_pqp->bIsMachineJournal)
        return scutil_MachineJournalQueue ();

    WCHAR *lpHost = a_lpszPathName;
    WCHAR *lpPath = wcschr (a_lpszPathName, L'\\');

    if (! lpPath)
        return NULL;

    int ccHostName = lpPath - a_lpszPathName;

    if ((ccHostName == 1) && (*lpHost == L'.')) {
        lpHost = gMachine->lpszHostName;
        ccHostName = wcslen (lpHost);
    }

    int ccQueueName = wcslen (lpPath) + 1;
    int ccTag = 0;

    if (a_pqp->bIsJournal)
        ccTag = SVSUTIL_CONSTSTRLEN(MSMQ_SC_FORMAT_JOURNAL);

    WCHAR *lpszFormatName = (WCHAR *)g_funcAlloc ((ccQueueName + ccHostName + ccTag +
                SVSUTIL_CONSTSTRLEN(MSMQ_SC_FORMAT_DIRECT_OS)) * sizeof(WCHAR), g_pvAllocData);

    if (! lpszFormatName)
        return NULL;

    memcpy (lpszFormatName, MSMQ_SC_FORMAT_DIRECT_OS, sizeof(WCHAR) * SVSUTIL_CONSTSTRLEN(MSMQ_SC_FORMAT_DIRECT_OS));
    memcpy (&lpszFormatName[SVSUTIL_CONSTSTRLEN(MSMQ_SC_FORMAT_DIRECT_OS)], lpHost, ccHostName * sizeof(WCHAR));
    memcpy (&lpszFormatName[ccHostName + SVSUTIL_CONSTSTRLEN(MSMQ_SC_FORMAT_DIRECT_OS)], lpPath, ccQueueName * sizeof(WCHAR));

    if (a_pqp->bIsJournal)
        memcpy (&lpszFormatName[ccHostName + ccQueueName + SVSUTIL_CONSTSTRLEN(MSMQ_SC_FORMAT_DIRECT_OS) - 1], MSMQ_SC_FORMAT_JOURNAL, sizeof (MSMQ_SC_FORMAT_JOURNAL));

    return lpszFormatName;
}

WCHAR    *scutil_MakeFormatName (WCHAR *a_lpszHostName, WCHAR *a_lpszQueueName, ScQueueParms *a_pqp) {
    if (a_pqp->bIsDeadLetter)
        return scutil_DeadLetterQueue ();

    if (a_pqp->bIsMachineJournal)
        return scutil_MachineJournalQueue ();

    int ccHostNameLen  = wcslen (a_lpszHostName);
    int ccQueueNameLen = wcslen (a_lpszQueueName);
    int ccx1 =   (a_pqp->bFormatType == SCFILE_QP_FORMAT_HTTP)  ? SVSUTIL_CONSTSTRLEN(MSMQ_SC_FORMAT_DIRECT_HTTP) :
                ((a_pqp->bFormatType == SCFILE_QP_FORMAT_HTTPS) ? SVSUTIL_CONSTSTRLEN(MSMQ_SC_FORMAT_DIRECT_HTTPS) :
                ((a_pqp->bFormatType == SCFILE_QP_FORMAT_TCP)   ? SVSUTIL_CONSTSTRLEN(MSMQ_SC_FORMAT_DIRECT_TCP) :
                                                                  SVSUTIL_CONSTSTRLEN(MSMQ_SC_FORMAT_DIRECT_OS)));

    int ccx2 = a_pqp->bIsPublic ? 0 : SVSUTIL_CONSTSTRLEN(MSMQ_SC_PATHNAME_PRIVATE) + 1;

    int ccTag = 0;

    if (a_pqp->bIsJournal)
        ccTag = SVSUTIL_CONSTSTRLEN(MSMQ_SC_FORMAT_JOURNAL);

    WCHAR *lpszFormatName = (WCHAR *)g_funcAlloc ((ccQueueNameLen + ccHostNameLen + ccx1 + ccx2 + 2 + ccTag) * sizeof(WCHAR), g_pvAllocData);

    if (! lpszFormatName)
        return NULL;

    WCHAR chPathSeparator;

    switch (a_pqp->bFormatType) {
    case SCFILE_QP_FORMAT_HTTP:
        memcpy (lpszFormatName, MSMQ_SC_FORMAT_DIRECT_HTTP, sizeof(MSMQ_SC_FORMAT_DIRECT_HTTP));
        chPathSeparator = L'/';
        break;

    case SCFILE_QP_FORMAT_HTTPS:
        memcpy (lpszFormatName, MSMQ_SC_FORMAT_DIRECT_HTTPS, sizeof(MSMQ_SC_FORMAT_DIRECT_HTTPS));
        chPathSeparator = L'/';
        break;

    case SCFILE_QP_FORMAT_TCP:
        memcpy (lpszFormatName, MSMQ_SC_FORMAT_DIRECT_TCP, sizeof(MSMQ_SC_FORMAT_DIRECT_TCP));
        chPathSeparator = L'\\';
        break;

    default:
        memcpy (lpszFormatName, MSMQ_SC_FORMAT_DIRECT_OS, sizeof(MSMQ_SC_FORMAT_DIRECT_OS));
        chPathSeparator = L'\\';
        break;
    }

    memcpy (&lpszFormatName[ccx1], a_lpszHostName, ccHostNameLen * sizeof(WCHAR));
    lpszFormatName[ccx1 + ccHostNameLen] = chPathSeparator;

    if (ccx2) {
        memcpy (&lpszFormatName[ccHostNameLen + ccx1 + 1], MSMQ_SC_PATHNAME_PRIVATE, (ccx2 - 1) * sizeof(WCHAR));
        lpszFormatName[ccx1 + ccx2 + ccHostNameLen] = chPathSeparator;
    }

    memcpy (&lpszFormatName[ccHostNameLen + ccx1 + ccx2 + 1], a_lpszQueueName, (ccQueueNameLen + 1) * sizeof(WCHAR));

    if (a_pqp->bIsJournal)
        memcpy (&lpszFormatName[ccHostNameLen + ccQueueNameLen + ccx1 + ccx2 + 1], MSMQ_SC_FORMAT_JOURNAL, sizeof (MSMQ_SC_FORMAT_JOURNAL));

    return lpszFormatName;
}

//
//    Note: supported format names are
//        DIRECT=OS:machinename[\private$]\queuename
//        DIRECT=TCP:ipaddress[\private$]\queuename
//        DIRECT=HTTP://machinename/queuename
//        DIRECT=HTTPS://machinename/queuename
//
int scutil_ParseNonLocalDirectFormatName (WCHAR *a_lpszFormatName, WCHAR *&a_lpszHostName, WCHAR *&a_lpszQueueName, ScQueueParms *a_pqp) {
    SVSUTIL_ASSERT_PREFAST (a_lpszFormatName && a_pqp);
    
    SVSUTIL_ASSERT (! a_pqp->bIsIncoming);

    a_lpszHostName = NULL;
    a_lpszQueueName = NULL;

    WCHAR *ptr, *ptr2;

    if (_wcsnicmp (a_lpszFormatName, MSMQ_SC_FORMAT_DIRECT_OS, SVSUTIL_CONSTSTRLEN(MSMQ_SC_FORMAT_DIRECT_OS)) == 0) {
        a_pqp->bFormatType = SCFILE_QP_FORMAT_OS;
        ptr = &a_lpszFormatName[SVSUTIL_CONSTSTRLEN(MSMQ_SC_FORMAT_DIRECT_OS)];
        ptr2 = wcschr (ptr, L'\\');
    } else if (_wcsnicmp (a_lpszFormatName, MSMQ_SC_FORMAT_DIRECT_TCP, SVSUTIL_CONSTSTRLEN(MSMQ_SC_FORMAT_DIRECT_TCP)) == 0) {
        a_pqp->bFormatType = SCFILE_QP_FORMAT_TCP;
        ptr = &a_lpszFormatName[SVSUTIL_CONSTSTRLEN(MSMQ_SC_FORMAT_DIRECT_TCP)];
        ptr2 = wcschr (ptr, L'\\');
    } else if (_wcsnicmp (a_lpszFormatName, MSMQ_SC_FORMAT_DIRECT_HTTP, SVSUTIL_CONSTSTRLEN(MSMQ_SC_FORMAT_DIRECT_HTTP)) == 0) {
        a_pqp->bFormatType = SCFILE_QP_FORMAT_HTTP;
        ptr = &a_lpszFormatName[SVSUTIL_CONSTSTRLEN(MSMQ_SC_FORMAT_DIRECT_HTTP)];
        ptr2 = wcschr (ptr, L'/');
    } else if (_wcsnicmp (a_lpszFormatName, MSMQ_SC_FORMAT_DIRECT_HTTPS, SVSUTIL_CONSTSTRLEN(MSMQ_SC_FORMAT_DIRECT_HTTPS)) == 0) {
        a_pqp->bFormatType = SCFILE_QP_FORMAT_HTTPS;
        ptr = &a_lpszFormatName[SVSUTIL_CONSTSTRLEN(MSMQ_SC_FORMAT_DIRECT_HTTPS)];
        ptr2 = wcschr (ptr, L'/');
    } else
        return FALSE;

    if (! ptr2)
        return FALSE;

    WCHAR *ptr3;

    if (((a_pqp->bFormatType == SCFILE_QP_FORMAT_OS) || (a_pqp->bFormatType == SCFILE_QP_FORMAT_TCP)) &&
            (_wcsnicmp (ptr2 + 1, MSMQ_SC_PATHNAME_PRIVATE, SVSUTIL_CONSTSTRLEN(MSMQ_SC_PATHNAME_PRIVATE)) == 0)) {
        ptr3 = ptr2 + 1 + SVSUTIL_CONSTSTRLEN(MSMQ_SC_PATHNAME_PRIVATE);
        a_pqp->bIsPublic = FALSE;
    } else {
        ptr3 = ptr2;
        a_pqp->bIsPublic = TRUE;
    }

    if (*ptr3 != *ptr2)
        return FALSE;

    ++ptr3;

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

    WCHAR *ptr4 = wcschr (ptr3, L';');

    int ccHostName = ptr2 - ptr;
    if ((ccHostName == 1) && (*ptr == L'.')) {
        ptr = gMachine->lpszHostName;
        ccHostName = wcslen (ptr);
    }

    if (_wcsnicmp(gMachine->lpszHostName, ptr, ccHostName) == 0) {
        a_pqp->bIsIncoming = TRUE;
        return FALSE;
    }

    a_lpszHostName = (WCHAR *)g_funcAlloc ((ccHostName + 1) * sizeof(WCHAR), g_pvAllocData);

    if (! a_lpszHostName)
        return FALSE;

    memcpy (a_lpszHostName, ptr, ccHostName * sizeof(WCHAR));
    a_lpszHostName[ccHostName] = L'\0';

    if (scutil_IsLocalTCP (a_lpszHostName)) {
        g_funcFree (a_lpszHostName, g_pvFreeData);
        a_lpszHostName = NULL;
        a_pqp->bIsIncoming = TRUE;
        return FALSE;
    }

    int ccQueueName = ptr4 ? ptr4 - ptr3 : wcslen(ptr3);

    a_lpszQueueName = (WCHAR *)g_funcAlloc ((ccQueueName + 1) * sizeof(WCHAR), g_pvAllocData);

    if (! a_lpszQueueName) {
        g_funcFree (a_lpszHostName, g_pvFreeData);
        a_lpszHostName = NULL;
        return FALSE;
    }

    memcpy (a_lpszQueueName, ptr3, ccQueueName * sizeof(WCHAR));
    a_lpszQueueName[ccQueueName] = L'\0';

    return TRUE;
}

int scutil_ParsePathName (WCHAR *a_lpszPathName, WCHAR *&a_lpszHostName, WCHAR *&a_lpszQueueName, ScQueueParms *a_pqp) {
    SVSUTIL_ASSERT_PREFAST (a_lpszPathName && a_pqp);

    a_lpszHostName = NULL;
    a_lpszQueueName = NULL;

    WCHAR chPathSeparator = ((a_pqp->bFormatType == SCFILE_QP_FORMAT_HTTP) || (a_pqp->bFormatType == SCFILE_QP_FORMAT_HTTPS)) ? L'/' : L'\\';

    if (wcschr (a_lpszPathName, L';'))
        return FALSE;

    WCHAR *ptr = a_lpszPathName;
    WCHAR *ptr2 = wcschr (ptr, chPathSeparator);

    if (! ptr2)
        return FALSE;

    WCHAR *ptr3;
    int bIsPublic;

    if (_wcsnicmp (ptr2 + 1, MSMQ_SC_PATHNAME_PRIVATE, SVSUTIL_CONSTSTRLEN(MSMQ_SC_PATHNAME_PRIVATE)) == 0) {
        ptr3 = ptr2 + 1 + SVSUTIL_CONSTSTRLEN(MSMQ_SC_PATHNAME_PRIVATE);
        bIsPublic = FALSE;
    } else {
        ptr3 = ptr2;
        bIsPublic = TRUE;
    }

    if (*ptr3 != chPathSeparator)
        return FALSE;

    ++ptr3;

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

    int ccHostName = ptr2 - ptr;
    if ((ccHostName == 1) && (*ptr == L'.')) {
        ptr = gMachine->lpszHostName;
        ccHostName = wcslen (ptr);
    }

    int bIsIncoming = (_wcsnicmp(gMachine->lpszHostName, ptr, ccHostName) == 0);

    if (bIsIncoming && bIsPublic)
        return FALSE;

    a_lpszHostName = (WCHAR *)g_funcAlloc ((ccHostName + 1) * sizeof(WCHAR), g_pvAllocData);

    if (! a_lpszHostName)
        return FALSE;

    memcpy (a_lpszHostName, ptr, ccHostName * sizeof(WCHAR));
    a_lpszHostName[ccHostName] = L'\0';

    int ccQueueName = wcslen(ptr3);

    a_lpszQueueName = (WCHAR *)g_funcAlloc ((ccQueueName + 1) * sizeof(WCHAR), g_pvAllocData);

    if (! a_lpszQueueName) {
        g_funcFree (a_lpszHostName, g_pvFreeData);
        a_lpszHostName = NULL;
        return FALSE;
    }

    memcpy (a_lpszQueueName, ptr3, ccQueueName * sizeof(WCHAR));
    a_lpszQueueName[ccQueueName] = L'\0';

    a_pqp->bIsIncoming = bIsIncoming;
    a_pqp->bIsPublic   = bIsPublic;

    return TRUE;
}

WCHAR *scutil_MakePathName   (WCHAR *a_lpszHostName,   WCHAR *a_lpszQueueName, ScQueueParms *a_pqp) {
    SVSUTIL_ASSERT (a_lpszHostName && a_lpszQueueName && a_pqp);

    int ccHostLen = wcslen(a_lpszHostName);
    int ccQueueLen = wcslen (a_lpszQueueName);
    int ccPrivateTag = a_pqp->bIsPublic ? 0 : SVSUTIL_CONSTSTRLEN(MSMQ_SC_PATHNAME_PRIVATE) + 1;
    int iSz = (ccHostLen + ccQueueLen + 2 + ccPrivateTag) * sizeof(WCHAR);

    WCHAR chPathSeparator = ((a_pqp->bFormatType == SCFILE_QP_FORMAT_HTTP) || (a_pqp->bFormatType == SCFILE_QP_FORMAT_HTTPS)) ? L'/' : L'\\';

    WCHAR *lpszPathName = (WCHAR *)g_funcAlloc (iSz, g_pvAllocData);

    memcpy (lpszPathName, a_lpszHostName, sizeof(WCHAR) * ccHostLen);
    lpszPathName[ccHostLen] = chPathSeparator;

    if (ccPrivateTag) {
        memcpy (&lpszPathName[ccHostLen+1], MSMQ_SC_PATHNAME_PRIVATE, sizeof (WCHAR) * SVSUTIL_CONSTSTRLEN(MSMQ_SC_PATHNAME_PRIVATE));
        lpszPathName[ccHostLen + 1 + SVSUTIL_CONSTSTRLEN(MSMQ_SC_PATHNAME_PRIVATE)] = chPathSeparator;
    }

    memcpy (&lpszPathName[ccHostLen + 1 + ccPrivateTag], a_lpszQueueName, sizeof(WCHAR) * (ccQueueLen + 1));

    return lpszPathName;
}

WCHAR    *scutil_MakeFileName (WCHAR *a_lpszQueueHost, WCHAR *a_lpszQueueName, ScQueueParms *a_pqp) {
    SVSUTIL_ASSERT (a_lpszQueueHost && a_lpszQueueName && a_pqp);

    if (a_pqp->bIsIncoming)
        a_lpszQueueHost = MSMQ_SC_LOCALHOST;

    WCHAR *lpszFileExt = (! a_pqp->bIsIncoming) ? MSMQ_SC_EXT_OQ :
                             (a_pqp->bIsJournal ? MSMQ_SC_EXT_JQ :
                      (a_pqp->bIsMachineJournal ? MSMQ_SC_EXT_MJ : MSMQ_SC_EXT_IQ));

    int ccDirLen   = wcslen(gMachine->lpszDirName);
    int ccQueueLen = wcslen(a_lpszQueueName);
    int ccHostLen  = wcslen(a_lpszQueueHost);
    int ccPublicToken = a_pqp->bIsPublic ? SVSUTIL_CONSTSTRLEN (MSMQ_SC_FILENAME_PUBLIC) : 0;
    int ccHttpToken = 0;

    if (a_pqp->bFormatType == SCFILE_QP_FORMAT_HTTP)
        ccHttpToken = SVSUTIL_CONSTSTRLEN(MSMQ_SC_FILENAME_HTTP);
    else if (a_pqp->bFormatType == SCFILE_QP_FORMAT_HTTPS)
        ccHttpToken = SVSUTIL_CONSTSTRLEN(MSMQ_SC_FILENAME_HTTPS);

    int ccFileName = ccDirLen + ccHostLen + ccQueueLen + ccPublicToken + ccHttpToken + wcslen(lpszFileExt) + 2 + 3;

    WCHAR *lpszFileName = (WCHAR *)g_funcAlloc (ccFileName * sizeof(WCHAR), g_pvAllocData);
    if (lpszFileName==NULL)
        return NULL;

    memcpy (lpszFileName, gMachine->lpszDirName, sizeof(WCHAR) * ccDirLen);
    lpszFileName[ccDirLen] = L'\\';
    memcpy (lpszFileName + ccDirLen + 1, a_lpszQueueHost, sizeof(WCHAR) * ccHostLen);
    lpszFileName[ccDirLen + ccHostLen + 1] = L' ';
    lpszFileName[ccDirLen + ccHostLen + 2] = L'-';
    lpszFileName[ccDirLen + ccHostLen + 3] = L' ';
    if (ccPublicToken)
        memcpy (lpszFileName + ccDirLen + ccHostLen + 4, MSMQ_SC_FILENAME_PUBLIC, sizeof(MSMQ_SC_FILENAME_PUBLIC));

    if (a_pqp->bFormatType == SCFILE_QP_FORMAT_HTTP)
        memcpy (lpszFileName + ccDirLen + ccPublicToken + ccHostLen + 4, MSMQ_SC_FILENAME_HTTP, sizeof(WCHAR) * ccHttpToken);
    else if (a_pqp->bFormatType == SCFILE_QP_FORMAT_HTTPS)
        memcpy (lpszFileName + ccDirLen + ccPublicToken + ccHostLen + 4, MSMQ_SC_FILENAME_HTTPS, sizeof(WCHAR) * ccHttpToken);

    memcpy (lpszFileName + ccDirLen + ccPublicToken + ccHttpToken + ccHostLen + 4, a_lpszQueueName, sizeof(WCHAR) * ccQueueLen);
    
    int ccFileExtOffset = ccDirLen + ccPublicToken + ccHttpToken + ccHostLen + ccQueueLen + 4;
    HRESULT hr = StringCchCopyW(lpszFileName + ccFileExtOffset, ccFileName - ccFileExtOffset, lpszFileExt);
    ASSERT(SUCCEEDED(hr));

    WCHAR *p = lpszFileName + ccDirLen + ccPublicToken + ccHttpToken + ccHostLen + 4;

    while (*p) {
        if ((*p == '\\') || (*p == '/'))
            *p = '_';
        ++p;
    }

    return lpszFileName;
}

WCHAR *scutil_QFtoString (QUEUE_FORMAT *pqft) {
    WCHAR   szBuffer[_MAX_PATH];
    ULONG    ulSize = 0;

    NTSTATUS nts = MQpQueueFormatToFormatName(pqft, szBuffer, _MAX_PATH, &ulSize);
    if (nts == MQ_OK)
        return svsutil_wcsdup (szBuffer);
    if (nts == MQ_ERROR_FORMATNAME_BUFFER_TOO_SMALL) {
        WCHAR *lpRes = (WCHAR *)g_funcAlloc (ulSize * sizeof (WCHAR), g_pvAllocData);
        if (! lpRes)
            return NULL;
        if (MQ_OK == MQpQueueFormatToFormatName(pqft, lpRes, ulSize, &ulSize))
            return lpRes;

        g_funcFree (lpRes, g_pvFreeData);
    }
    return NULL;
}

/*++
Abstract:

    Format Name parsing.
    QUEUE_FORMAT <--> Format Name String conversion routines
--*/

//=========================================================
//
//  Format Name String -> QUEUE_FORMAT conversion routines
//
//=========================================================

//---------------------------------------------------------
//
//  Skip white space characters, return next non ws char
//
//  N.B. if no white space is needed uncomment next line
//#define skip_ws(p) (p)
inline LPCWSTR skip_ws(LPCWSTR p)
{
    //
    //  Don't skip first non white space
    //
    while(iswspace(*p))
    {
        ++p;
    }

    return p;
}


//---------------------------------------------------------
//
//  Skip white space characters, return next non ws char
//
inline LPCWSTR FindPathNameDelimiter(LPCWSTR p)
{
    return wcschr(p, PN_DELIMITER_C);
}


//---------------------------------------------------------
//
//  Parse Format Name Type prefix string.
//  Return next char to parse on success, 0 on failure.
//
static LPCWSTR ParsePrefixString(LPCWSTR p, QUEUE_FORMAT_TYPE& qft)
{
    const int unique = 1;
    //----------------0v-------------------------
    ASSERT(L'U' == FN_PUBLIC_TOKEN    [unique]);
    ASSERT(L'R' == FN_PRIVATE_TOKEN   [unique]);
    ASSERT(L'O' == FN_CONNECTOR_TOKEN [unique]);
    ASSERT(L'A' == FN_MACHINE_TOKEN   [unique]);
    ASSERT(L'I' == FN_DIRECT_TOKEN    [unique]);
    //----------------0^-------------------------

    //
    //  accelarate token recognition by checking 3rd character
    //
    switch(towupper(p[unique]))
    {
        //  pUblic
        case L'U':
            qft = QUEUE_FORMAT_TYPE_PUBLIC;
            if(_wcsnicmp(p, FN_PUBLIC_TOKEN, FN_PUBLIC_TOKEN_LEN) == 0)
                return (p + FN_PUBLIC_TOKEN_LEN);
            break;

        //  pRivate
        case L'R':
            qft = QUEUE_FORMAT_TYPE_PRIVATE;
            if(_wcsnicmp(p, FN_PRIVATE_TOKEN, FN_PRIVATE_TOKEN_LEN) == 0)
                return (p + FN_PRIVATE_TOKEN_LEN);
            break;

        //  cOnnector
        case L'O':
            qft = QUEUE_FORMAT_TYPE_CONNECTOR;
            if(_wcsnicmp(p, FN_CONNECTOR_TOKEN, FN_CONNECTOR_TOKEN_LEN) == 0)
                return (p + FN_CONNECTOR_TOKEN_LEN);
            break;
    
        //  mAchine
        case L'A':
            qft = QUEUE_FORMAT_TYPE_MACHINE;
            if(_wcsnicmp(p, FN_MACHINE_TOKEN, FN_MACHINE_TOKEN_LEN) == 0)
                return (p + FN_MACHINE_TOKEN_LEN);
            break;
            
        //  dIrect
        case L'I':
            qft = QUEUE_FORMAT_TYPE_DIRECT;
            if(_wcsnicmp(p, FN_DIRECT_TOKEN, FN_DIRECT_TOKEN_LEN) == 0)
                return (p + FN_DIRECT_TOKEN_LEN);
            break;
    }

    return 0;
}


//---------------------------------------------------------
//
//  Parse a guid string, into guid.
//  Return next char to parse on success, 0 on failure.
//
LPCWSTR scutil_ParseGuidString(LPCWSTR p, GUID* pg)
{
    //
    //  N.B. scanf stores the results in an int, no matter what the field size
    //      is. Thus we store the result in tmp variabes.
    //
    int n;
    UINT w2, w3, d[8];
    if(swscanf(
            p,
            GUID_FORMAT L"%n",
            &pg->Data1,
            &w2, &w3,                       //  Data2, Data3
            &d[0], &d[1], &d[2], &d[3],     //  Data4[0..3]
            &d[4], &d[5], &d[6], &d[7],     //  Data4[4..7]
            &n                              //  number of characters scaned
            ) != 11)
    {
        //
        //  not all 11 fields where not found.
        //
        return 0;
    }

    pg->Data2 = (WORD)w2;
    pg->Data3 = (WORD)w3;
    for(int i = 0; i < 8; i++)
    {
        pg->Data4[i] = (BYTE)d[i];
    }

    return (p + n);
}

// called on scapi_MQSendMessage on determining how big a buffer to allocate for packet.
ULONG CalcFwdRevViaSectionSize(CAPROPVARIANT *pEntry) {
    ULONG ulNeeded = sizeof(CDataHeader);

    for (unsigned int i =0; i < pEntry->cElems; i++) {
        PROPVARIANT *pVar = &pEntry->pElems[i];

        if (! pVar || pVar->vt != VT_LPWSTR) 
            return 0;

        ulNeeded += ((wcslen(pVar->pwszVal)+1) * sizeof(WCHAR));
    }

    SVSUTIL_ASSERT(ulNeeded > sizeof(CDataHeader));
    return ALIGNUP4(ulNeeded);
}

// called on scapi_RetrievePacketInfo to determine how space required by calling app.
ULONG CalcFwdRevViaSectionSize(CDataHeader *pFwdRevEntry, DWORD *pdwElements) {
    SVSUTIL_ASSERT(*pdwElements == 0);
    if (!pFwdRevEntry)
        return 0;

    ULONG ulNeeded  = 0;

    int   iLen      = pFwdRevEntry->GetDataLengthInWCHARs();
    WCHAR *szStart  = (WCHAR*) pFwdRevEntry->GetData();
    WCHAR *pszTrav  = szStart;

    // VIA list is stored as a NULL separated multi-string.  Elements
    // may be set to \0 to indicate an empty tag.
    while (pszTrav - szStart < iLen) {
        int iElementLen = wcslen(pszTrav)+1;

        ulNeeded += (iElementLen*sizeof(WCHAR) + sizeof(PROPVARIANT));
        pszTrav  += iElementLen;
        (*pdwElements)++;
    }
    SVSUTIL_ASSERT(pszTrav-szStart == iLen);
    return ulNeeded;
}

// Copy the (VT_VARIAT | VT_VECTOR) data associated with SOAP VIA elements
// into the user process space.  To do this, we build the buffer up in
// servicesd.exe process space and then copy it all at once into app-space.
BOOL SetFwdRevViaSection(CDataHeader *pFwdRevEntry, CAPROPVARIANT *pPropVar, DWORD dwElements, 
                         DWORD cbBufferSize, HANDLE hCallerProc, HRESULT *pHR) {
    int          iStringLen = pFwdRevEntry->GetDataLengthInWCHARs();
    WCHAR        *szStart   = (WCHAR*) pFwdRevEntry->GetData();
    WCHAR        *pszTrav   = szStart;
    // Data in servicesd.exe to write to
    WCHAR        *szWrite;
    // Data client will access (in its process space).  This is not 
    // safe to dereference in servicesd.exe.
    WCHAR        *szWriteClient;
    // Makep copy in servicesd.exe space that we write to temporarily
    PROPVARIANT  *pPropWriteBase = (PROPVARIANT *)LocalAlloc(0,cbBufferSize);
    PROPVARIANT  *pPropWrite     = pPropWriteBase;
    // Helper to actually write data to process.  We can't use MarshalBuffer_t
    // since we're not necesarrily running on PSL caller thread.
    CMarshallDataToProcess copyData(hCallerProc);

    if (! pPropWriteBase) {
        *pHR = MQ_ERROR_INSUFFICIENT_RESOURCES;
        return FALSE;
    }

    pPropVar->cElems  = dwElements;
    szWrite           = (WCHAR*) ((CHAR*)pPropWriteBase+ sizeof(PROPVARIANT)*dwElements);
    memset(pPropWriteBase,0,dwElements*sizeof(PROPVARIANT));
    szWriteClient     = (WCHAR*) ((CHAR*)pPropVar->pElems+ sizeof(PROPVARIANT)*dwElements);
    
    // VIA list is stored as a NULL separated multi-string.  Elements
    // may be set to \0 to indicate an empty tag.
    while (pszTrav - szStart < iStringLen) {
        int iElementLen = wcslen(pszTrav)+1;
        memcpy(szWrite,pszTrav,iElementLen*sizeof(WCHAR));

        pPropWrite->vt = VT_LPWSTR;
        // This is the pointer that the user mode process will use to access
        // the written out data, not the actual memory MSMQ is writing out for
        // this particular value.
        pPropWrite->pwszVal = szWriteClient;
        pPropWrite++;
        
        szWrite    += iElementLen;
        pszTrav += iElementLen;
        szWriteClient += iElementLen;
    }

    // Check we wrote out the number of bytes we thought we would.
    SVSUTIL_ASSERT(pszTrav-szStart == iStringLen);

    copyData.WriteBufToProc((UCHAR*)pPropVar->pElems, (UCHAR*)pPropWriteBase, cbBufferSize, pHR);
    LocalFree(pPropWriteBase);
    return (*pHR == MQ_OK);
}

WCHAR * GetNextHopOnFwdList(ScPacketImage *pImage) {
    // If we're receiving a message off wire (fSRMPGenerated=TRUE), then the 1st entry in pFwdVia header
    // is the current machine name's <fwd><via> entry, skip it because we want to route
    // to the next queue in line.  When we're generating the message locally (fSRMPGenerated=FALSE) then 
    // 1st element in pFwdViaHeader is queue to route to.

    if (! pImage->flags.fSRMPGenerated)
        return (WCHAR*) pImage->sect.pFwdViaHeader->GetData();

    WCHAR *szFirstElement  = (WCHAR*) pImage->sect.pFwdViaHeader->GetData();
    int    ccElementList   = pImage->sect.pFwdViaHeader->GetDataLengthInWCHARs();
    int    ccFirstElement  = wcslen(szFirstElement) + 1;

    // only one entry is in <fwd> list.
    if (ccElementList == ccFirstElement)
        return NULL;

    return (szFirstElement+ccFirstElement);
}

//---------------------------------------------------------
//
//  Parse private id uniquifier, into guid.
//  Return next char to parse on success, 0 on failure.
//
static LPCWSTR ParsePrivateIDString(LPCWSTR p, ULONG* pUniquifier)
{
    int n;
    if(swscanf(
            p,
            PRIVATE_ID_FORMAT L"%n",
            pUniquifier,
            &n                              //  number of characters scaned
            ) != 1)
    {
        //
        //  private id field was not found.
        //
        return 0;
    }

    return (p + n);
}

extern const WCHAR cszHttpPrefix[]    = L"HTTP://";
extern const WCHAR cszHttpsPrefix[]   = L"HTTPS://";
extern const DWORD ccHttpPrefix      = SVSUTIL_CONSTSTRLEN(cszHttpPrefix);
extern const DWORD ccHttpsPrefix     = SVSUTIL_CONSTSTRLEN(cszHttpsPrefix);


BOOL IsUriHttp(const WCHAR *szQueue) {
    return (0 == _wcsnicmp(szQueue,cszHttpPrefix,ccHttpPrefix));
}

BOOL IsUriHttps(const WCHAR *szQueue) {
    return (0 == _wcsnicmp(szQueue,cszHttpsPrefix,ccHttpsPrefix));
}


//---------------------------------------------------------
//
//  Parse direct token type infix string.
//  Return next char to parse on success, 0 on failure.
//
LPCWSTR ParseDirectTokenString(LPCWSTR p, DIRECT_TOKEN_TYPE& dtt)
{
    const int unique = 0;
    //-----------------------v-------------------
    ASSERT(L'O' == FN_DIRECT_OS_TOKEN   [unique]);
    ASSERT(L'T' == FN_DIRECT_TCP_TOKEN  [unique]);
    ASSERT(L'S' == FN_DIRECT_SPX_TOKEN  [unique]);
    //-----------------------^-------------------

    //
    //  accelarate token recognition by checking 1st character
    //
    switch(towupper(p[unique]))
    {
        // Os:
        case L'O':
            dtt = DT_OS;
            if(_wcsnicmp(p, FN_DIRECT_OS_TOKEN, FN_DIRECT_OS_TOKEN_LEN) == 0)
                return (p + FN_DIRECT_OS_TOKEN_LEN);
            break;

        // Tcp:
        case L'T':
            dtt = DT_TCP;
            if(_wcsnicmp(p, FN_DIRECT_TCP_TOKEN, FN_DIRECT_TCP_TOKEN_LEN) == 0)
                return (p + FN_DIRECT_TCP_TOKEN_LEN);
            break;

        // Spx:
        case L'S':
            dtt = DT_SPX;
            if(_wcsnicmp(p, FN_DIRECT_SPX_TOKEN, FN_DIRECT_SPX_TOKEN_LEN) == 0)
                return (p + FN_DIRECT_SPX_TOKEN_LEN);
            break;

        // http:// or https://
        case L'H':
            if (IsUriHttp(p))
            {
                dtt = DT_HTTP;
                return p + ccHttpPrefix;
            }
            if (IsUriHttps(p))
            {
                dtt = DT_HTTPS;
                return p + ccHttpsPrefix;
            }
            break;
    }

    return 0;
}

//---------------------------------------------------------
//
//  Parse queue name string, (private, public)
//  N.B. queue name must end with either format name suffix
//      delimiter aka ';' or with end of string '\0'
//  Return next char to parse on success, 0 on failure.
//
static LPCWSTR ParseQueueNameString(LPCWSTR p, BOOL* pfPrivate)
{
    if(_wcsnicmp(p, PRIVATE_QUEUE_PATH_INDICATIOR, PRIVATE_QUEUE_PATH_INDICATIOR_LENGTH) == 0)
    {
        *pfPrivate = TRUE;
        p += PRIVATE_QUEUE_PATH_INDICATIOR_LENGTH;
    }
    else
    {
        *pfPrivate = FALSE;
    }

    //
    //  Zero length queue name is illeagal
    //
    if(*p == L'\0')
        return 0;

    while(
        (*p != L'\0') &&
        (*p != PN_DELIMITER_C) &&
        (*p != FN_SUFFIX_DELIMITER_C)
        )
    {
        ++p;
    }

    //
    //  Path name delimiter is illeagal in queue name
    //
    if(*p == PN_DELIMITER_C)
        return 0;

    return p;
}


//---------------------------------------------------------
//
//  Parse machine name in a path name
//  N.B. machine name must end with a path name delimiter aka slash '\\'
//  Return next char to parse on success, 0 on failure.
//
static LPCWSTR ParseMachineNameString(LPCWSTR p)
{
    //
    //  Zero length machine name is illeagal
    //  don't fall off the string (p++)
    //
    if(*p == PN_DELIMITER_C)
        return 0;

    if((p = FindPathNameDelimiter(p)) == 0)
        return 0;

    return (p + 1);
}


//---------------------------------------------------------
//
//  Check if this is an expandable machine path. i.e., ".\\"
//
inline BOOL IsExpandableMachinePath(LPCWSTR p)
{
    return ((p[0] == PN_LOCAL_MACHINE_C) && (p[1] == PN_DELIMITER_C));
}

//---------------------------------------------------------
//
//  Optionally expand a path name with local machine name.
//  N.B. expansion is done if needed.
//  return pointer to new/old string
//
static LPCWSTR ExpandPathName(LPCWSTR pStart, ULONG offset, LPWSTR* ppStringToFree)
{
    if((ppStringToFree == 0) || !IsExpandableMachinePath(&pStart[offset]))
        return pStart;

    int len = wcslen(pStart);
    int iHostNameLen = wcslen (gMachine->lpszHostName);

    LPWSTR pCopy = (WCHAR *)g_funcAlloc (sizeof(WCHAR) * (len + iHostNameLen + 1 - 1), g_pvAllocData);

    //
    //  copy prefix, till offset '.'
    //
    memcpy(
        pCopy,
        pStart,
        offset * sizeof(WCHAR)
        );

    //
    //  copy computer name to offset
    //

    memcpy(
        pCopy + offset,
        gMachine->lpszHostName,
        iHostNameLen * sizeof(WCHAR)
        );

    //
    //  copy rest of string not including dot '.'
    //
    memcpy(
        pCopy + offset + iHostNameLen,
        pStart + offset + 1,                        // skip dot
        (len - offset - 1 + 1) * sizeof(WCHAR)      // skip dot, include '\0'
        );

    *ppStringToFree = pCopy;
    return pCopy;
}


//---------------------------------------------------------
//
//  Parse OS direct format string. (check validity of path
//  name and optionally expand it)
//  ppDirectFormat - expended direct format string. (in out)
//  ppStringToFree - return string to free if needed.
//  Return next char to parse on success, 0 on failure.
//
static LPCWSTR ParseDirectOSString(LPCWSTR p, LPCWSTR* ppDirectFormat, LPWSTR* ppStringToFree)
{
    LPCWSTR pMachineName = p;
    LPCWSTR pStringToCopy = *ppDirectFormat;

    if((p = ParseMachineNameString(p)) == 0)
        return 0;

    BOOL fPrivate;
    if((p = ParseQueueNameString(p, &fPrivate)) == 0)
        return 0;


    *ppDirectFormat = ExpandPathName(pStringToCopy, (pMachineName - pStringToCopy), ppStringToFree);

    return p;
}


//---------------------------------------------------------
//
//  Parse net (tcp/spx) address part of direct format string.
//  Return next char to parse on success, 0 on failure.
//
static LPCWSTR ParseNetAddressString(LPCWSTR p)
{
    //
    //  Zero length address string is illeagal
    //  don't fall off the string (p++)
    //
    if(*p == PN_DELIMITER_C)
        return 0;

    if((p = FindPathNameDelimiter(p)) == 0)
        return 0;

    return (p + 1);
}

//---------------------------------------------------------
//
//  Parse HTTP / HTTPS direct format string.
//  Return next char to parse on success, 0 on failure.
//
LPCWSTR
ParseDirectHttpString(
    LPCWSTR p,
    LPCWSTR* ppDirectFormat, 
    LPWSTR* ppStringToFree
    )
{
    size_t MachineNameOffset = p - *ppDirectFormat;

    //
    // Check host name is available
    //
    if(wcschr(FN_HTTP_SEPERATORS, *p) != 0)
        return NULL;

    for(; *p != L'\0' && *p != FN_SUFFIX_DELIMITER_C && *p != FN_MQF_SEPARATOR_C; p++)     {
        NULL;
    }

    *ppDirectFormat = ExpandPathName(*ppDirectFormat, MachineNameOffset, ppStringToFree);
    return p;
}



//---------------------------------------------------------
//
//  Parse net (tcp/spx) direct format string. (check validity of queue name)
//  Return next char to parse on success, 0 on failure.
//
static LPCWSTR ParseDirectNetString(LPCWSTR p)
{
    if((p = ParseNetAddressString(p)) == 0)
        return 0;

    BOOL fPrivate;
    if((p = ParseQueueNameString(p, &fPrivate)) == 0)
        return 0;

    return p;
}


//---------------------------------------------------------
//
//  Parse direct format string.
//  return expended direct format string.
//  return string to free if needed.
//  Return next char to parse on success, 0 on failure.
//
static LPCWSTR ParseDirectString(LPCWSTR p, LPCWSTR* ppExpandedDirectFormat, LPWSTR* ppStringToFree)
{
    *ppExpandedDirectFormat = p;

    DIRECT_TOKEN_TYPE dtt;
    if((p = ParseDirectTokenString(p, dtt)) == 0)
        return 0;

    switch(dtt)
    {
        case DT_OS:
            p = ParseDirectOSString(p, ppExpandedDirectFormat, ppStringToFree);
            break;
                
        case DT_TCP:
        case DT_SPX:
            p = ParseDirectNetString(p);
            break;

        case DT_HTTP:
        case DT_HTTPS:
            p = ParseDirectHttpString(p, ppExpandedDirectFormat, ppStringToFree);
            break;

        default:
            ASSERT(0);
    }

    if (p) {
        SVSUTIL_ASSERT(*p != FN_SUFFIX_DELIMITER_C);
    }

    return p;
}


//---------------------------------------------------------
//
//  Parse format name suffix string.
//  Return next char to parse on success, 0 on failure.
//
static LPCWSTR ParseSuffixString(LPCWSTR p, QUEUE_SUFFIX_TYPE& qst)
{
    const int unique = 5;
    //---------------01234v----------------------
    ASSERT(L'N' == FN_JOURNAL_SUFFIX    [unique]);
    ASSERT(L'L' == FN_DEADLETTER_SUFFIX [unique]);
    ASSERT(L'X' == FN_DEADXACT_SUFFIX   [unique]);
    ASSERT(L'O' == FN_XACTONLY_SUFFIX   [unique]);
    //---------------01234^----------------------

    //
    //  we already know that first character is ";"
    //
    ASSERT(*p == FN_SUFFIX_DELIMITER_C);

    //
    //  accelarate token recognition by checking 6th character
    //
    switch(towupper(p[unique]))
    {
        // ;jourNal
        case L'N':
            qst = QUEUE_SUFFIX_TYPE_JOURNAL;
            if(_wcsnicmp(p, FN_JOURNAL_SUFFIX, FN_JOURNAL_SUFFIX_LEN) == 0)
                return (p + FN_JOURNAL_SUFFIX_LEN);
            break;

        // ;deadLetter
        case L'L':
            qst = QUEUE_SUFFIX_TYPE_DEADLETTER;
            if(_wcsnicmp(p, FN_DEADLETTER_SUFFIX, FN_DEADLETTER_SUFFIX_LEN) == 0)
                return (p + FN_DEADLETTER_SUFFIX_LEN);
            break;

        // ;deadXact
        case L'X':
            qst = QUEUE_SUFFIX_TYPE_DEADXACT;
            if(_wcsnicmp(p, FN_DEADXACT_SUFFIX, FN_DEADXACT_SUFFIX_LEN) == 0)
                return (p + FN_DEADXACT_SUFFIX_LEN);
            break;

        // ;xactOnly
        case L'O':
            qst = QUEUE_SUFFIX_TYPE_XACTONLY;
            if(_wcsnicmp(p, FN_XACTONLY_SUFFIX, FN_XACTONLY_SUFFIX_LEN) == 0)
                return (p + FN_XACTONLY_SUFFIX_LEN);
            break;
    }

    return 0;
}


//---------------------------------------------------------
//
//  Function:
//      RTpFormatNameToQueueFormat
//
//  Description:
//      Convert a format name string to a QUEUE_FORMAT union.
//
//---------------------------------------------------------
static BOOL
RTpFormatNameToQueueFormat(
    LPCWSTR pfn,            // pointer to format name string
    QUEUE_FORMAT* pqf,      // pointer to QUEUE_FORMAT
    LPWSTR* ppStringToFree  // pointer to allocated string need to free at end of use
    )                       // if null, format name will not get expanded
{
    LPCWSTR p = pfn;
    QUEUE_FORMAT_TYPE qft;

    if((p = ParsePrefixString(p, qft)) == 0)
        return FALSE;

    p = skip_ws(p);

    if(*p++ != FN_EQUAL_SIGN_C)
        return FALSE;

    p = skip_ws(p);

    GUID guid;
    switch(qft)
    {
        //
        //  "PUBLIC=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\0"
        //
        case QUEUE_FORMAT_TYPE_PUBLIC:
            if((p = scutil_ParseGuidString(p, &guid)) == 0)
                return FALSE;

            pqf->PublicID(guid);
            break;

        //
        //  "PRIVATE=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\\xxxxxxxx\0"
        //
        case QUEUE_FORMAT_TYPE_PRIVATE:
            if((p = scutil_ParseGuidString(p, &guid)) == 0)
                return FALSE;

            p = skip_ws(p);

            if(*p++ != FN_PRIVATE_SEPERATOR_C)
                return FALSE;

            p = skip_ws(p);

            ULONG uniquifier;
            if((p = ParsePrivateIDString(p, &uniquifier)) == 0)
                return FALSE;

            pqf->PrivateID(guid, uniquifier);
            break;

        //
        //  "DIRECT=OS:bla-bla\0"
        //
        case QUEUE_FORMAT_TYPE_DIRECT:
            LPCWSTR pExpandedDirectFormat;
            if((p = ParseDirectString(p, &pExpandedDirectFormat, ppStringToFree)) == 0)
                return FALSE;

            pqf->DirectID(const_cast<LPWSTR>(pExpandedDirectFormat));
            break;

        //
        //  "MACHINE=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\0"
        //
        case QUEUE_FORMAT_TYPE_MACHINE:
           if((p = scutil_ParseGuidString(p, &guid)) == 0)
                return FALSE;

            pqf->MachineID(guid);
            break;

        //
        //  "CONNECTOR=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\0"
        //
        case QUEUE_FORMAT_TYPE_CONNECTOR:
           if((p = scutil_ParseGuidString(p, &guid)) == 0)
                return FALSE;

            pqf->ConnectorID(guid);
            break;

        default:
            ASSERT(0);
    }

    p = skip_ws(p);

    //
    //  We're at end of string, return now.
    //  N.B. Machine format name *must* have a suffix
    //
    if(*p == L'\0')
        return (qft != QUEUE_FORMAT_TYPE_MACHINE);

    if(*p != FN_SUFFIX_DELIMITER_C)
        return FALSE;

    QUEUE_SUFFIX_TYPE qst;
    if((p = ParseSuffixString(p, qst)) == 0)
        return FALSE;

    p = skip_ws(p);

    //
    //  Only white space padding is allowed.
    //
    if(*p != L'\0')
        return FALSE;

    pqf->Suffix(qst);

    return pqf->Leagal();
}

int scutil_StringToQF (QUEUE_FORMAT *pqf, WCHAR *lpszName) {
    return RTpFormatNameToQueueFormat (lpszName, pqf, NULL);
}

int scutil_GetLanNum (void) {
    char name[MSMQ_SC_REGBUFFER];

    if (gethostname (name, sizeof(name)))
        return 0;

    HOSTENT *phe = gethostbyname (name);
    if (NULL == phe) {
        scerror_DebugOutM (VERBOSE_MASK_FATAL, (L"gethostbyname(%S) fails.  WSAGetLastError returns 0x%08x\n",name,WSAGetLastError()));
        SVSUTIL_ASSERT(0);
        return 0;
    }
    
    if ((phe->h_addrtype != AF_INET) || (phe->h_length != sizeof (IN_ADDR)) || (*phe->h_addr_list == NULL))
        return 0;

    int iRes = 0;

    IN_ADDR **ppia = (IN_ADDR **)phe->h_addr_list;

    while (*ppia != NULL) {
        if (ntohl((**ppia).S_un.S_addr) != INADDR_LOOPBACK)
            ++iRes;

        ++ppia;
    }

    return iRes;
}

int scutil_IsLocalTCP (WCHAR *szHostName) {
    static int iTcpAddresses = 0;
    static unsigned long aulTcpAddresses[MSMQ_SC_MAX_IP_INTERFACES];

    if (! szHostName) {
        iTcpAddresses = 0;

        char name[MSMQ_SC_REGBUFFER];

        if (gethostname (name, sizeof(name)))
            return 0;

        HOSTENT *phe = gethostbyname (name);
        if (NULL == phe) {
            scerror_DebugOutM (VERBOSE_MASK_FATAL, (L"gethostbyname(%S) fails.  WSAGetLastError returns 0x%08x\n",name,WSAGetLastError()));
            SVSUTIL_ASSERT(0);
            return 0;
        }
    
        if ((phe->h_addrtype != AF_INET) || (phe->h_length != sizeof (IN_ADDR)) || (*phe->h_addr_list == NULL))
            return FALSE;

        IN_ADDR **ppia = (IN_ADDR **)phe->h_addr_list;

        while ((*ppia != NULL) && (iTcpAddresses < MSMQ_SC_MAX_IP_INTERFACES)) {
            unsigned long ulInterfaceAddress = (**ppia).S_un.S_addr;
            if (ulInterfaceAddress != INADDR_LOOPBACK)
                aulTcpAddresses[iTcpAddresses++] = ulInterfaceAddress;

            ++ppia;
        }

        return iTcpAddresses;
    }

    char szaddr[MSMQ_SC_IPBUFFER];

    if (! WideCharToMultiByte (CP_ACP, 0, szHostName, -1, szaddr, sizeof(szaddr), NULL, NULL)) 
        return FALSE;

    unsigned long ul_addr = inet_addr (szaddr);

    if (ul_addr == INADDR_NONE)
        return FALSE;

    for (int i = 0 ; i < iTcpAddresses ; ++i) {
        if (ul_addr == aulTcpAddresses[i])
            return TRUE;
    }

    return FALSE;
}

WCHAR* scutil_StrStrWI(const WCHAR * str1, const WCHAR * str2) {
    WCHAR *cp = (WCHAR *) str1;
    WCHAR *s1, *s2;
    while (*cp) {
        s1 = cp;
        s2 = (WCHAR *) str2;
        while ( *s1 && *s2 && (towlower(*s1) == towlower(*s2)) )
            s1++, s2++;
        if (!*s2)
            return(cp);
        cp++;
    }
    return(NULL);
}

void scutil_ReplaceBackSlashesWithSlashes(WCHAR *a_lpszName) {
    for (WCHAR *p = a_lpszName; *p; p++) {
        if (*p == '\\')
            *p = '/';
    }
}

//
// CRemoteAllocMgr implementation
// Handles cases when we need to remotely alloc / dealloc memory in the 
// caller process.  Note this MUST be done on the PSL thread, and not 
// on a servicesd.exe worker.
//
//

// Destroys a remote heap.
void DeleteRemoteHeapInfo(void *pvData) {
    SVSUTIL_ASSERT((glServiceState == SERVICE_STATE_OFF) || gMem->IsLocked());
    REMOTE_HEAP_INFO *pRemoteInfo = (REMOTE_HEAP_INFO*)pvData;
    if (pRemoteInfo->hHeap)
        HeapDestroy(pRemoteInfo->hHeap);
}

// Checks current caller process and determines if heap is associated with it.
BOOL DoesHeapInfoBelongToCaller(void *pvContext, void *pvData) {
    SVSUTIL_ASSERT(gMem->IsLocked());
    REMOTE_HEAP_INFO *pRemoteInfo = (REMOTE_HEAP_INFO*)pvContext;

    // Passed in pvData, via SVSLinkManager filter mechanism, is the
    // result of GetCallerVMProcessId().  Don't call it in each call to
    // this fcn to avoid extra PSL calls.
    return (pRemoteInfo->hProcess == (HANDLE)pvData);
}

// Finds heap caller process owns, and dereferences its usage count, deleting once 0
BOOL DeRefHeapOnProcessDeInit(void *pvContext, void *pvData) {
    SVSUTIL_ASSERT(gMem->IsLocked());
    REMOTE_HEAP_INFO *pRemoteInfo = (REMOTE_HEAP_INFO*)pvContext;

    // pvData is result of GetCallerVMProcessId() by caller
    if (pRemoteInfo->hProcess != (HANDLE)pvData)
        return FALSE;

    pRemoteInfo->dwRefs--;
    if (pRemoteInfo->dwRefs == 0)
        return TRUE; // indicate to SVSLinkManager delete this node now

    return FALSE; // don't delete this node
}

// Creates a new entry in m_remoteHeaps for process tying into MSMQ (if it wasn't present already)
BOOL CRemoteAllocMgr::InitForProcess(void) {
    if (IsCallerProcSameAsCurrent())
        return TRUE;

    SVSUTIL_ASSERT(gMem->IsLocked());
    REMOTE_HEAP_INFO *pRemoteInfo = FindRemoteHeapOfCaller();

    if (pRemoteInfo) {
        // Process already has a remote heap structure allocated; just increment ref count.
        SVSUTIL_ASSERT(pRemoteInfo->dwRefs >= 1);
        pRemoteInfo->dwRefs++;
        return TRUE;
    }
    
    pRemoteInfo = (REMOTE_HEAP_INFO *)m_remoteHeaps.AllocEntry();
    if (! pRemoteInfo)
        return FALSE;

    pRemoteInfo->dwRefs   = 1;
    pRemoteInfo->hProcess = (HANDLE)GetCallerVMProcessId();
    pRemoteInfo->hHeap    = CeRemoteHeapCreate((DWORD)pRemoteInfo->hProcess,0);
    if (!pRemoteInfo->hHeap) {
        scerror_DebugOutM(VERBOSE_MASK_FATAL,(L"MSMQ: CeRemoteHeapCreate(callerVM=0x%08x) fails, GLE = 0x%08x\r\n",pRemoteInfo->hProcess,GetLastError()));
        m_remoteHeaps.RemoveEntry(pRemoteInfo);
        return FALSE;
    }

    return TRUE;
}

// Destroys associated entry in m_remoteHeaps (if ref count is at 0)
BOOL CRemoteAllocMgr::DeInitForProcess(void) {
    SVSUTIL_ASSERT(gMem->IsLocked());
    m_remoteHeaps.RemoveEntriesFiltered(DeRefHeapOnProcessDeInit, (void*)GetCallerVMProcessId());
    return TRUE;
}

// Duplicates a buffer and translates to appropriate address space
BYTE *CRemoteAllocMgr::DuplicateBufferAndTranslate(const BYTE *pbData, DWORD cbData) {
    SVSUTIL_ASSERT(gMem->IsLocked());

    BYTE *pbCopy;
    if (IsCallerProcSameAsCurrent()) {
        pbCopy = (BYTE*)LocalAlloc(0,cbData);
        if (! pbCopy)
            return NULL;

        memcpy(pbCopy,pbData,cbData);
        return pbCopy;
    }

    REMOTE_HEAP_INFO *pRemoteInfo = FindRemoteHeapOfCaller();
    if (! pRemoteInfo)
        return NULL;

    pbCopy = (BYTE*)HeapAlloc(pRemoteInfo->hHeap,0,cbData);
    if (! pbCopy)
        return NULL;

    memcpy(pbCopy,pbData,cbData);
    return (BYTE*)CeRemoteHeapTranslatePointer(pRemoteInfo->hHeap,0,pbCopy);

};

// Duplicates a string and translates to appropriate address space
WCHAR *CRemoteAllocMgr::DuplicateStringAndTranslate(const WCHAR *szData) {
    return (WCHAR*)DuplicateBufferAndTranslate((BYTE*)szData,sizeof(WCHAR)*(wcslen(szData)+1));
}

// Allocates data on the caller's heap.
BYTE *CRemoteAllocMgr::AllocOnCallerHeap(DWORD dwData) {
    SVSUTIL_ASSERT(gMem->IsLocked());

    if (IsCallerProcSameAsCurrent())
        return (BYTE*)LocalAlloc(0,dwData);

    REMOTE_HEAP_INFO *pRemoteInfo = FindRemoteHeapOfCaller();
    if (! pRemoteInfo)
        return NULL;

    return (BYTE*)HeapAlloc(pRemoteInfo->hHeap,0,dwData);
}

// Translates the data to caller process address space
BYTE *CRemoteAllocMgr::TranslateData(BYTE *pbData) {
    if (IsCallerProcSameAsCurrent())
        return pbData;

    SVSUTIL_ASSERT(gMem->IsLocked());
    REMOTE_HEAP_INFO *pRemoteInfo = FindRemoteHeapOfCaller();
    if (! pRemoteInfo)
        return NULL;

    return (BYTE*)CeRemoteHeapTranslatePointer(pRemoteInfo->hHeap,0,pbData);
}

// Frees data allocated on remote heap (HeapFree wrapper)
void CRemoteAllocMgr::FreeData(BYTE *pbData) {
    if (IsCallerProcSameAsCurrent()) {
        LocalFree(pbData);
        return;
    }

    SVSUTIL_ASSERT(gMem->IsLocked());
    REMOTE_HEAP_INFO *pRemoteInfo = FindRemoteHeapOfCaller();
    if (! pRemoteInfo)
        return;

    HeapFree(pRemoteInfo->hHeap,0,pbData);
}

