2025-11-28 00:35:46 +09:00

1178 lines
34 KiB
C++

//////////////////////////////////////////////////////////////////////////////
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//////////////////////////////////////////////////////////////////////////////
#include <windows.h>
#include <stdlib.h>
#include <strsafe.h>
#include <string.h>
#include <wsdapi.h>
#include <time.h>
#include "TargetService.h"
#include "Common.h"
// Generates an EndpointReference in the form of a urn:uuid.
// The caller should call delete[] on the EndpointReference.
_Success_( return == S_OK )
static HRESULT _GenerateEndpointReference
( _Outptr_ LPWSTR *epr
);
// Generates an XAddrs list that contains an XAddrs as
// described below. The caller shall call WSDFreeLinkedMemory
// on the returned WSD_URI_LIST.
//
// For the purpose of this demo, a mechanism for providing an
// XAddrs was not provided by WSDiscoveryService.exe, and hence
// one will be generated on your behalf. The XAddrs that is
// being generated will be dependent on what EndpointReference
// was provided. By default, if no EndpointReference was
// provided at the time when WSDiscoveryService.exe is created,
// a logical address will be automatically generated and used
// for this target service.
//
// A logical address is any address that begins with urn:uuid:
// or uuid:, followed by a 36-character UUID.
// A physical address is any addresses that begins with http://,
// https:// or uri:. Anything else is treated as invalid.
//
// If a logical address is provided (or generated), then the
// XAddrs list will contain a generated XAddrs of the following
// format:
// http://hostname.sdk/(UUID of the logical address)
// E.g.: http://prn-example.sdk/f452f1ae-fbb4-11de-a6bb-00cc30bfc300
//
// If a physical address is provided, then the physical address
// is also used as an XAddrs and will be included in the XAddrs list.
//
// Note: This generated XAddrs is for demo purposes only.
// The developer should ensure that the XAddrs are valid
// network resolvable addresses. An example of such an
// address may look like http://prn-example/PRN42/b42-1668-a
// (Section 1.3 of the WS-Discovery spec). The developer
// should further ensure that the clients understand
// the XAddrs and know how to contact the target service.
_Success_(return == S_OK)
static HRESULT _GenerateXAddrsList
( _In_ LPWSTR epr
, _Outptr_ WSD_URI_LIST **xAddrsList
);
_Success_( return == S_OK )
HRESULT CreateTargetService
( _In_opt_ LPCWSTR id
, _In_opt_ const WSD_URI_LIST *scopesList
, _Outptr_ CTargetService **targetService
)
{
HRESULT hr = S_OK;
CTargetService *tempService = NULL;
if ( NULL == targetService )
{
hr = E_POINTER;
}
else
{
*targetService = NULL;
}
if ( S_OK == hr )
{
tempService = new CTargetService();
if ( NULL == tempService )
{
hr = E_OUTOFMEMORY;
}
}
if ( S_OK == hr )
{
hr = tempService->Init( id, scopesList );
}
if ( S_OK == hr )
{
// outside pointer now owns the temp service
*targetService = tempService;
tempService = NULL;
}
if ( NULL != tempService )
{
tempService->Release();
tempService = NULL;
}
return hr;
}
CTargetService::CTargetService()
: m_epr( NULL )
, m_typesList( NULL ) // <-- always NULL for the purpose of this sample
, m_scopesList( NULL )
, m_xAddrsList( NULL )
, m_publisher( NULL )
, m_instanceId( 0 )
, m_messageNum( 0 )
, m_isActive( FALSE )
, m_isSuspended( TRUE ) // suspend by default - will be waken up when sending Hello
, m_cRef( 1 )
{
InitializeCriticalSection( &m_msgSeqCriticalSection );
}
CTargetService::~CTargetService()
{
// Terminate the service if it is still running.
// The Terminate method releases the notification sinks
// if they are still being registered.
//
// No need to check whether it is active or not
// since the Terminate method will check this.
(void)Terminate();
if ( NULL != m_publisher )
{
m_publisher->Release();
m_publisher = NULL;
}
if ( NULL != m_epr )
{
delete[] m_epr;
m_epr = NULL;
}
if ( NULL != m_typesList )
{
WSDFreeLinkedMemory( m_typesList );
m_typesList = NULL;
}
if ( NULL != m_scopesList )
{
WSDFreeLinkedMemory( m_scopesList );
m_scopesList = NULL;
}
if ( NULL != m_xAddrsList )
{
WSDFreeLinkedMemory( m_xAddrsList );
m_xAddrsList = NULL;
}
m_cRef = 0;
m_instanceId = 0;
m_messageNum = 0;
m_isActive = FALSE;
m_isSuspended = TRUE;
DeleteCriticalSection( &m_msgSeqCriticalSection );
}
HRESULT STDMETHODCALLTYPE CTargetService::Init
( _In_opt_ LPCWSTR id
, _In_opt_ const WSD_URI_LIST *scopesList
)
{
HRESULT hr = S_OK;
IWSDiscoveryPublisher *tempPublisher = NULL;
LPWSTR tempEpr = NULL;
WSD_URI_LIST *tempUriList = NULL;
WSD_URI_LIST *tempXAddrsList = NULL;
if ( m_isActive || NULL != m_publisher )
{
// The target service has previously been initialized,
// or the previous initialization has failed.
hr = E_ABORT;
}
if ( S_OK == hr )
{
// Create the discovery publisher with no context.
// The discovery publisher is responsible for handling
// all WS-Discovery related messages, including sending
// and receiving operations.
//
// Note: You may supply your own XML context for this
// target service by replacing NULL with your XML context.
hr = WSDCreateDiscoveryPublisher( NULL, &tempPublisher );
}
if ( S_OK == hr )
{
if ( NULL == id )
{
// no EndpointReference specified
// generate one
hr = _GenerateEndpointReference( &tempEpr );
}
else
{
// an EndpointReference has been specified
// deep copy it
hr = DeepCopyString( id, &tempEpr );
}
}
if ( S_OK == hr )
{
// Generate XAddrsList
// See above for comment on the usages of XAddrs.
hr = _GenerateXAddrsList( tempEpr, &tempXAddrsList );
}
if ( S_OK == hr && NULL != scopesList )
{
// scopes list has been specified - deep copy it
hr = DeepCopyWsdUriList( scopesList, &tempUriList );
}
if ( S_OK == hr )
{
// Register a custom scope matching rule. All custom
// scope matching rules should implement IWSDScopeMatchingRule
// and have them registered using this method. If you have no
// custom rules to use, this step is optional.
hr = tempPublisher->RegisterScopeMatchingRule(
static_cast<IWSDScopeMatchingRule *>( this )
);
}
if ( S_OK == hr )
{
// Use the current time as the InstanceId.
// This returns the time in seconds.
// Note that the maximum value allowed for
// the InstanceId is UINT_MAX, which is
// 0xFFFF FFFF, 4 294 967 295.
// Using the current time, this gives you
// a valid InstanceId for about 136 years
// from 1970-01-01 - ample amount of valid
// InstanceId's to use.
//
// We are using the time function here to simplify
// the logic around InstanceId's in this sample.
// Although Appendix I of the WS-Discovery
// Specifications states that using this approach
// is one way of dealing with InstanceId's, it is
// not a good practice in general. If more than
// one target service is created within a second,
// there is a strong chance that the InstanceId's
// of these target services will be the same, and
// hence violate the application sequencing
// requirement of WS-Discovery. A better approach
// in doing this is to have the InstanceId's stored
// in a persistence storage mechanism such as in a
// file or in the registry. This will be up to the
// developer of the target service to implement.
m_instanceId = (ULONGLONG)time( NULL );
// reset message number to 0
m_messageNum = 0;
// let target service own the "temporary variables"
m_publisher = tempPublisher;
m_epr = tempEpr;
m_scopesList = tempUriList;
m_xAddrsList = tempXAddrsList;
tempPublisher = NULL;
tempEpr = NULL;
tempUriList = NULL;
tempXAddrsList = NULL;
// Note that, by default, m_typesList is always NULL in this sample.
// You can have them implemented in this method as appropriate.
//
// We let the target service own the temporary variables now because
// the notification sink, which we are about to register, uses these
// variables when it receives callbacks.
//
// Registering the notification sink starts the publisher
// (it will begin to listen to Probe and Resolve messages).
hr = m_publisher->RegisterNotificationSink(
static_cast<IWSDiscoveryPublisherNotify *>( this )
);
}
if ( S_OK == hr )
{
// If the notification sink registers successfully, then
// this target service officially becomes active. If it fails,
// then this target service is not usable and it must be released.
// A new target service must be created instead. Reinitializing
// a previously failed attempt is not allowed since m_publisher
// will not be NULL.
m_isActive = TRUE;
// Keep the target service in suspended state. It will be awaken
// when a Hello message is sent.
m_isSuspended = TRUE;
}
if ( NULL != tempPublisher )
{
tempPublisher->Release();
tempPublisher = NULL;
}
if ( NULL != tempEpr )
{
delete[] tempEpr;
tempEpr = NULL;
}
if ( NULL != tempUriList )
{
WSDFreeLinkedMemory( tempUriList );
tempUriList = NULL;
}
if ( NULL != tempXAddrsList )
{
WSDFreeLinkedMemory( tempXAddrsList );
tempXAddrsList = NULL;
}
return hr;
}
HRESULT STDMETHODCALLTYPE CTargetService::GetEndpointReference
( _Outptr_ LPCWSTR *epr
)
{
HRESULT hr = S_OK;
if ( NULL == epr )
{
hr = E_POINTER;
}
if ( S_OK == hr )
{
// if Init has not been successfully called,
// (or if the target service has been terminated)
// this will result in NULL (which is OK)
//
// do not return S_FALSE as outside sources may
// only be checking for S_OK
*epr = m_epr;
}
return hr;
}
HRESULT STDMETHODCALLTYPE CTargetService::SendHelloMessage()
{
HRESULT hr = S_OK;
ULONGLONG instanceId = 0;
ULONGLONG messageNum = 0;
if ( !m_isActive )
{
// target service has not been initialized yet
hr = E_ABORT;
}
if ( S_OK == hr )
{
// Get the MessageNumber and InstanceId for
// the next message. This is needed so that the
// target service can conform to the WS-Discovery
// sequencing specifications.
hr = this->GetNextMessageNumber( &messageNum, &instanceId );
}
if ( S_OK == hr )
{
// The Publish method sends the Hello message.
//
// To publish the target service with extended information in the ANY section,
// use PublishEx instead.
//
// Note that a target service may send more than one Hello message to update
// its metadata, type, scope and XAddr information.
hr = m_publisher->Publish(
m_epr, TARGET_SVC_METADATA_VER, instanceId,
messageNum, TARGET_SVC_SESSION_ID, m_typesList, m_scopesList, m_xAddrsList );
}
if ( S_OK == hr )
{
// Once the Hello message is sent, wake up the target service.
m_isSuspended = FALSE;
}
return hr;
}
HRESULT STDMETHODCALLTYPE CTargetService::SendByeMessage()
{
HRESULT hr = S_OK;
ULONGLONG instanceId = 0;
ULONGLONG messageNum = 0;
if ( !m_isActive )
{
// target service has not been initialized yet
hr = E_ABORT;
}
if ( S_OK == hr )
{
// Get the MessageNumber and InstanceId for
// the next message. This is needed so that the
// target service can conform to the WS-Discovery
// sequencing specifications.
hr = this->GetNextMessageNumber( &messageNum, &instanceId );
}
if ( S_OK == hr )
{
// The UnPublish method sends the Bye message.
// If there are extended information needed in the
// ANY section, replace the last argument with that information.
hr = m_publisher->UnPublish(
m_epr, instanceId, messageNum,
TARGET_SVC_SESSION_ID, NULL );
}
if ( S_OK == hr )
{
// Once the target service has successfully send a Bye message,
// suspend the target service.
m_isSuspended = TRUE;
}
if ( S_OK == hr )
{
// reset MessageNumber to 0 and increment InstanceId
EnterCriticalSection( &m_msgSeqCriticalSection );
m_messageNum = 0;
// We cap InstanceId between 0 and MAX_INSTANCE_ID. See
// GetNextMessageNumber for details on this.
m_instanceId = ( m_instanceId + 1 ) % ( MAX_INSTANCE_ID + 1 );
LeaveCriticalSection( &m_msgSeqCriticalSection );
}
return hr;
}
HRESULT STDMETHODCALLTYPE CTargetService::Terminate()
{
HRESULT hr = S_OK;
if ( !m_isActive )
{
// the target service has previously been terminated
hr = E_ABORT;
}
if ( S_OK == hr )
{
// We do not care about the return code. Once terminated,
// it cannot be reinitialized. If the terminate fails,
// the remaining clean up will be done when the target service
// is released.
// Send one final Bye message before we shut down.
// (don't care if this fails)
(void)SendByeMessage();
// unregister notification sink
(void)m_publisher->UnRegisterNotificationSink(
static_cast<IWSDiscoveryPublisherNotify *>( this )
);
// unregister scope matching rule
(void)m_publisher->UnRegisterScopeMatchingRule(
static_cast<IWSDScopeMatchingRule *>( this )
);
}
// Terminate does not clean up internal variables.
// That is done in the destructor when Release is called.
// The terminate method can only be called once.
// Hence, whether it is successful or not, the
// target service will now be deactivated. (If it
// has never been activated before, setting
// m_isActive = FALSE will have no effect.)
//
// Setting m_isActive = FALSE does not mean this target
// service can be reactivated by calling Init. Init
// will check to make sure that m_publisher is NULL
// and m_isActive is FALSE before an initialization
// can be done.
m_isActive = FALSE;
// Also suspend the target service.
m_isSuspended = TRUE;
return hr;
}
HRESULT STDMETHODCALLTYPE CTargetService::ProbeHandler
( _In_ const WSD_SOAP_MESSAGE *soap
, _In_ IWSDMessageParameters *messageParameters
)
{
HRESULT hr = S_OK;
ULONGLONG instanceId = 0;
ULONGLONG messageNum = 0;
if ( !m_isActive )
{
// target service has not been initialized yet
hr = E_ABORT;
}
if ( S_OK == hr && !m_isSuspended )
{
// Only handle this Probe message if a Bye message has not previously
// been sent (i.e. the target service is not suspended). Otherwise,
// ignore this Probe.
if ( S_OK == hr )
{
// Get the MessageNumber and InstanceId for
// the next message. This is needed so that the
// target service can conform to the WS-Discovery
// sequencing specifications.
hr = GetNextMessageNumber( &messageNum, &instanceId );
}
if ( S_OK == hr )
{
// The MatchProbe method performs all probe matching
// routines for the target service. These routines
// include scopes matching, types matching, etc.
// It also sends a ProbeMatches message to the client
// if one should be sent. Custom scopes matching
// is also done by this method since it calls MatchScopes
// method of the target service. The MatchScopes method
// will only be called by the MatchProbe method if there
// is at least one scope attached to the target service,
// i.e. m_scopesList contains at least one scope.
//
// If there are any extended information needed to
// be populated in the ANY section, use MatchProbeEx
// instead.
//
// Note that calling the MatchProbe method is OPTIONAL.
// If you already knew that the Probe does not match,
// you do not need to call MatchProbe for
// additional processing. However, you may always call
// MatchProbe regardless of whether you know the Probe
// will match or not. The MatchProbe method will send
// a ProbeMatches message ONLY if the Probe matches the
// target service.
hr = m_publisher->MatchProbe(
soap, messageParameters, m_epr,
TARGET_SVC_METADATA_VER, instanceId, messageNum,
TARGET_SVC_SESSION_ID, m_typesList, m_scopesList,
m_xAddrsList );
}
}
return hr;
}
HRESULT STDMETHODCALLTYPE CTargetService::ResolveHandler
( _In_ const WSD_SOAP_MESSAGE *soap
, _In_ IWSDMessageParameters *messageParameters
)
{
HRESULT hr = S_OK;
ULONGLONG instanceId = 0;
ULONGLONG messageNum = 0;
if ( !m_isActive )
{
// target service has not been initialized yet
hr = E_ABORT;
}
if ( S_OK == hr && !m_isSuspended )
{
// Only handle this Resolve message if a Bye message has not previously
// been sent (i.e. the target service is not suspended). Otherwise,
// ignore this Probe.
if ( S_OK == hr )
{
// Get the MessageNumber and InstanceId for
// the next message. This is needed so that the
// target service can conform to the WS-Discovery
// sequencing specifications.
hr = this->GetNextMessageNumber( &messageNum, &instanceId );
}
if ( S_OK == hr )
{
// This method functions almost the same as MatchProbe,
// except this method handles Resolve matching routines
// rather than Probe matching. See ProbeHandler method
// (above) for additional comments.
hr = m_publisher->MatchResolve(
soap, messageParameters, m_epr,
TARGET_SVC_METADATA_VER, instanceId, messageNum,
TARGET_SVC_SESSION_ID, m_typesList, m_scopesList,
m_xAddrsList );
}
}
return hr;
}
HRESULT STDMETHODCALLTYPE CTargetService::GetScopeRule
( _Outptr_ LPCWSTR *scopeMatchingRule
)
{
HRESULT hr = S_OK;
if ( NULL == scopeMatchingRule )
{
hr = E_POINTER;
}
else
{
*scopeMatchingRule = NULL;
}
if ( S_OK == hr )
{
hr = DeepCopyStringLinked( MATCHBY_CUSTOM, NULL, const_cast<LPWSTR *>( scopeMatchingRule ) );
}
return hr;
}
HRESULT STDMETHODCALLTYPE CTargetService::MatchScopes
( _In_ LPCWSTR scope1 // client
, _In_ LPCWSTR scope2 // target service
, _Out_ BOOL *isMatch
)
{
HRESULT hr = S_OK;
size_t length = 0;
if ( NULL == scope1 || NULL == scope2 )
{
hr = E_INVALIDARG;
}
else if ( NULL == isMatch )
{
hr = E_POINTER;
}
else
{
*isMatch = FALSE;
}
if ( S_OK == hr )
{
// Note that the client scope is scope1,
// target service is scope2. The MatchScopes
// method is called at least once per client scope.
// If a client scope does not match a given target
// sevice scope, it will continue matching with more
// target service scope until one matches the client.
// If none matches the client, then the target service
// doe not match the client's scopes.
//
// Example: (assuming they must be identical to match)
// client: uri:123, uri:456, uri:789
// service: uri:846, uri:123, uri:790
//
// order scope1 scope2 isMatch
// 1 uri:123 uri:846 FALSE
// 2 uri:123 uri:123 TRUE (match with uri:123)
// 3 uri:456 uri:846 FALSE
// 4 uri:456 uri:123 FALSE
// 5 uri:456 uri:790 FALSE (does not match uri:456)
//
// Therefore, the target service does not match the scopes specified
// by the client.
//
// The target service much have all scopes matching the scopes specified
// by the client in the probe message in order for a target service to be
// considered a match.
//
// Also note that this target service must have at least one scope
// attached to it in order for the MatchScopes method to be called.
// For the purpose of this sample, we have an
// usual custom scope matching rule here. Normally,
// a scope matching rule checks for scopes from
// both client and target service. However,
// we only check the scope passed by the client here.
// If the scope of the client ends with one of
// L'1', L'3', L'5', L'7' or L'9', it is a match.
// Otherwise, it is not a match. Again, the
// target service must have at least one scope
// attached to it, regardless of whether we are
// checking against the target service scopes or not.
length = wcslen( scope1 ); // scope 1 = client
if (1 <= length)
{
*isMatch = ( L'1' == scope1[length - 1] ||
L'3' == scope1[length - 1] ||
L'5' == scope1[length - 1] ||
L'7' == scope1[length - 1] ||
L'9' == scope1[length - 1] );
}
else
{
// empty string don't check
*isMatch = FALSE;
}
}
return hr;
}
HRESULT STDMETHODCALLTYPE CTargetService::GetNextMessageNumber
( _Out_ ULONGLONG *nextMessageNum
, _Out_ ULONGLONG *nextInstanceId
)
{
HRESULT hr = S_OK;
EnterCriticalSection( &m_msgSeqCriticalSection );
if ( !m_isActive )
{
// target service has not been initialized yet
hr = E_ABORT;
}
else if ( NULL == nextMessageNum ||
NULL == nextInstanceId )
{
hr = E_POINTER;
}
if ( S_OK == hr )
{
// The m_instanceId and m_messageNum specifies
// the Instance ID and the message number of the next message.
*nextMessageNum = m_messageNum;
*nextInstanceId = m_instanceId;
// Increment the MessageNumber (and InstanceId).
if ( MAX_MESSAGE_NUM <= m_messageNum )
{
// We have reached the maximum MessageNumber
// and we need to increment the InstanceId.
m_messageNum = 0;
// We cap InstanceId between 0 and MAX_INSTANCE_ID. Note that it
// is very difficult for the Instance ID to ever get to the
// maximum value. It practically never happens on properly functioning
// device. You will only get to this kind of situation if your device is
// sending A LOT OF MESSAGES or a lot of BYEs to be able to get here.
// However, you should still properly handle this case as the WS-Discovery
// specifications clearly indicated a maximum value for this.
m_instanceId = ( m_instanceId + 1 ) % ( MAX_INSTANCE_ID + 1 );
}
else
{
// InstanceId does not need to be incremented
m_messageNum++;
}
}
LeaveCriticalSection( &m_msgSeqCriticalSection );
return hr;
}
HRESULT STDMETHODCALLTYPE CTargetService::QueryInterface
( _In_ REFIID riid
, _Outptr_ __RPC__deref_out void __RPC_FAR *__RPC_FAR *object
)
{
HRESULT hr = S_OK;
if ( NULL == object )
{
hr = E_POINTER;
}
if ( S_OK == hr )
{
*object = NULL;
if ( __uuidof(IWSDiscoveryPublisherNotify) == riid )
{
*object = static_cast<IWSDiscoveryPublisherNotify *>( this );
}
else if ( __uuidof(IWSDScopeMatchingRule) == riid )
{
*object = static_cast<IWSDScopeMatchingRule *>( this );
}
else if ( __uuidof(IUnknown) == riid )
{
*object = static_cast<IUnknown*>(
static_cast<IWSDiscoveryPublisherNotify*>(this) );
}
else
{
hr = E_NOINTERFACE;
}
}
if ( S_OK == hr )
{
((LPUNKNOWN) *object)->AddRef();
}
return hr;
}
ULONG STDMETHODCALLTYPE CTargetService::AddRef()
{
ULONG newRefCount = (ULONG)InterlockedIncrement( (LONG *)&m_cRef );
return newRefCount;
}
ULONG STDMETHODCALLTYPE CTargetService::Release()
{
ULONG newRefCount = (ULONG)InterlockedDecrement( (LONG *)&m_cRef );
if ( 0 == newRefCount )
{
delete this;
}
return newRefCount;
}
_Success_( return == S_OK )
HRESULT _GenerateEndpointReference
( _Outptr_ LPWSTR *epr
)
{
HRESULT hr = S_OK;
UUID uuidGenerated = {0};
size_t guidStringLength = 0;
size_t eprStringLength = 0;
LPWSTR guidString = NULL;
LPWSTR tempEpr = NULL;
if ( NULL == epr )
{
hr = E_POINTER;
}
else
{
*epr = NULL;
}
if ( S_OK == hr )
{
// generate a UUID for use with this EndpointReference
hr = UuidCreate(&uuidGenerated);
if ( (HRESULT)RPC_S_OK == hr || (HRESULT)RPC_S_UUID_LOCAL_ONLY == hr )
{
// the UUID has been generated successfully
hr = S_OK;
}
else
{
// failed to generate a UUID
hr = E_FAIL;
}
}
if ( S_OK == hr )
{
// convert the UUID (which is a GUID) as a string
hr = GetGuidString( uuidGenerated, &guidString );
}
if ( S_OK == hr )
{
// length of the GUID string
guidStringLength = wcslen( guidString );
// 123456789 + GUID string len
// urn:uuid:<GUID string>
eprStringLength = 9 + guidStringLength;
// allocate string for the EndpointReference
tempEpr = new WCHAR[eprStringLength + 1]; // + 1 for null char
if ( NULL == tempEpr )
{
hr = E_OUTOFMEMORY;
}
}
if ( S_OK == hr )
{
// formulate the EndpointReference string
hr = StringCchPrintfW( tempEpr, eprStringLength + 1,
L"urn:uuid:%s", guidString);
}
if ( S_OK == hr )
{
// outside pointer now owns the string
*epr = tempEpr;
tempEpr = NULL;
}
if ( NULL != tempEpr )
{
delete[] tempEpr;
tempEpr = NULL;
}
if ( NULL != guidString )
{
delete[] guidString;
guidString = NULL;
}
return hr;
}
_Success_(return == S_OK)
HRESULT _GenerateXAddrsList
( _In_ LPWSTR epr
, _Outptr_ WSD_URI_LIST **xAddrsList
)
{
HRESULT hr = S_OK;
LPWSTR hostName = NULL;
LPWSTR tempXAddrs = NULL;
LPWSTR eprSubstring = NULL; // (soft copy) used to determine if this is a logical or physical address
LPWSTR tempEprUuid = NULL; // (soft copy) used to retrieve the UUID of a logical address
WSD_URI_LIST *tempXAddrsList = NULL;
size_t length = 0;
if ( NULL == epr )
{
hr = E_INVALIDARG;
}
else if ( NULL == xAddrsList )
{
hr = E_POINTER;
}
else
{
*xAddrsList = NULL;
}
if ( S_OK == hr )
{
// create the XAddrs list
tempXAddrsList = (WSD_URI_LIST *)
WSDAllocateLinkedMemory( NULL, sizeof( WSD_URI_LIST ) );
if ( NULL == tempXAddrsList )
{
hr = E_OUTOFMEMORY;
}
else
{
tempXAddrsList->Element = NULL;
tempXAddrsList->Next = NULL;
}
}
if ( S_OK == hr )
{
// Determine whether this EndpointReference begins with http://
eprSubstring = wcsstr( epr, L"http://" );
if ( NULL == eprSubstring )
{
// The EndpointReference does not begin with http:.
// See if it begins with https://.
eprSubstring = wcsstr( epr, L"https://" );
}
if ( NULL == eprSubstring )
{
// The EndpointReference does not begin with https://.
// See if it begins with uri:.
eprSubstring = wcsstr( epr, L"uri:" );
}
if ( NULL != eprSubstring )
{
// This EndpointReference begins with one of http://, https:// or uri:// and we will
// treat this as a physical address, hence XAddrs = EndpointReference.
// Duplicate the EndpointReference and link it to the XAddrs list.
hr = DeepCopyStringLinked( epr, tempXAddrsList, &tempXAddrs );
}
}
if ( S_OK == hr && NULL == eprSubstring )
{
// The EndpointReference is not a physical address.
// Determine whether this string begins with urn:uuid:
eprSubstring = wcsstr( epr, L"urn:uuid:" );
if ( NULL != eprSubstring )
{
// The EndpointReference begins with urn:uuid:.
// 1 2 3 4
// 123456789012345678901234567890123456789012345
// urn:uuid:f452f1ae-fbb4-11de-a6bb-00cc30bfc300
// Do a sanity check to make sure that the string is exactly
// 45 characters long.
if ( wcslen( eprSubstring ) != 45 )
{
hr = E_INVALIDARG;
}
if ( S_OK == hr )
{
// Soft copy the UUID portion of the address.
// 0123456789...
// urn:uuid:f452f1ae-fbb4-11de-a6bb-00cc30bfc300
tempEprUuid = &(eprSubstring[9]);
}
}
}
if ( S_OK == hr && NULL == eprSubstring )
{
// The EndpointReference does not begin with urn:uuid:.
// See if it begins with uuid: instead
eprSubstring = wcsstr( epr, L"uuid:" );
if ( NULL != eprSubstring )
{
// The string begins with uuid:.
// 1 2 3 4
// 12345678901234567890123456789012345678901
// uuid:f452f1ae-fbb4-11de-a6bb-00cc30bfc300
// Do a sanity check to make sure that the string is exactly
// 41 characters long.
if ( wcslen( eprSubstring ) != 41 )
{
hr = E_INVALIDARG;
}
if ( S_OK == hr )
{
// Soft copy the UUID portion of the address.
// 012345...
// uuid:f452f1ae-fbb4-11de-a6bb-00cc30bfc300
tempEprUuid = &(eprSubstring[5]);
}
}
}
if ( S_OK == hr && NULL == eprSubstring )
{
// The string is not a logical nor a physical address.
hr = E_INVALIDARG;
}
if ( S_OK == hr && NULL != tempEprUuid )
{
// The EndpointReference begins with either urn:uuid or uuid:,
// in which we know what the UUID of the EndpointReference is.
// We proceed to generate an XAddrs using the format
// http://hostname.sdk/(UUID of the logical address).
if ( S_OK == hr )
{
// get the host name
hr = GetWideStringHostName( &hostName );
}
if ( S_OK == hr )
{
// allocate memory big enough for the string
//
// 1234567 + wcslen(hostName) + 12345 + wcslen(tempEprUuid) +1 for NULL char
// http:// hostName .sdk/ (UUID of the logical address)
length = sizeof( WCHAR ) * (
7 + // http://
wcslen( hostName ) +
5 + // .sdk/
wcslen( tempEprUuid ) +
1 // NULL char
);
tempXAddrs = (LPWSTR)WSDAllocateLinkedMemory( tempXAddrsList, length );
if ( NULL == tempXAddrs )
{
hr = E_OUTOFMEMORY;
}
}
if ( S_OK == hr )
{
// create the XAddrs string
hr = StringCchPrintfW( tempXAddrs, length, L"http://%s.sdk/%s",
hostName, tempEprUuid );
}
}
if ( S_OK == hr )
{
// the xaddrs list now owns the xaddrs string
tempXAddrsList->Element = tempXAddrs;
tempXAddrs = NULL;
// outside pointer now owns the list
*xAddrsList = tempXAddrsList;
tempXAddrsList = NULL;
}
// soft copy strings - do not delete
eprSubstring = NULL;
tempEprUuid = NULL;
if ( NULL != tempXAddrs )
{
WSDFreeLinkedMemory( tempXAddrs );
tempXAddrs = NULL;
}
if ( NULL != tempXAddrsList )
{
WSDFreeLinkedMemory( tempXAddrsList );
tempXAddrsList = NULL;
}
if ( NULL != hostName )
{
delete[] hostName;
hostName = NULL;
}
return hr;
}