2357 lines
64 KiB
C++
2357 lines
64 KiB
C++
#include "precomp.h"
|
||
DEBUG_FILEZONE(ZONE_T120_MCSNC);
|
||
/*
|
||
* user.cpp
|
||
*
|
||
* Copyright (c) 1993 - 1996 by DataBeam Corporation, Lexington, KY
|
||
*
|
||
* Abstract:
|
||
* This is the implementation file for the User class. Objects of this
|
||
* class represent the attachment between a user application and an MCS
|
||
* domain. It "talks" to the application through an application interface
|
||
* object, which is identified to it as a constructor parameter. Since
|
||
* this class inherits from CommandTarget, it can talk to the domain
|
||
* object using the MCS command language defined therein. The domain
|
||
* object to which it must attach is another constructor parameter.
|
||
*
|
||
* When one of these objects is first created, it must register its
|
||
* presence with both the application interface object above it, and the
|
||
* domain object below it. To register with the application interface
|
||
* object it sends it a registration message through the owner callback.
|
||
* To register with the domain object, it issues an attach user request
|
||
* on behalf of the application that created this attachment.
|
||
*
|
||
* This module contains code to perform three different tasks: accept
|
||
* T.122 requests and responses from the user application and forward them
|
||
* to the domain as MCS commands; accept MCS commands from the domain and
|
||
* forward them to the application as T.122 primitives; and buffer those
|
||
* indications and confirms until the controller allocates a time slice in
|
||
* which to send them.
|
||
*
|
||
* T.122 requests and responses come from the application interface as
|
||
* public member functions whose name is prefixed with "MCS" (for example,
|
||
* "MCSChannelJoinRequest"). After validation, the equivalent MCS command
|
||
* (whose name does NOT begin with "MCS") is sent to the domain object.
|
||
*
|
||
* MCS commands come from the domain object as public member functions that
|
||
* are inherited from CommandTarget and overridden by this class. The
|
||
* names of these functions are NOT prefixed with "MCS". Any MCS commands
|
||
* that do not map to (or can be converted to) T.122 primitives are simply
|
||
* not overridden. The default behavior of these functions ,as defined in
|
||
* the CommandTarget class, is to return an error.
|
||
*
|
||
* Indication and confirm primitives are buffered by objects of this class
|
||
* before being sent to the application. This allows the controller more
|
||
* flexibility in the timing of events in the system. This is done by
|
||
* allocating a structure to hold the information associated with the
|
||
* primitive, and then putting a pointer to that structure into a linked
|
||
* list. When the command comes to flush this message queue, the
|
||
* primitives are sent to the application interface object through the
|
||
* owner callback, and the structures are released.
|
||
*
|
||
* Private Instance Variables:
|
||
* m_pDomain
|
||
* This is a pointer to the domain, to which this user is (or wishes
|
||
* to be) attached.
|
||
* User_ID
|
||
* This is the user ID assigned to this user attachment. This is
|
||
* guaranteed to be unique ONLY within this domain. Note that a value
|
||
* of 0 (zero) indicates that this user is not yet attached to the
|
||
* domain. This is set by a successful attach user confirm, and the
|
||
* user application should wait until that confirm is received before
|
||
* trying to invoke any other MCS services.
|
||
* Merge_In_Progress
|
||
* This is a boolean flag that indicates whether or not the attached
|
||
* Domain object is in the merge state. When in the merge state it
|
||
* is invalid to send it any MCS commands.
|
||
* Deletion_Pending
|
||
* This is a boolean flag that indicates whether or not an internally
|
||
* requested deletion is pending. This is used by the destructor to
|
||
* determine if a deletion was requested by the object itself, or is
|
||
* simply an asynchronous event.
|
||
* Maximum_User_Data_Length
|
||
* This is the maximum amount of user data that can be placed into
|
||
* a single MCS PDU. This number is derived from the arbitrated
|
||
* maximum MCS PDU size (minus enough space for overhead bytes).
|
||
*
|
||
* Private Member Functions:
|
||
* ValidateUserRequest
|
||
* This member function is called each time the user application makes
|
||
* a request. It checks the current state of the system to see if
|
||
* conditions are such that the request can be processed at the
|
||
* current time.
|
||
* PurgeMessageQueue
|
||
* This member function walks through the current message queue,
|
||
* freeing all resources held therein.
|
||
*
|
||
* Caveats:
|
||
* None.
|
||
*
|
||
* Author:
|
||
* James P. Galvin, Jr.
|
||
*/
|
||
|
||
#include "omcscode.h"
|
||
|
||
#define USER_MSG_BASE WM_APP
|
||
|
||
/*
|
||
* bugbug:
|
||
* The following constant is only used to cover a bug in NM 2.0 for backward
|
||
* compatibility purposes. NM 2.0 can not accept MCS data PDUs with more than
|
||
* 4096 bytes of user data. Because of the Max MCS PDU size we negotiate (4128),
|
||
* even in NM 2.0, we should have been able to send 4120 bytes. But NM 2.0 chokes
|
||
* in this case.
|
||
* The constant should eliminated after NM 3.0.
|
||
*/
|
||
#define BER_PROTOCOL_EXTRA_OVERHEAD 24
|
||
|
||
/*
|
||
* This is a global variable that has a pointer to the one MCS coder that
|
||
* is instantiated by the MCS Controller. Most objects know in advance
|
||
* whether they need to use the MCS or the GCC coder, so, they do not need
|
||
* this pointer in their constructors.
|
||
*/
|
||
extern CMCSCoder *g_MCSCoder;
|
||
// The external MCS Controller object
|
||
extern PController g_pMCSController;
|
||
// The global MCS Critical Section
|
||
extern CRITICAL_SECTION g_MCS_Critical_Section;
|
||
// The DLL's HINSTANCE
|
||
extern HINSTANCE g_hDllInst;
|
||
// Class name for windows used by MCS attachments.
|
||
static char s_WindowClassName[CLASS_NAME_LENGTH];
|
||
|
||
|
||
// Initialization of the class's static variables.
|
||
CTimerUserList2* User::s_pTimerUserList2 = NULL;
|
||
HINSTANCE User::s_hInstance = NULL;
|
||
|
||
/*
|
||
* BOOL InitializeClass ()
|
||
*
|
||
* Public, static
|
||
*
|
||
* Functional Description
|
||
*
|
||
* This function initializes the class's static variables. It is
|
||
* called during the MCS Controller's construction.
|
||
*/
|
||
BOOL User::InitializeClass (void)
|
||
{
|
||
BOOL bReturnValue;
|
||
WNDCLASS window_class;
|
||
|
||
DBG_SAVE_FILE_LINE
|
||
s_pTimerUserList2 = new CTimerUserList2();
|
||
bReturnValue = (s_pTimerUserList2 != NULL);
|
||
|
||
if (bReturnValue) {
|
||
// Construct the window class name
|
||
wsprintf (s_WindowClassName, "MCS Window %x %x", GetCurrentProcessId(), GetTickCount());
|
||
|
||
/*
|
||
* Fill out a window class structure in preparation for registering
|
||
* the window with Windows. Note that since this is a hidden
|
||
* window, most of the fields can be set to NULL or 0.
|
||
*/
|
||
ZeroMemory (&window_class, sizeof(WNDCLASS));
|
||
window_class.lpfnWndProc = (WNDPROC) UserWindowProc;
|
||
window_class.hInstance = s_hInstance = g_hDllInst;
|
||
window_class.lpszClassName = s_WindowClassName;
|
||
|
||
/*
|
||
* Register the class with Windows so that we can create a window
|
||
* for use by this portal.
|
||
*/
|
||
if (RegisterClass (&window_class) == 0)
|
||
{
|
||
ERROR_OUT (("InitWindowPortals: window class registration failed. Error: %d", GetLastError()));
|
||
bReturnValue = FALSE;
|
||
}
|
||
}
|
||
else {
|
||
ERROR_OUT(("User::InitializeClass: Failed to allocate timer dictionary."));
|
||
}
|
||
|
||
return bReturnValue;
|
||
}
|
||
|
||
|
||
/*
|
||
* void CleanupClass ()
|
||
*
|
||
* Public, static
|
||
*
|
||
* Functional Description
|
||
*
|
||
* This function cleans up the class's static variables. It is
|
||
* called when the MCS Controller is deleted.
|
||
*/
|
||
void User::CleanupClass (void)
|
||
{
|
||
delete s_pTimerUserList2;
|
||
UnregisterClass (s_WindowClassName, s_hInstance);
|
||
}
|
||
|
||
/*
|
||
* MCSError MCS_AttachRequest ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This API entry point is used to attach to an existing domain. Once
|
||
* attached, a user application can utilize the services of MCS. When
|
||
* a user application is through with MCS, it should detach from the domain
|
||
* by calling MCSDetachUserRequest (see below).
|
||
*/
|
||
MCSError WINAPI MCS_AttachRequest (IMCSSap ** ppIMCSSap,
|
||
DomainSelector domain_selector,
|
||
UINT, // domain_selector_length
|
||
MCSCallBack user_callback,
|
||
PVoid user_defined,
|
||
UINT flags)
|
||
{
|
||
MCSError return_value = MCS_NO_ERROR;
|
||
AttachRequestInfo attach_request_info;
|
||
PUser pUser;
|
||
|
||
TRACE_OUT(("AttachUserRequest: beginning attachment process"));
|
||
ASSERT (user_callback);
|
||
|
||
// Initialize the interface ptr.
|
||
*ppIMCSSap = NULL;
|
||
|
||
/*
|
||
* Pack the attach parameters into a structure since they will not fit
|
||
* into the one parameter we have available in the owner callback.
|
||
*/
|
||
attach_request_info.domain_selector = (GCCConfID *) domain_selector;
|
||
attach_request_info.ppuser = &pUser;
|
||
|
||
/*
|
||
* Enter the critical section which protects global data.
|
||
*/
|
||
EnterCriticalSection (& g_MCS_Critical_Section);
|
||
|
||
if (g_pMCSController != NULL) {
|
||
|
||
/*
|
||
* Send an attach user request message to the controller through its
|
||
* owner callback function.
|
||
*/
|
||
return_value = g_pMCSController->HandleAppletAttachUserRequest(&attach_request_info);
|
||
if (return_value == (ULong) MCS_NO_ERROR)
|
||
{
|
||
// Set the returned interface ptr
|
||
*ppIMCSSap = (IMCSSap *) pUser;
|
||
|
||
/*
|
||
* If the request was accepted, then register
|
||
* the new user attachment. Note that there
|
||
* is still no user ID associated with this
|
||
* attachment, since the attach user confirm
|
||
* has not yet been received.
|
||
*/
|
||
pUser->RegisterUserAttachment (user_callback, user_defined,
|
||
flags);
|
||
}
|
||
}
|
||
else {
|
||
ERROR_OUT(("MCS_AttachRequest: MCS Provider is not initialized."));
|
||
return_value = MCS_NOT_INITIALIZED;
|
||
}
|
||
/*
|
||
* Leave the critical section before returning.
|
||
*/
|
||
LeaveCriticalSection (& g_MCS_Critical_Section);
|
||
|
||
return (return_value);
|
||
}
|
||
|
||
|
||
/*
|
||
* User ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This is the constructor for the user class. It initializes all instance
|
||
* variables (mostly with passed in information). It then registers its
|
||
* presence with the application interface object, so that user requests
|
||
* and responses will get here okay. Finally, it issues an attach user
|
||
* request to the domain to start the attachment process.
|
||
*/
|
||
User::User (PDomain pDomain,
|
||
PMCSError pError)
|
||
:
|
||
CAttachment(USER_ATTACHMENT),
|
||
m_pDomain(pDomain),
|
||
Deletion_Pending (FALSE),
|
||
User_ID (0),
|
||
Merge_In_Progress (FALSE),
|
||
m_DataPktQueue(),
|
||
m_PostMsgPendingQueue(),
|
||
m_DataIndMemoryBuf2(),
|
||
CRefCount(MAKE_STAMP_ID('U','s','e','r'))
|
||
{
|
||
DomainParameters domain_parameters;
|
||
|
||
g_pMCSController->AddRef();
|
||
/*
|
||
* We now need to create the window that the MCS Provider
|
||
* will use to deliver MCS messages to the attachment.
|
||
* These messages are indications and confirms.
|
||
*/
|
||
m_hWnd = CreateWindow (s_WindowClassName,
|
||
NULL,
|
||
WS_POPUP,
|
||
CW_USEDEFAULT,
|
||
CW_USEDEFAULT,
|
||
CW_USEDEFAULT,
|
||
CW_USEDEFAULT,
|
||
NULL,
|
||
NULL,
|
||
g_hDllInst,
|
||
NULL);
|
||
|
||
if (m_hWnd != NULL) {
|
||
/*
|
||
* Call the domain object to find out the current domain parameters.
|
||
* From this, set the maximum user data length appropriately.
|
||
*/
|
||
m_pDomain->GetDomainParameters (&domain_parameters, NULL, NULL);
|
||
Maximum_User_Data_Length = domain_parameters.max_mcspdu_size -
|
||
(MAXIMUM_PROTOCOL_OVERHEAD_MCS +
|
||
BER_PROTOCOL_EXTRA_OVERHEAD);
|
||
TRACE_OUT (("User::User: "
|
||
"maximum user data length = %ld", Maximum_User_Data_Length));
|
||
|
||
/*
|
||
* Use the specified domain parameters to set the type of encoding rules
|
||
* to be used.
|
||
*/
|
||
ASSERT (domain_parameters.protocol_version == PROTOCOL_VERSION_PACKED);
|
||
|
||
/*
|
||
* Send an attach user request to the specified domain.
|
||
*/
|
||
m_pDomain->AttachUserRequest (this);
|
||
*pError = MCS_NO_ERROR;
|
||
}
|
||
else {
|
||
*pError = MCS_ALLOCATION_FAILURE;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* ~User ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
*
|
||
*/
|
||
User::~User ()
|
||
{
|
||
PDataPacket packet;
|
||
while (NULL != (packet = m_PostMsgPendingQueue.Get()))
|
||
{
|
||
packet->Unlock();
|
||
}
|
||
|
||
if (m_hWnd) {
|
||
// Destroy the window; we do not need it anymore
|
||
DestroyWindow (m_hWnd);
|
||
}
|
||
g_pMCSController->Release();
|
||
}
|
||
|
||
/*
|
||
* MCSError GetBuffer ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This function allocates an MCS buffer for a user attachment.
|
||
* Because this function allocates a buffer for the user and a Memory
|
||
* object that immediately precedes the buffer, after the user fills in
|
||
* the buffer with data and gives it to MCS to send, it needs to specify the
|
||
* right flags in the SendData request API.
|
||
*/
|
||
|
||
MCSError User::GetBuffer (UINT size, PVoid *pbuffer)
|
||
{
|
||
|
||
MCSError return_value;
|
||
PMemory memory;
|
||
|
||
EnterCriticalSection (& g_MCS_Critical_Section);
|
||
|
||
/*
|
||
* This request may be a retry from a previous request which
|
||
* returned MCS_TRANSMIT_BUFFER_FULL. If so, delete the associated
|
||
* buffer retry info structure since resource levels will be
|
||
* checked in this function anyway.
|
||
*/
|
||
if (m_BufferRetryInfo != NULL) {
|
||
KillTimer (NULL, m_BufferRetryInfo->timer_id);
|
||
s_pTimerUserList2->Remove(m_BufferRetryInfo->timer_id);
|
||
delete m_BufferRetryInfo;
|
||
m_BufferRetryInfo = NULL;
|
||
|
||
}
|
||
|
||
// Allocate the memory
|
||
DBG_SAVE_FILE_LINE
|
||
memory = AllocateMemory (NULL, size + MAXIMUM_PROTOCOL_OVERHEAD,
|
||
SEND_PRIORITY);
|
||
|
||
LeaveCriticalSection (& g_MCS_Critical_Section);
|
||
|
||
if (NULL != memory) {
|
||
// the allocation succeeded.
|
||
ASSERT ((PUChar) memory + sizeof(Memory) == memory->GetPointer());
|
||
*pbuffer = (PVoid) (memory->GetPointer() + MAXIMUM_PROTOCOL_OVERHEAD);
|
||
return_value = MCS_NO_ERROR;
|
||
}
|
||
else {
|
||
// the allocation failed.
|
||
TRACE_OUT (("User::GetBuffer: Failed to allocate data buffer."));
|
||
CreateRetryTimer (size + MAXIMUM_PROTOCOL_OVERHEAD);
|
||
return_value = MCS_TRANSMIT_BUFFER_FULL;
|
||
}
|
||
return (return_value);
|
||
}
|
||
|
||
/*
|
||
* MCSError FreeBuffer ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
*/
|
||
|
||
void User::FreeBuffer (PVoid buffer_ptr)
|
||
{
|
||
PMemory memory;
|
||
|
||
ASSERT (m_fFreeDataIndBuffer == FALSE);
|
||
|
||
/*
|
||
* Attempt to find the buffer in the m_DataIndDictionary dictionary.
|
||
* This is where irregular data indications go.
|
||
*/
|
||
if (NULL == (memory = m_DataIndMemoryBuf2.Remove(buffer_ptr)))
|
||
{
|
||
memory = GetMemoryObject(buffer_ptr);
|
||
}
|
||
|
||
// Free the memory.
|
||
EnterCriticalSection (& g_MCS_Critical_Section);
|
||
FreeMemory (memory);
|
||
LeaveCriticalSection (& g_MCS_Critical_Section);
|
||
}
|
||
|
||
/*
|
||
* Void CreateRetryTimer
|
||
*
|
||
* Private
|
||
*
|
||
* Functional Description
|
||
* This functions creates a timer in response to a failure to
|
||
* allocate memory for the send data that the user is trying to
|
||
* send. The timer will fire off periodically so that this code
|
||
* will remember to check the memory levels and provide an
|
||
* MCS_TRANSMIT_BUFFER_AVAILABLE_INDICATION to the user.
|
||
*
|
||
* Return Value:
|
||
* None.
|
||
*
|
||
* Side effects:
|
||
* The timer is created.
|
||
*/
|
||
|
||
Void User::CreateRetryTimer (ULong size)
|
||
{
|
||
UINT_PTR timer_id;
|
||
|
||
timer_id = SetTimer (NULL, 0, TIMER_PROCEDURE_TIMEOUT, (TIMERPROC) TimerProc);
|
||
if (timer_id != 0) {
|
||
DBG_SAVE_FILE_LINE
|
||
m_BufferRetryInfo = new BufferRetryInfo;
|
||
|
||
if (m_BufferRetryInfo != NULL) {
|
||
m_BufferRetryInfo->user_data_length = size;
|
||
m_BufferRetryInfo->timer_id = timer_id;
|
||
|
||
s_pTimerUserList2->Append(timer_id, this);
|
||
}
|
||
else {
|
||
ERROR_OUT (("User::CreateRetryTimer: Failed to allocate BufferRetryInfo struct."));
|
||
KillTimer (NULL, timer_id);
|
||
}
|
||
}
|
||
else {
|
||
/*
|
||
* This is a bad error, The notification to the user when buffers
|
||
* are available will be lost. Hopefully, the user will try again
|
||
* later.
|
||
*/
|
||
WARNING_OUT(("User::CreateRetryTimer: Could not SetTimer."));
|
||
}
|
||
}
|
||
|
||
/*
|
||
* MCSError ReleaseInterface ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This function is called when a user wishes to detach from the domain.
|
||
* It kicks off the process of detaching, and seeing that this object
|
||
* is properly deleted.
|
||
*/
|
||
MCSError User::ReleaseInterface ()
|
||
{
|
||
CUidList deletion_list;
|
||
MCSError return_value;
|
||
|
||
EnterCriticalSection (& g_MCS_Critical_Section);
|
||
/*
|
||
* Check to see if there is a merge operation in progress before proceeding
|
||
* with the request.
|
||
*/
|
||
if (Merge_In_Progress == FALSE)
|
||
{
|
||
/*
|
||
* If deletion is not already pending, then it is necessary for us
|
||
* to tell the domain that we are leaving.
|
||
*/
|
||
if (Deletion_Pending == FALSE)
|
||
{
|
||
/*
|
||
* If we are already attached, user ID will not be 0, and we
|
||
* should send a detach user request. If user ID IS 0, then we
|
||
* are not yet attached to the domain, so a disconnect provider
|
||
* ultimatum is used instead.
|
||
*/
|
||
if (User_ID != 0)
|
||
{
|
||
deletion_list.Append(User_ID);
|
||
m_pDomain->DetachUserRequest (this,
|
||
REASON_USER_REQUESTED, &deletion_list);
|
||
User_ID = 0;
|
||
}
|
||
else
|
||
m_pDomain->DisconnectProviderUltimatum (this,
|
||
REASON_USER_REQUESTED);
|
||
|
||
/*
|
||
* Set the flag that will cause the object to be deleted during
|
||
* the next call to FlushMessageQueue.
|
||
*/
|
||
Deletion_Pending = TRUE;
|
||
}
|
||
|
||
/*
|
||
* Empty out the message queue (the application should receive no
|
||
* messages once the attachment has been deleted).
|
||
*/
|
||
PurgeMessageQueue ();
|
||
|
||
// Cleanup timers and retry structures;
|
||
if (m_BufferRetryInfo != NULL) {
|
||
s_pTimerUserList2->Remove(m_BufferRetryInfo->timer_id);
|
||
KillTimer (NULL, m_BufferRetryInfo->timer_id);
|
||
delete m_BufferRetryInfo;
|
||
m_BufferRetryInfo = NULL;
|
||
}
|
||
|
||
return_value = MCS_NO_ERROR;
|
||
|
||
// Release can release the MCS Controller, so, we have to exit the CS now.
|
||
LeaveCriticalSection (& g_MCS_Critical_Section);
|
||
|
||
/*
|
||
* Release this object. Note that the object may be deleted
|
||
* here, so, we should not access any member variables after this
|
||
* call.
|
||
*/
|
||
Release();
|
||
}
|
||
else
|
||
{
|
||
LeaveCriticalSection (& g_MCS_Critical_Section);
|
||
/*
|
||
* This operation could not be processed at this time due to a merge
|
||
* operation in progress at the local provider.
|
||
*/
|
||
WARNING_OUT (("User::ReleaseInterface: "
|
||
"merge in progress"));
|
||
return_value = MCS_DOMAIN_MERGING;
|
||
}
|
||
|
||
return (return_value);
|
||
}
|
||
|
||
#define CHANNEL_JOIN 0
|
||
#define CHANNEL_LEAVE 1
|
||
#define CHANNEL_CONVENE 2
|
||
#define CHANNEL_DISBAND 3
|
||
|
||
/*
|
||
* MCSError ChannelJLCD ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This function is called when the user application wishes to join/leave/convene/disband
|
||
* a channel. If the user is attached to the domain, the request will be
|
||
* repackaged as an MCS command and sent to the domain object.
|
||
*/
|
||
MCSError User::ChannelJLCD (int type, ChannelID channel_id)
|
||
{
|
||
MCSError return_value;
|
||
|
||
EnterCriticalSection (& g_MCS_Critical_Section);
|
||
/*
|
||
* Verify that current conditions are appropriate for a request to be
|
||
* accepted from a user attachment.
|
||
*/
|
||
return_value = ValidateUserRequest ();
|
||
|
||
if (return_value == MCS_NO_ERROR) {
|
||
switch (type) {
|
||
case CHANNEL_JOIN:
|
||
m_pDomain->ChannelJoinRequest (this, User_ID, channel_id);
|
||
break;
|
||
case CHANNEL_LEAVE:
|
||
{
|
||
CChannelIDList deletion_list;
|
||
deletion_list.Append(channel_id);
|
||
m_pDomain->ChannelLeaveRequest (this, &deletion_list);
|
||
}
|
||
break;
|
||
case CHANNEL_CONVENE:
|
||
m_pDomain->ChannelConveneRequest (this, User_ID);
|
||
break;
|
||
case CHANNEL_DISBAND:
|
||
m_pDomain->ChannelDisbandRequest (this, User_ID, channel_id);
|
||
break;
|
||
}
|
||
}
|
||
|
||
LeaveCriticalSection (& g_MCS_Critical_Section);
|
||
|
||
return (return_value);
|
||
}
|
||
|
||
/*
|
||
* MCSError ChannelJoin ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This function is called when the user application wishes to join a
|
||
* channel. If the user is attached to the domain, the request will be
|
||
* repackaged as an MCS command and sent to the domain object.
|
||
*/
|
||
MCSError User::ChannelJoin (ChannelID channel_id)
|
||
{
|
||
return (ChannelJLCD (CHANNEL_JOIN, channel_id));
|
||
}
|
||
|
||
/*
|
||
* MCSError ChannelLeave ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This function is called when the user application wishes to leave a
|
||
* channel. If the user is attached to the domain, the request will be
|
||
* repackaged as an MCS command and sent to the domain object.
|
||
*/
|
||
MCSError User::ChannelLeave (ChannelID channel_id)
|
||
{
|
||
return (ChannelJLCD (CHANNEL_LEAVE, channel_id));
|
||
}
|
||
|
||
/*
|
||
* MCSError ChannelConvene ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This function is called when the user application wishes to convene a
|
||
* private channel. If the user is attached to the domain, the request
|
||
* will be repackaged as an MCS command and sent to the domain object.
|
||
*/
|
||
MCSError User::ChannelConvene ()
|
||
{
|
||
return (ChannelJLCD (CHANNEL_CONVENE, 0));
|
||
}
|
||
|
||
/*
|
||
* MCSError ChannelDisband ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This function is called when the user application wishes to disband a
|
||
* private channel. If the user is attached to the domain, the request
|
||
* will be repackaged as an MCS command and sent to the domain object.
|
||
*/
|
||
MCSError User::ChannelDisband (
|
||
ChannelID channel_id)
|
||
{
|
||
return (ChannelJLCD (CHANNEL_DISBAND, channel_id));
|
||
}
|
||
|
||
/*
|
||
* MCSError ChannelAdmit ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This function is called when the user application wishes to admit more
|
||
* users to a private channel for which it is manager. If the user is
|
||
* attached to the domain, the request will be repackaged as an MCS command
|
||
* and sent to the domain object.
|
||
*/
|
||
MCSError User::ChannelAdmit (
|
||
ChannelID channel_id,
|
||
PUserID user_id_list,
|
||
UINT user_id_count)
|
||
{
|
||
UINT count;
|
||
CUidList local_user_id_list;
|
||
MCSError return_value = MCS_NO_ERROR;
|
||
|
||
/*
|
||
* Verify that the value of each user ID included in the user ID list is
|
||
* a valid value. Otherwise, fail the call.
|
||
*/
|
||
for (count = 0; count < user_id_count; count++)
|
||
{
|
||
if (user_id_list[count] > 1000) {
|
||
// add the UserID into the singly-linked list.
|
||
local_user_id_list.Append(user_id_list[count]);
|
||
}
|
||
else {
|
||
return_value = MCS_INVALID_PARAMETER;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (return_value == MCS_NO_ERROR) {
|
||
|
||
EnterCriticalSection (& g_MCS_Critical_Section);
|
||
|
||
/*
|
||
* Verify that current conditions are appropriate for a request to be
|
||
* accepted from a user attachment.
|
||
*/
|
||
return_value = ValidateUserRequest ();
|
||
|
||
if (return_value == MCS_NO_ERROR)
|
||
{
|
||
m_pDomain->ChannelAdmitRequest (this, User_ID, channel_id,
|
||
&local_user_id_list);
|
||
}
|
||
|
||
LeaveCriticalSection (& g_MCS_Critical_Section);
|
||
}
|
||
|
||
return (return_value);
|
||
}
|
||
|
||
#ifdef USE_CHANNEL_EXPEL_REQUEST
|
||
/*
|
||
* MCSError MCSChannelExpelRequest ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This function is called when the user application wishes to expel
|
||
* users from a private channel for which it is manager. If the user is
|
||
* attached to the domain, the request will be repackaged as an MCS command
|
||
* and sent to the domain object.
|
||
*/
|
||
MCSError User::ChannelExpel (
|
||
ChannelID channel_id,
|
||
PMemory memory,
|
||
UINT user_id_count)
|
||
{
|
||
UINT count;
|
||
CUidList local_user_id_list;
|
||
MCSError return_value;
|
||
PUserID user_id_list = (PUserID) memory->GetPointer();
|
||
|
||
/*
|
||
* Verify that current conditions are appropriate for a request to be
|
||
* accepted from a user attachment.
|
||
*/
|
||
return_value = ValidateUserRequest ();
|
||
|
||
if (return_value == MCS_NO_ERROR)
|
||
{
|
||
/*
|
||
* Repack the user ID list into an S-list before sending it on.
|
||
*/
|
||
for (count=0; count < user_id_count; count++)
|
||
local_user_id_list.append ((DWORD) user_id_list[count]);
|
||
|
||
m_pDomain->ChannelExpelRequest (this, User_ID, channel_id,
|
||
&local_user_id_list);
|
||
}
|
||
|
||
if (return_value != MCS_DOMAIN_MERGING)
|
||
FreeMemory (memory);
|
||
|
||
return (return_value);
|
||
}
|
||
#endif // USE_CHANNEL_EXPEL_REQUEST
|
||
|
||
/*
|
||
* MCSError SendData ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This function is called when the user application wishes to send data
|
||
* on a channel. If the user is attached to the domain, the request will
|
||
* be repackaged as an MCS command and sent to the domain object.
|
||
*
|
||
* Note that this version of the send data request assumes that the user
|
||
* data has not already been segmented. This is the function that
|
||
* performs the segmentation.
|
||
*/
|
||
MCSError User::SendData (DataRequestType request_type,
|
||
ChannelID channel_id,
|
||
Priority priority,
|
||
unsigned char * user_data,
|
||
ULong user_data_length,
|
||
SendDataFlags flags)
|
||
{
|
||
MCSError return_value = MCS_NO_ERROR;
|
||
ULong i, request_count, user_packet_length;
|
||
PDataPacket packet;
|
||
ASN1choice_t choice;
|
||
UINT type;
|
||
PUChar data_ptr = user_data;
|
||
PacketError packet_error;
|
||
Segmentation segmentation;
|
||
PMemory memory;
|
||
PDataPacket *packets;
|
||
|
||
/*
|
||
* Calculate how many different MCS packets are going to be generated.
|
||
* Remember that if the size of the request exceeds the maximum allowed
|
||
* value, we will segment the data into multiple smaller pieces.
|
||
*/
|
||
request_count = ((user_data_length + (Maximum_User_Data_Length - 1)) /
|
||
Maximum_User_Data_Length);
|
||
|
||
/*
|
||
* Allocate the array of PDataPackets, before we get the critical section.
|
||
*/
|
||
if (request_count == 1) {
|
||
packets = &packet;
|
||
packet = NULL;
|
||
}
|
||
else {
|
||
DBG_SAVE_FILE_LINE
|
||
packets = new PDataPacket[request_count];
|
||
if (packets == NULL) {
|
||
ERROR_OUT (("User::SendData: Failed to allocate packet array."));
|
||
return_value = MCS_TRANSMIT_BUFFER_FULL;
|
||
}
|
||
else {
|
||
ZeroMemory ((PVoid) packets, request_count * sizeof(PDataPacket));
|
||
}
|
||
}
|
||
|
||
if (MCS_NO_ERROR == return_value) {
|
||
// Set the choice and type variables for all the DataPackets.
|
||
if (NORMAL_SEND_DATA == request_type) {
|
||
choice = SEND_DATA_REQUEST_CHOSEN;
|
||
type = MCS_SEND_DATA_INDICATION;
|
||
}
|
||
else {
|
||
choice = UNIFORM_SEND_DATA_REQUEST_CHOSEN;
|
||
type = MCS_UNIFORM_SEND_DATA_INDICATION;
|
||
}
|
||
|
||
EnterCriticalSection (& g_MCS_Critical_Section);
|
||
|
||
/*
|
||
* Verify that current conditions are appropriate for a request to be
|
||
* accepted from a user attachment.
|
||
*/
|
||
return_value = ValidateUserRequest ();
|
||
|
||
/*
|
||
* Check to see if there is a merge operation in progress before proceeding
|
||
* with the request.
|
||
*/
|
||
if (MCS_NO_ERROR == return_value) {
|
||
|
||
/*
|
||
* This request may be a retry from a previous request which
|
||
* returned MCS_TRANSMIT_BUFFER_FULL. If so, delete the associated
|
||
* buffer retry info structure since resource levels will be
|
||
* checked in this function anyway.
|
||
*/
|
||
if (m_BufferRetryInfo != NULL) {
|
||
s_pTimerUserList2->Remove(m_BufferRetryInfo->timer_id);
|
||
KillTimer (NULL, m_BufferRetryInfo->timer_id);
|
||
delete m_BufferRetryInfo;
|
||
m_BufferRetryInfo = NULL;
|
||
}
|
||
|
||
/*
|
||
* Depending on the "flags" argument, we either have
|
||
* to allocate the buffer space and copy the data into
|
||
* it, or just create a Memory object for the supplied
|
||
* buffer.
|
||
*/
|
||
if (flags != APP_ALLOCATION) {
|
||
|
||
ASSERT (flags == MCS_ALLOCATION);
|
||
/*
|
||
* The buffer was allocated by MCS, thru an
|
||
* MCSGetBufferRequest call. So, the Memory object
|
||
* must preceed the buffer.
|
||
*/
|
||
memory = GetMemoryObject (user_data);
|
||
ASSERT (SIGNATURE_MATCH(memory, MemorySignature));
|
||
}
|
||
else
|
||
memory = NULL;
|
||
|
||
/*
|
||
* We now attempt to allocate all data packets at once.
|
||
* We need to do that before starting to send them, because
|
||
* the request has to be totally successful or totally fail.
|
||
* We can not succeed in sending a part of the request.
|
||
*/
|
||
for (i = 0; (ULong) i < request_count; i++) {
|
||
// take care of segmentation flags
|
||
if (i == 0)
|
||
// first segment
|
||
segmentation = SEGMENTATION_BEGIN;
|
||
else
|
||
segmentation = 0;
|
||
if (i == request_count - 1) {
|
||
// last segment
|
||
segmentation |= SEGMENTATION_END;
|
||
user_packet_length = user_data_length - (ULong)(data_ptr - user_data);
|
||
}
|
||
else {
|
||
user_packet_length = Maximum_User_Data_Length;
|
||
}
|
||
|
||
// Now, create the new DataPacket.
|
||
DBG_SAVE_FILE_LINE
|
||
packets[i] = new DataPacket (choice, data_ptr, user_packet_length,
|
||
(UINT) channel_id, priority,
|
||
segmentation, (UINT) User_ID,
|
||
flags, memory, &packet_error);
|
||
|
||
// Make sure the allocation succeeded
|
||
if ((packets[i] == NULL) || (packet_error != PACKET_NO_ERROR)) {
|
||
/*
|
||
* The allocation of the packet failed. We must therefore
|
||
* return a failure to the user application.
|
||
*/
|
||
WARNING_OUT (("User::SendData: data packet allocation failed"));
|
||
return_value = MCS_TRANSMIT_BUFFER_FULL;
|
||
break;
|
||
}
|
||
|
||
// Adjust the user data ptr
|
||
data_ptr += Maximum_User_Data_Length;
|
||
}
|
||
|
||
if (return_value == MCS_NO_ERROR) {
|
||
// We now can send the data.
|
||
// Forward all the data packets to the appropriate places.
|
||
for (i = 0; i < request_count; i++) {
|
||
/*
|
||
* Send the successfully created packet to the domain
|
||
* for processing.
|
||
*/
|
||
m_pDomain->SendDataRequest (this, (UINT) type, packets[i]);
|
||
|
||
/*
|
||
* Enable the packet to free itself. Note that it will not
|
||
* actually do so until everyone that is using it is through
|
||
* with it. Also, if nobody has locked it so far,
|
||
* it will be deleted.
|
||
*/
|
||
packets[i]->Unlock ();
|
||
}
|
||
}
|
||
else {
|
||
// some packet allocation failed
|
||
for (i = 0; i < request_count; i++)
|
||
delete packets[i];
|
||
}
|
||
}
|
||
if (request_count > 1)
|
||
delete [] packets;
|
||
}
|
||
|
||
if (MCS_TRANSMIT_BUFFER_FULL == return_value) {
|
||
CreateRetryTimer(user_data_length + request_count * MAXIMUM_PROTOCOL_OVERHEAD);
|
||
}
|
||
else if (MCS_NO_ERROR == return_value) {
|
||
FreeMemory (memory);
|
||
}
|
||
|
||
LeaveCriticalSection (& g_MCS_Critical_Section);
|
||
return (return_value);
|
||
}
|
||
|
||
#define GRAB 0
|
||
#define INHIBIT 1
|
||
#define PLEASE 2
|
||
#define RELEASE 3
|
||
#define TEST 4
|
||
|
||
/*
|
||
* MCSError TokenGIRPT ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This function is called when the user application wishes to grab/inhibit/request/release/test
|
||
* a token. If the user is attached to the domain, the request will
|
||
* be repackaged as an MCS command and sent to the domain object.
|
||
*/
|
||
MCSError User::TokenGIRPT (int type, TokenID token_id)
|
||
{
|
||
MCSError return_value;
|
||
|
||
EnterCriticalSection (& g_MCS_Critical_Section);
|
||
/*
|
||
* Verify that current conditions are appropriate for a request to be
|
||
* accepted from a user attachment.
|
||
*/
|
||
return_value = ValidateUserRequest ();
|
||
|
||
if (return_value == MCS_NO_ERROR)
|
||
{
|
||
switch (type) {
|
||
case GRAB:
|
||
m_pDomain->TokenGrabRequest (this, User_ID, token_id);
|
||
break;
|
||
case INHIBIT:
|
||
m_pDomain->TokenInhibitRequest (this, User_ID, token_id);
|
||
break;
|
||
case PLEASE:
|
||
m_pDomain->TokenPleaseRequest (this, User_ID, token_id);
|
||
break;
|
||
case RELEASE:
|
||
m_pDomain->TokenReleaseRequest (this, User_ID, token_id);
|
||
break;
|
||
case TEST:
|
||
m_pDomain->TokenTestRequest (this, User_ID, token_id);
|
||
break;
|
||
}
|
||
}
|
||
LeaveCriticalSection (& g_MCS_Critical_Section);
|
||
|
||
return (return_value);
|
||
}
|
||
|
||
/*
|
||
* MCSError TokenGrab ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This function is called when the user application wishes to grab
|
||
* a token. If the user is attached to the domain, the request will
|
||
* be repackaged as an MCS command and sent to the domain object.
|
||
*/
|
||
MCSError User::TokenGrab (TokenID token_id)
|
||
{
|
||
return (TokenGIRPT (GRAB, token_id));
|
||
}
|
||
|
||
/*
|
||
* MCSError TokenInhibit ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This function is called when the user application wishes to inhibit
|
||
* a token. If the user is attached to the domain, the request will
|
||
* be repackaged as an MCS command and sent to the domain object.
|
||
*/
|
||
MCSError User::TokenInhibit (TokenID token_id)
|
||
{
|
||
return (TokenGIRPT (INHIBIT, token_id));
|
||
}
|
||
|
||
/*
|
||
* MCSError TokenGive ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This function is called when the user application wishes to give away
|
||
* a token. If the user is attached to the domain, the request will
|
||
* be repackaged as an MCS command and sent to the domain object.
|
||
*/
|
||
MCSError User::TokenGive (TokenID token_id, UserID receiver_id)
|
||
{
|
||
MCSError return_value;
|
||
TokenGiveRecord TokenGiveRec;
|
||
|
||
if (receiver_id > 1000) {
|
||
// Fill in the TokenGive command structure.
|
||
TokenGiveRec.uidInitiator = User_ID;
|
||
TokenGiveRec.token_id = token_id;
|
||
TokenGiveRec.receiver_id = receiver_id;
|
||
|
||
EnterCriticalSection (& g_MCS_Critical_Section);
|
||
/*
|
||
* Verify that current conditions are appropriate for a request to be
|
||
* accepted from a user attachment.
|
||
*/
|
||
return_value = ValidateUserRequest ();
|
||
|
||
if (return_value == MCS_NO_ERROR) {
|
||
m_pDomain->TokenGiveRequest (this, &TokenGiveRec);
|
||
}
|
||
LeaveCriticalSection (& g_MCS_Critical_Section);
|
||
}
|
||
else {
|
||
ERROR_OUT(("User::TokenGive: Invalid UserID for receiver."));
|
||
return_value = MCS_INVALID_PARAMETER;
|
||
}
|
||
|
||
return (return_value);
|
||
}
|
||
|
||
/*
|
||
* MCSError TokenGiveResponse ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This function is called when the user application wishes to respond to
|
||
* a previously received token give indication. If the user is attached to
|
||
* the domain, the request will be repackaged as an MCS command and sent to
|
||
* the domain object.
|
||
*/
|
||
MCSError User::TokenGiveResponse (TokenID token_id, Result result)
|
||
{
|
||
MCSError return_value;
|
||
|
||
EnterCriticalSection (& g_MCS_Critical_Section);
|
||
/*
|
||
* Verify that current conditions are appropriate for a request to be
|
||
* accepted from a user attachment.
|
||
*/
|
||
return_value = ValidateUserRequest ();
|
||
|
||
if (return_value == MCS_NO_ERROR)
|
||
{
|
||
m_pDomain->TokenGiveResponse (this, result, User_ID, token_id);
|
||
}
|
||
LeaveCriticalSection (& g_MCS_Critical_Section);
|
||
|
||
return (return_value);
|
||
}
|
||
|
||
/*
|
||
* MCSError TokenPlease ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This function is called when the user application wishes to be given
|
||
* a token. If the user is attached to the domain, the request will
|
||
* be repackaged as an MCS command and sent to the domain object.
|
||
*/
|
||
MCSError User::TokenPlease (TokenID token_id)
|
||
{
|
||
return (TokenGIRPT (PLEASE, token_id));
|
||
}
|
||
|
||
/*
|
||
* MCSError TokenRelease ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This function is called when the user application wishes to release
|
||
* a token. If the user is attached to the domain, the request will
|
||
* be repackaged as an MCS command and sent to the domain object.
|
||
*/
|
||
MCSError User::TokenRelease (TokenID token_id)
|
||
{
|
||
return (TokenGIRPT (RELEASE, token_id));
|
||
}
|
||
|
||
/*
|
||
* MCSError TokenTest ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This function is called when the user application wishes to test
|
||
* a token. If the user is attached to the domain, the request will
|
||
* be repackaged as an MCS command and sent to the domain object.
|
||
*/
|
||
MCSError User::TokenTest (TokenID token_id)
|
||
{
|
||
return (TokenGIRPT (TEST, token_id));
|
||
}
|
||
|
||
/*
|
||
* MCSError ValidateUserRequest ()
|
||
*
|
||
* Private
|
||
*
|
||
* Functional Description:
|
||
* This function is used to determine if it is valid to process an incoming
|
||
* request at the current time. It checks several different conditions
|
||
* to determine this, as follows:
|
||
*
|
||
* - If there is a merge in progress, then the request is not valid.
|
||
* - If this user is not yet attached to a domain, then the request
|
||
* is not valid.
|
||
* - If there are not enough objects of the Memory, Packet, or UserMessage
|
||
* class to handle a reasonable request, then the request is not valid.
|
||
*
|
||
* Note that the check on number of objects is not an absolute guarantee
|
||
* that there will be enough to handle a given request, because a request
|
||
* can result in MANY PDUs and user messages being generated. For example,
|
||
* a single channel admit request can result in lots of channel admit
|
||
* indications being sent. However, checking against a minimum number
|
||
* of objects can reduce the possibility of failure to be astronomically
|
||
* low. And remember, even if MCS runs out of something while processing
|
||
* such a request, it WILL handle it properly (by cleanly destroying the
|
||
* user attachment or MCS connection upon which the failure occurred). So
|
||
* there is no chance of MCS crashing as a result of this.
|
||
*
|
||
* Caveats:
|
||
* None.
|
||
*/
|
||
MCSError User::ValidateUserRequest ()
|
||
{
|
||
MCSError return_value = MCS_NO_ERROR;
|
||
|
||
/*
|
||
* Check to see if there is a merge operation in progress.
|
||
*/
|
||
if (Merge_In_Progress == FALSE)
|
||
{
|
||
/*
|
||
* Make sure the user is attached to the domain.
|
||
*/
|
||
if (User_ID == 0)
|
||
{
|
||
/*
|
||
* The user is not yet attached to the domain. So fail the request
|
||
* without passing it on to the domain object.
|
||
*/
|
||
TRACE_OUT (("User::ValidateUserRequest: user not attached"));
|
||
return_value = MCS_USER_NOT_ATTACHED;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/*
|
||
* This operation could not be processed at this time due to a merge
|
||
* operation in progress at the local provider.
|
||
*
|
||
* NOTE for JASPER:
|
||
* Jasper probably will need to wait on an event handle here, which will be
|
||
* set when the main MCS thread receives all the merging PDUs that get us out
|
||
* of the merging state. Since the only MCS client for Jasper is the GCC,
|
||
* it should be ok to block the client (GCC) while the merging goes on.
|
||
*/
|
||
WARNING_OUT (("User::ValidateUserRequest: merge in progress"));
|
||
return_value = MCS_DOMAIN_MERGING;
|
||
}
|
||
|
||
return (return_value);
|
||
}
|
||
|
||
/*
|
||
* Void RegisterUserAttachment ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This method registers a user attachment with the User object.
|
||
*/
|
||
void User::RegisterUserAttachment (MCSCallBack mcs_callback,
|
||
PVoid user_defined,
|
||
UINT flags)
|
||
{
|
||
TRACE_OUT (("User::RegisterUserAttachment: user handle = %p", this));
|
||
|
||
/*
|
||
* Fill in all of the members of the User object.
|
||
*/
|
||
m_MCSCallback = mcs_callback;
|
||
m_UserDefined = user_defined;
|
||
m_BufferRetryInfo = NULL;
|
||
m_fDisconnectInDataLoss = (flags & ATTACHMENT_DISCONNECT_IN_DATA_LOSS);
|
||
m_fFreeDataIndBuffer = (flags & ATTACHMENT_MCS_FREES_DATA_IND_BUFFER);
|
||
|
||
// Increase the ref count to indicate that the client is now using the object.
|
||
AddRef();
|
||
}
|
||
|
||
/*
|
||
* Void SetDomainParameters ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This command is used to set the current value of the instance variable
|
||
* that holds the maximum user data field length.
|
||
*/
|
||
void User::SetDomainParameters (
|
||
PDomainParameters domain_parameters)
|
||
{
|
||
/*
|
||
* Set the maximum user data length instance variable to conform to the
|
||
* maximum PDU size within the attached domain (minus some overhead to
|
||
* allow for protocol bytes).
|
||
*/
|
||
Maximum_User_Data_Length = domain_parameters->max_mcspdu_size -
|
||
(MAXIMUM_PROTOCOL_OVERHEAD_MCS +
|
||
BER_PROTOCOL_EXTRA_OVERHEAD);
|
||
TRACE_OUT (("User::SetDomainParameters: "
|
||
"maximum user data length = %ld", Maximum_User_Data_Length));
|
||
|
||
/*
|
||
* Use the specified domain parameters to set the type of encoding rules
|
||
* to be used.
|
||
*/
|
||
ASSERT (domain_parameters->protocol_version == PROTOCOL_VERSION_PACKED);
|
||
}
|
||
|
||
/*
|
||
* Void PurgeChannelsIndication ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This function is called during a domain merge operation when there is
|
||
* a conflict in the use of channels. The former Top Provider responds
|
||
* by issuing this command, which causes all users of the channel to be
|
||
* expelled from it. Additionally, if the channel corresponds to a user
|
||
* ID channel, that user is purged from the network.
|
||
*/
|
||
void User::PurgeChannelsIndication (
|
||
CUidList *purge_user_list,
|
||
CChannelIDList *)
|
||
{
|
||
/*
|
||
* Issue a DetachUserIndication to each user contained in the purge user
|
||
* list.
|
||
*/
|
||
DetachUserIndication(REASON_PROVIDER_INITIATED, purge_user_list);
|
||
}
|
||
|
||
/*
|
||
* Void DisconnectProviderUltimatum ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This function will be called when the domain determines the need to
|
||
* tear down quickly. This call simulates the reception of a detach user
|
||
* indication (if the user is already attached), or an unsuccessful
|
||
* attach user confirm (if the user is not yet attached). In either
|
||
* case, the user attachment will be eliminated by this call.
|
||
*/
|
||
void User::DisconnectProviderUltimatum (
|
||
Reason reason)
|
||
{
|
||
CUidList deletion_list;
|
||
|
||
if (User_ID != 0)
|
||
{
|
||
/*
|
||
* If the user is already attached, simulate a detach user indication
|
||
* on the local user ID.
|
||
*/
|
||
deletion_list.Append(User_ID);
|
||
DetachUserIndication(reason, &deletion_list);
|
||
}
|
||
else
|
||
{
|
||
/*
|
||
* If the user is not yet attached, simulate an unsuccessful attach
|
||
* user confirm.
|
||
*/
|
||
AttachUserConfirm(RESULT_UNSPECIFIED_FAILURE, 0);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Void AttachUserConfirm ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This function is called by the domain in response to the attach user
|
||
* request that was sent by this object when it was first created. This
|
||
* call will contain the result of that attachment operation. If the
|
||
* result is successful, this call will also contain the user ID for this
|
||
* attachment.
|
||
*/
|
||
void User::AttachUserConfirm (
|
||
Result result,
|
||
UserID uidInitiator)
|
||
{
|
||
LPARAM parameter;
|
||
|
||
if (Deletion_Pending == FALSE)
|
||
{
|
||
ASSERT (User_ID == 0);
|
||
|
||
/*
|
||
* If the result is successful, set the user ID of this user
|
||
* object to indicate its new status.
|
||
*/
|
||
if (result == RESULT_SUCCESSFUL)
|
||
User_ID = uidInitiator;
|
||
else
|
||
Deletion_Pending = TRUE;
|
||
|
||
parameter = PACK_PARAMETER (uidInitiator, result);
|
||
|
||
/*
|
||
* Post the user message to the application.
|
||
*/
|
||
if (! PostMessage (m_hWnd, USER_MSG_BASE + MCS_ATTACH_USER_CONFIRM,
|
||
(WPARAM) this, parameter)) {
|
||
WARNING_OUT (("User::AttachUserConfirm: Failed to post msg to application. Error: %d",
|
||
GetLastError()));
|
||
if (result != RESULT_SUCCESSFUL)
|
||
Release();
|
||
}
|
||
}
|
||
else {
|
||
Release();
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Void DetachUserIndication ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This function is called by the domain whenever a user leaves the domain
|
||
* (voluntarily or otherwise). Furthermore, if a user ID in the indication
|
||
* is the same as the local user ID, then this user is being involuntarily
|
||
* detached.
|
||
*/
|
||
Void User::DetachUserIndication (
|
||
Reason reason,
|
||
CUidList *user_id_list)
|
||
{
|
||
UserID uid;
|
||
LPARAM parameter;
|
||
BOOL bPostMsgResult;
|
||
|
||
if (Deletion_Pending == FALSE)
|
||
{
|
||
/*
|
||
* Iterate through the list of users to be deleted.
|
||
*/
|
||
user_id_list->Reset();
|
||
while (NULL != (uid = user_id_list->Iterate()))
|
||
{
|
||
parameter = PACK_PARAMETER(uid, reason);
|
||
|
||
/*
|
||
* Post the user message to the application.
|
||
*/
|
||
bPostMsgResult = PostMessage (m_hWnd, USER_MSG_BASE + MCS_DETACH_USER_INDICATION,
|
||
(WPARAM) this, parameter);
|
||
if (! bPostMsgResult) {
|
||
WARNING_OUT (("User::DetachUserIndication: Failed to post msg to application. Error: %d",
|
||
GetLastError()));
|
||
}
|
||
|
||
/*
|
||
* If this indication is deleting this user attachment, then
|
||
* set the deletion pending flag, and break out of the loop.
|
||
*/
|
||
if (User_ID == uid)
|
||
{
|
||
m_originalUser_ID = User_ID;
|
||
User_ID = 0;
|
||
Deletion_Pending = TRUE;
|
||
if (! bPostMsgResult)
|
||
Release();
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
/*
|
||
* The user has already called ReleaseInterface(). If the
|
||
* Indication is for this attachment, we have to release and
|
||
* probably, delete the object.
|
||
*/
|
||
if (user_id_list->Find(User_ID)) {
|
||
Release();
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Void ChannelJLCDAEConfInd ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This function is called to post a channel confirm/indication message
|
||
* to the user application. It handles ChannelJoinConfirms,
|
||
* ChannelLeaveIndications, ChannelConveneConfirms, ChannelDisbandIndications
|
||
* and ChannelExpelIndications.
|
||
*/
|
||
Void User::ChannelConfInd ( UINT type,
|
||
ChannelID channel_id,
|
||
UINT arg16)
|
||
{
|
||
LPARAM parameter;
|
||
|
||
ASSERT (HIWORD(arg16) == 0);
|
||
|
||
if (Deletion_Pending == FALSE)
|
||
{
|
||
parameter = PACK_PARAMETER (channel_id, arg16);
|
||
|
||
/*
|
||
* Post the user message to the application.
|
||
*/
|
||
if (! PostMessage (m_hWnd, USER_MSG_BASE + type,
|
||
(WPARAM) this, parameter)) {
|
||
WARNING_OUT (("User::ChannelConfInd: Failed to post msg to application. Type: %d. Error: %d",
|
||
type, GetLastError()));
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/*
|
||
* Void ChannelJoinConfirm ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This function is called by the domain in response to a previous channel
|
||
* join request. This call contains the result of the join request, as
|
||
* well as the channel that has just been joined.
|
||
*/
|
||
Void User::ChannelJoinConfirm (
|
||
Result result,
|
||
UserID,
|
||
ChannelID requested_id,
|
||
ChannelID channel_id)
|
||
{
|
||
ChannelConfInd (MCS_CHANNEL_JOIN_CONFIRM, channel_id, (UINT) result);
|
||
}
|
||
|
||
|
||
/*
|
||
* Void ChannelLeaveIndication ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
*/
|
||
Void User::ChannelLeaveIndication (
|
||
Reason reason,
|
||
ChannelID channel_id)
|
||
{
|
||
ChannelConfInd (MCS_CHANNEL_LEAVE_INDICATION, channel_id, (UINT) reason);
|
||
}
|
||
|
||
/*
|
||
* Void ChannelConveneConfirm ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This function is called by the domain in response to a previous channel
|
||
* convene request. This call contains the result of the request, as
|
||
* well as the channel that has just been convened.
|
||
*/
|
||
Void User::ChannelConveneConfirm (
|
||
Result result,
|
||
UserID,
|
||
ChannelID channel_id)
|
||
{
|
||
ChannelConfInd (MCS_CHANNEL_CONVENE_CONFIRM, channel_id, (UINT) result);
|
||
}
|
||
|
||
/*
|
||
* Void ChannelDisbandIndication ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This function is called by the domain when MCS disbands an existing
|
||
* private channel.
|
||
*/
|
||
Void User::ChannelDisbandIndication (
|
||
ChannelID channel_id)
|
||
{
|
||
ChannelConfInd (MCS_CHANNEL_DISBAND_INDICATION, channel_id, REASON_CHANNEL_PURGED);
|
||
}
|
||
|
||
/*
|
||
* Void ChannelAdmitIndication ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This function is called by the domain when a user is admitted to a
|
||
* private channel.
|
||
*/
|
||
Void User::ChannelAdmitIndication (
|
||
UserID uidInitiator,
|
||
ChannelID channel_id,
|
||
CUidList *)
|
||
{
|
||
ChannelConfInd (MCS_CHANNEL_ADMIT_INDICATION, channel_id, (UINT) uidInitiator);
|
||
}
|
||
|
||
/*
|
||
* Void ChannelExpelIndication ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This function is called by the domain when a user is expelled from a
|
||
* private channel.
|
||
*/
|
||
Void User::ChannelExpelIndication (
|
||
ChannelID channel_id,
|
||
CUidList *)
|
||
{
|
||
ChannelConfInd (MCS_CHANNEL_EXPEL_INDICATION, channel_id, REASON_USER_REQUESTED);
|
||
}
|
||
|
||
/*
|
||
* Void SendDataIndication ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This function is called by the domain when data needs to sent to the
|
||
* user on a channel that the user has joined.
|
||
*/
|
||
Void User::SendDataIndication (
|
||
UINT message_type,
|
||
PDataPacket packet)
|
||
{
|
||
if (Deletion_Pending == FALSE)
|
||
{
|
||
/*
|
||
* Lock the packet object to indicate that we wish to have future
|
||
* access to the decoded data that it contains. Then get the
|
||
* address of the decoded data structure.
|
||
*/
|
||
packet->Lock ();
|
||
packet->SetMessageType(message_type);
|
||
|
||
// flush packets in the pending queue
|
||
PDataPacket pkt;
|
||
while (NULL != (pkt = m_PostMsgPendingQueue.PeekHead()))
|
||
{
|
||
if (::PostMessage(m_hWnd, USER_MSG_BASE + pkt->GetMessageType(),
|
||
(WPARAM) this, (LPARAM) pkt))
|
||
{
|
||
// remove the item just posted
|
||
m_PostMsgPendingQueue.Get();
|
||
}
|
||
else
|
||
{
|
||
// fail to post pending ones, just append the new one and bail out.
|
||
m_PostMsgPendingQueue.Append(packet);
|
||
return;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Post the user message to the application.
|
||
*/
|
||
if (! ::PostMessage(m_hWnd, USER_MSG_BASE + message_type,
|
||
(WPARAM) this, (LPARAM) packet))
|
||
{
|
||
// fail to post pending ones, just append the new one and bail out.
|
||
m_PostMsgPendingQueue.Append(packet);
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Void TokenConfInd ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This function is called to post a token confirm/indication message
|
||
* to the user application.
|
||
*/
|
||
Void User::TokenConfInd (UINT type,
|
||
TokenID token_id,
|
||
UINT arg16)
|
||
{
|
||
LPARAM parameter;
|
||
|
||
ASSERT (HIWORD(arg16) == 0);
|
||
|
||
if (Deletion_Pending == FALSE)
|
||
{
|
||
parameter = PACK_PARAMETER (token_id, arg16);
|
||
|
||
/*
|
||
* Post the user message to the application.
|
||
*/
|
||
if (! PostMessage (m_hWnd, USER_MSG_BASE + type,
|
||
(WPARAM) this, parameter)) {
|
||
WARNING_OUT (("User::TokenConfInd: Failed to post msg to application. Type: %d. Error: %d",
|
||
type, GetLastError()));
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Void TokenGrabConfirm ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This function is called by the domain in response to a previous token
|
||
* grab request. This call contains the result of the grab request, as
|
||
* well as the token that has just been grabbed.
|
||
*/
|
||
Void User::TokenGrabConfirm (
|
||
Result result,
|
||
UserID,
|
||
TokenID token_id,
|
||
TokenStatus)
|
||
{
|
||
TokenConfInd (MCS_TOKEN_GRAB_CONFIRM, token_id, (UINT) result);
|
||
}
|
||
|
||
/*
|
||
* Void TokenInhibitConfirm ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This function is called by the domain in response to a previous token
|
||
* inhibit request. This call contains the result of the inhibit request,
|
||
* as well as the token that has just been inhibited.
|
||
*/
|
||
Void User::TokenInhibitConfirm (
|
||
Result result,
|
||
UserID,
|
||
TokenID token_id,
|
||
TokenStatus)
|
||
{
|
||
TokenConfInd (MCS_TOKEN_INHIBIT_CONFIRM, token_id, (UINT) result);
|
||
}
|
||
|
||
/*
|
||
* Void TokenGiveIndication ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This function is called by the domain when another user attempts to
|
||
* give this user a token.
|
||
*/
|
||
Void User::TokenGiveIndication (
|
||
PTokenGiveRecord pTokenGiveRec)
|
||
{
|
||
TokenConfInd (MCS_TOKEN_GIVE_INDICATION, pTokenGiveRec->token_id,
|
||
(UINT) pTokenGiveRec->uidInitiator);
|
||
}
|
||
|
||
/*
|
||
* Void TokenGiveConfirm ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This function is called by the domain in response to a previous token
|
||
* give request. This call contains the result of the give request.
|
||
*/
|
||
Void User::TokenGiveConfirm (
|
||
Result result,
|
||
UserID,
|
||
TokenID token_id,
|
||
TokenStatus)
|
||
{
|
||
TokenConfInd (MCS_TOKEN_GIVE_CONFIRM, token_id, (UINT) result);
|
||
}
|
||
|
||
/*
|
||
* Void TokenPleaseIndication ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This function is called by the domain when a user somewhere in the
|
||
* domain issues a token please request for a token that is currently
|
||
* owned by this user.
|
||
*/
|
||
Void User::TokenPleaseIndication (
|
||
UserID uidInitiator,
|
||
TokenID token_id)
|
||
{
|
||
TokenConfInd (MCS_TOKEN_PLEASE_INDICATION, token_id, (UINT) uidInitiator);
|
||
}
|
||
|
||
/*
|
||
* Void TokenReleaseIndication ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This command is called when a token is being purged from the lower
|
||
* domain after a new connection is established. It causes the indication
|
||
* to be forwarded to the user application, letting it know that it no
|
||
* longer owns the token.
|
||
*/
|
||
Void User::TokenReleaseIndication (
|
||
Reason reason,
|
||
TokenID token_id)
|
||
{
|
||
TokenConfInd (MCS_TOKEN_RELEASE_INDICATION, token_id, (UINT) reason);
|
||
}
|
||
|
||
/*
|
||
* Void TokenReleaseConfirm ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This function is called by the domain in response to a previous token
|
||
* release request. This call contains the result of the release request,
|
||
* as well as the token that has just been released.
|
||
*/
|
||
Void User::TokenReleaseConfirm (
|
||
Result result,
|
||
UserID,
|
||
TokenID token_id,
|
||
TokenStatus)
|
||
{
|
||
TokenConfInd (MCS_TOKEN_RELEASE_CONFIRM, token_id, (UINT) result);
|
||
}
|
||
|
||
/*
|
||
* Void TokenTestConfirm ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This function is called by the domain in response to a previous token
|
||
* test request. This call contains the result of the test request,
|
||
* as well as the token that has just been tested.
|
||
*/
|
||
Void User::TokenTestConfirm (
|
||
UserID,
|
||
TokenID token_id,
|
||
TokenStatus token_status)
|
||
{
|
||
TokenConfInd (MCS_TOKEN_TEST_CONFIRM, token_id, (UINT) token_status);
|
||
}
|
||
|
||
/*
|
||
* Void MergeDomainIndication ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This function is called by domain upon entering or leaving a domain
|
||
* merger state.
|
||
*/
|
||
Void User::MergeDomainIndication (
|
||
MergeStatus merge_status)
|
||
{
|
||
if (Deletion_Pending == FALSE)
|
||
{
|
||
/*
|
||
* If the merge operation is starting, set a boolean flag
|
||
* indicating that this object should reject all user activity.
|
||
* Otherwise, reset the flag.
|
||
*/
|
||
if (merge_status == MERGE_DOMAIN_IN_PROGRESS)
|
||
{
|
||
TRACE_OUT (("User::MergeDomainIndication: entering merge state"));
|
||
Merge_In_Progress = TRUE;
|
||
}
|
||
else
|
||
{
|
||
TRACE_OUT (("User::MergeDomainIndication: leaving merge state"));
|
||
Merge_In_Progress = FALSE;
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Void PurgeMessageQueue ()
|
||
*
|
||
* Private
|
||
*
|
||
* Functional Description:
|
||
* This function is called to purge all current entries from the message
|
||
* queue, freeing up resources correctly (to prevent leaks).
|
||
*
|
||
* Formal Parameters:
|
||
* None.
|
||
*
|
||
* Return Value:
|
||
* None.
|
||
*
|
||
* Side Effects:
|
||
* None.
|
||
*
|
||
* Caveats:
|
||
* This function should only be called in the client's thread's context.
|
||
*/
|
||
Void User::PurgeMessageQueue ()
|
||
{
|
||
MSG msg;
|
||
PDataPacket packet;
|
||
HWND hWnd;
|
||
|
||
// First, unlock the packets in the list of pending data indications
|
||
while (NULL != (packet = m_DataPktQueue.Get()))
|
||
packet->Unlock();
|
||
|
||
// Keep a copy of the attachment's HWND to destroy it later.
|
||
hWnd = m_hWnd;
|
||
m_hWnd = NULL;
|
||
|
||
/*
|
||
* This loop calls PeekMessage to go through all the messages in the thread's
|
||
* queue that were posted by the main MCS thread. It removes these
|
||
* messages and frees the resources that they consume.
|
||
*/
|
||
while (PeekMessage (&msg, hWnd, USER_MSG_BASE, USER_MSG_BASE + MCS_LAST_USER_MESSAGE,
|
||
PM_REMOVE)) {
|
||
|
||
if (msg.message == WM_QUIT) {
|
||
// Repost the quit
|
||
PostQuitMessage (0);
|
||
break;
|
||
}
|
||
|
||
/*
|
||
* If this is a data indication message, we need to unlock
|
||
* the packet associated with this message.
|
||
*/
|
||
else if ((msg.message == USER_MSG_BASE + MCS_SEND_DATA_INDICATION) ||
|
||
(msg.message == USER_MSG_BASE + MCS_UNIFORM_SEND_DATA_INDICATION)) {
|
||
((PDataPacket) msg.lParam)->Unlock ();
|
||
}
|
||
else if (((msg.message == USER_MSG_BASE + MCS_ATTACH_USER_CONFIRM) &&
|
||
((Result) HIWORD(msg.lParam) != RESULT_SUCCESSFUL)) ||
|
||
((msg.message == USER_MSG_BASE + MCS_DETACH_USER_INDICATION) &&
|
||
(m_originalUser_ID == (UserID) LOWORD(msg.lParam)))) {
|
||
ASSERT (this == (PUser) msg.wParam);
|
||
Release();
|
||
break;
|
||
}
|
||
}
|
||
|
||
// Destroy the window; we do not need it anymore
|
||
DestroyWindow (hWnd);
|
||
}
|
||
|
||
void User::IssueDataIndication (
|
||
UINT message_type,
|
||
PDataPacket packet)
|
||
{
|
||
LPARAM parameter;
|
||
PMemory memory;
|
||
BOOL bIssueCallback = TRUE;
|
||
BOOL bBufferInPacket = TRUE;
|
||
PUChar data_ptr;
|
||
SendDataIndicationPDU send_data_ind_pdu;
|
||
|
||
switch (packet->GetSegmentation()) {
|
||
case SEGMENTATION_BEGIN | SEGMENTATION_END:
|
||
parameter = (LPARAM) &(((PDomainMCSPDU) (packet->GetDecodedData()))->
|
||
u.send_data_indication);
|
||
data_ptr = packet->GetUserData();
|
||
memory = packet->GetMemory();
|
||
break;
|
||
|
||
case SEGMENTATION_END:
|
||
{
|
||
/*
|
||
* We now have to collect all the individual packets from m_DataPktQueue
|
||
* that go with this MCS Data PDU and sent them as a single data indication
|
||
* using a buffer large enough for all the data.
|
||
*/
|
||
/*
|
||
* First, find out the size of the large buffer we need to allocate.
|
||
* Note that we make a copy of the original m_DataPktList and operate
|
||
* on the copy, since we need to remove items from the original list.
|
||
*/
|
||
CDataPktQueue PktQ(&m_DataPktQueue);
|
||
UINT size;
|
||
PDataPacket data_pkt;
|
||
PUChar ptr;
|
||
#ifdef DEBUG
|
||
UINT uiCount = 0;
|
||
#endif // DEBUG
|
||
|
||
size = packet->GetUserDataLength();
|
||
PktQ.Reset();
|
||
while (NULL != (data_pkt = PktQ.Iterate()))
|
||
{
|
||
if (packet->Equivalent (data_pkt)) {
|
||
#ifdef DEBUG
|
||
if (uiCount == 0) {
|
||
ASSERT (data_pkt->GetSegmentation() == SEGMENTATION_BEGIN);
|
||
}
|
||
else {
|
||
ASSERT (data_pkt->GetSegmentation() == 0);
|
||
}
|
||
uiCount++;
|
||
#endif // DEBUG
|
||
size += data_pkt->GetUserDataLength();
|
||
// Remove from the original list, since we are processing the callback.
|
||
m_DataPktQueue.Remove(data_pkt);
|
||
}
|
||
}
|
||
// Allocate the memory we need.
|
||
DBG_SAVE_FILE_LINE
|
||
memory = AllocateMemory (NULL, size);
|
||
if (memory != NULL) {
|
||
bBufferInPacket = FALSE;
|
||
// Copy the individual indications into the large buffer.
|
||
data_ptr = ptr = memory->GetPointer();
|
||
PktQ.Reset();
|
||
/*
|
||
* We need to enter the MCS critical section, because
|
||
* we are unlocking packets.
|
||
*/
|
||
EnterCriticalSection (& g_MCS_Critical_Section);
|
||
while (NULL != (data_pkt = PktQ.Iterate()))
|
||
{
|
||
if (packet->Equivalent (data_pkt)) {
|
||
size = data_pkt->GetUserDataLength();
|
||
memcpy ((void *) ptr,
|
||
(void *) data_pkt->GetUserData(),
|
||
size);
|
||
ptr += size;
|
||
data_pkt->Unlock();
|
||
}
|
||
}
|
||
// Leave the MCS critical section
|
||
LeaveCriticalSection (& g_MCS_Critical_Section);
|
||
|
||
// Copy the last indication into the large buffer.
|
||
memcpy ((void *) ptr,
|
||
(void *) packet->GetUserData(),
|
||
packet->GetUserDataLength());
|
||
|
||
/*
|
||
* Prepare the SendDataIndicationPDU structure for the client.
|
||
* Notice that we can use the first 8 bytes from the decoded
|
||
* structure of the current "packet" to fill in the first bytes from
|
||
* it.
|
||
*/
|
||
memcpy ((void *) &send_data_ind_pdu,
|
||
(void *) &(((PDomainMCSPDU) (packet->GetDecodedData()))->
|
||
u.send_data_indication), 8);
|
||
send_data_ind_pdu.segmentation = SEGMENTATION_BEGIN | SEGMENTATION_END;
|
||
send_data_ind_pdu.user_data.length = memory->GetLength();
|
||
send_data_ind_pdu.user_data.value = data_ptr;
|
||
parameter = (ULONG_PTR) &send_data_ind_pdu;
|
||
}
|
||
else {
|
||
/*
|
||
* We have failed to issue the data indication callback to the client.
|
||
* The user attachment has been compromised. If the attachment can not
|
||
* live with this loss, we have to detach them from the conference.
|
||
*/
|
||
ERROR_OUT (("User::IssueDataIndication: Memory allocation failed for segmented buffer of size %d.",
|
||
size));
|
||
bIssueCallback = FALSE;
|
||
|
||
// Clean up after the failure
|
||
EnterCriticalSection (& g_MCS_Critical_Section);
|
||
PktQ.Reset();
|
||
while (NULL != (data_pkt = PktQ.Iterate()))
|
||
{
|
||
if (m_fDisconnectInDataLoss ||
|
||
(packet->Equivalent (data_pkt))) {
|
||
data_pkt->Unlock();
|
||
}
|
||
}
|
||
packet->Unlock();
|
||
LeaveCriticalSection (& g_MCS_Critical_Section);
|
||
|
||
// Disconnect if the client wants us to.
|
||
if (m_fDisconnectInDataLoss) {
|
||
// Clear the list of the already-cleared pending packets. We will soon get a ReleaseInterface().
|
||
m_DataPktQueue.Clear();
|
||
|
||
ERROR_OUT(("User::IssueDataIndication: Disconnecting user because of data loss..."));
|
||
/*
|
||
* Send a detach user indication directly to the user application.
|
||
* Note that this cannot go through the queue, due to the memory
|
||
* failure.
|
||
*/
|
||
(*m_MCSCallback) (MCS_DETACH_USER_INDICATION,
|
||
PACK_PARAMETER (User_ID, REASON_PROVIDER_INITIATED),
|
||
m_UserDefined);
|
||
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
|
||
case SEGMENTATION_BEGIN:
|
||
case 0:
|
||
// Append the packet to the list of packets for send.
|
||
m_DataPktQueue.Append(packet);
|
||
bIssueCallback = FALSE;
|
||
break;
|
||
|
||
default:
|
||
ASSERT (FALSE);
|
||
ERROR_OUT(("User::IssueDataIndication: Processed packet with invalid segmentation field."));
|
||
break;
|
||
}
|
||
|
||
if (bIssueCallback) {
|
||
/*
|
||
* If the client has advised the server not to free the data, we have to
|
||
* lock the buffer.
|
||
*/
|
||
if (m_fFreeDataIndBuffer == FALSE) {
|
||
if (bBufferInPacket)
|
||
LockMemory (memory);
|
||
|
||
// Enter the data indication info in a dictionary, for the Free request.
|
||
if (GetMemoryObject(data_ptr) != memory)
|
||
{
|
||
m_DataIndMemoryBuf2.Append((LPVOID) data_ptr, memory);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Issue the callback. The callee can not refuse to process this.
|
||
*/
|
||
(*m_MCSCallback) (message_type, parameter, m_UserDefined);
|
||
|
||
/*
|
||
* If the client has advised the server to free the data indication buffer
|
||
* after delivering the callback, we must do so.
|
||
*/
|
||
if (m_fFreeDataIndBuffer) {
|
||
if (bBufferInPacket == FALSE)
|
||
FreeMemory (memory);
|
||
}
|
||
|
||
// To unlock a packet, we need to enter the MCS CS.
|
||
EnterCriticalSection (& g_MCS_Critical_Section);
|
||
packet->Unlock();
|
||
LeaveCriticalSection (& g_MCS_Critical_Section);
|
||
}
|
||
}
|
||
|
||
|
||
/*
|
||
* LRESULT UserWindowProc ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This is the window procedure that will be used by all internally
|
||
* created windows. A hidden window is created internally when the
|
||
* application attaches to an MCS domain. This technique insures
|
||
* that callbacks are delivered to the owner in the same thread that
|
||
* initially created the attachment.
|
||
*/
|
||
LRESULT CALLBACK UserWindowProc (
|
||
HWND window_handle,
|
||
UINT message,
|
||
WPARAM word_parameter,
|
||
LPARAM long_parameter)
|
||
{
|
||
UINT mcs_message;
|
||
//PDataPacket packet;
|
||
PUser puser;
|
||
|
||
if ((message >= USER_MSG_BASE) && (message < USER_MSG_BASE + MCS_LAST_USER_MESSAGE)) {
|
||
// This is an MCS msg going to the user application.
|
||
|
||
// Compute the MCS msg type
|
||
mcs_message = message - USER_MSG_BASE;
|
||
|
||
// Retrieve the pointer to the User (interface) object.
|
||
puser = (PUser) word_parameter;
|
||
if (NULL != puser)
|
||
{
|
||
/*
|
||
* Find out whether this is a data indication. If it is, set the
|
||
* packet variable.
|
||
*/
|
||
if ((mcs_message == MCS_SEND_DATA_INDICATION) ||
|
||
(mcs_message == MCS_UNIFORM_SEND_DATA_INDICATION)) {
|
||
puser->IssueDataIndication (mcs_message, (PDataPacket) long_parameter);
|
||
}
|
||
else {
|
||
/*
|
||
* Issue the callback. Notice that the callee can not refuse
|
||
* to process this.
|
||
*/
|
||
(*(puser->m_MCSCallback)) (mcs_message, long_parameter, puser->m_UserDefined);
|
||
}
|
||
|
||
/*
|
||
* We may need to release the User object. This is the Server
|
||
* side release.
|
||
*/
|
||
if (((mcs_message == MCS_ATTACH_USER_CONFIRM) &&
|
||
((Result) HIWORD(long_parameter) != RESULT_SUCCESSFUL)) ||
|
||
((mcs_message == MCS_DETACH_USER_INDICATION) &&
|
||
(puser->m_originalUser_ID == (UserID) LOWORD(long_parameter)))) {
|
||
puser->Release();
|
||
}
|
||
}
|
||
else
|
||
{
|
||
ERROR_OUT(("UserWindowProc: null puser"));
|
||
}
|
||
return (0);
|
||
}
|
||
else {
|
||
/*
|
||
* Invoke the default window message handler to handle this
|
||
* message.
|
||
*/
|
||
return (DefWindowProc (window_handle, message, word_parameter,
|
||
long_parameter));
|
||
}
|
||
}
|
||
|
||
|
||
/*
|
||
* Void CALLBACK TimerProc (HWND, UINT, UINT, DWORD
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This is the timer procedure. Timer messages will be routed to this
|
||
* function as a result of timer events which have been set up to recheck
|
||
* resource levels. This would happen following a call to either
|
||
* MCSSendDataRequest or MCSUniformSendDataRequest which resulted in a
|
||
* return value of MCS_TRANSMIT_BUFFER_FULL.
|
||
*/
|
||
Void CALLBACK TimerProc (HWND, UINT, UINT timer_id, DWORD)
|
||
{
|
||
PUser puser;
|
||
|
||
/*
|
||
* Enter the critical section which protects global data.
|
||
*/
|
||
EnterCriticalSection (& g_MCS_Critical_Section);
|
||
|
||
/*
|
||
* First, we must find which user owns this timer. We will do this by
|
||
* searching through the Static_User_List.
|
||
*/
|
||
if (NULL == (puser = User::s_pTimerUserList2->Find(timer_id)))
|
||
{
|
||
WARNING_OUT (("TimerProc: no user owns this timer - deleting timer"));
|
||
KillTimer (NULL, timer_id);
|
||
goto Bail;
|
||
}
|
||
|
||
/*
|
||
* Make sure that this user is actively attached. If not, then kill the
|
||
* timer and delete the user's buffer retry info structure.
|
||
*/
|
||
if ((puser->User_ID == 0) || puser->Deletion_Pending)
|
||
{
|
||
WARNING_OUT (("TimerProc: user is not attached - deleting timer"));
|
||
goto CleanupBail;
|
||
}
|
||
|
||
/*
|
||
* If we don't have retryinfo just get out of here.
|
||
*/
|
||
if(puser->m_BufferRetryInfo == NULL)
|
||
{
|
||
WARNING_OUT (("TimerProc: user does not have buffer retry info - deleting timer"));
|
||
goto CleanupBail;
|
||
}
|
||
|
||
/*
|
||
* We have identified a valid owner of this timer.
|
||
* Verify that there is enough memory for the
|
||
* required size before proceeding. Note that since there
|
||
* can be multiple processes allocating from the same memory
|
||
* at the same time, this call does not guarantee
|
||
* that that the allocations will succeed.
|
||
*/
|
||
if (GetFreeMemory (SEND_PRIORITY) < puser->m_BufferRetryInfo->user_data_length)
|
||
{
|
||
TRACE_OUT (("TimerProc: not enough memory buffers of required size"));
|
||
goto Bail;
|
||
}
|
||
|
||
/*
|
||
* If the routine gets this far, then an adequate level of resources
|
||
* now exists.
|
||
*/
|
||
|
||
/*
|
||
* Issue an MCS_TRANSMIT_BUFFER_AVAILABLE_INDICATION to the user.
|
||
*/
|
||
TRACE_OUT(("TimerProc: Delivering MCS_TRANSMIT_BUFFER_AVAILABLE_INDICATION callback."));
|
||
// (*(puser->m_MCSCallback)) (MCS_TRANSMIT_BUFFER_AVAILABLE_INDICATION,
|
||
// 0, puser->m_UserDefined);
|
||
|
||
|
||
if(!PostMessage (puser->m_hWnd, USER_MSG_BASE + MCS_TRANSMIT_BUFFER_AVAILABLE_INDICATION,(WPARAM) puser, 0))
|
||
{
|
||
ERROR_OUT (("TimerProc: Failed to post msg to application. Error: %d", GetLastError()));
|
||
}
|
||
|
||
|
||
CleanupBail:
|
||
KillTimer (NULL, timer_id);
|
||
delete puser->m_BufferRetryInfo;
|
||
puser->m_BufferRetryInfo = NULL;
|
||
User::s_pTimerUserList2->Remove(timer_id);
|
||
|
||
Bail:
|
||
// Leave the attachment's critical section
|
||
LeaveCriticalSection (& g_MCS_Critical_Section);
|
||
|
||
}
|
||
|
||
|
||
|