You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
6039 lines
180 KiB
6039 lines
180 KiB
#include "precomp.h"
|
|
DEBUG_FILEZONE(ZONE_T120_MCSNC);
|
|
/*
|
|
* domain.cpp
|
|
*
|
|
* Copyright (c) 1993 - 1995 by DataBeam Corporation, Lexington, KY
|
|
*
|
|
* Abstract:
|
|
* This is the implementation file for the domain class. The domain
|
|
* class encapsulates a single instance of a domain information base.
|
|
* This class include all code necessary to modify and act upon that
|
|
* information base. Almost all activity into and out of objects of
|
|
* this class is in the form of MCS commands. These commands are
|
|
* implemented as virtual member functions that are inherited from its
|
|
* base class CommandTarget. These commands are essentially the same
|
|
* as the Protocol Data Units (PDUs) defined in T.125.
|
|
*
|
|
* This class inherits from CommandTarget, which is where the virtual
|
|
* member functions for each command is defined. All commands that are
|
|
* handled by this class are overridden by it. Life for a domain object
|
|
* consists of receiving these commands and responding by transmitting
|
|
* these commands. A Domain object has no need for its own "thread" of
|
|
* execution.
|
|
*
|
|
* Instances of the domain class maintain an information base that is
|
|
* used to determine how to respond to these commands. The commands are
|
|
* not only routed according to the state of the information base, but also
|
|
* act to change the information base. When two MCS providers are
|
|
* connected, the domain objects within them become logically linked. This
|
|
* means that they exchange these commands in such a manner as to guarantee
|
|
* the MCS services for which the providers are responsible.
|
|
*
|
|
* When a domain object is first instantiated, its information base is
|
|
* empty. That is, it has no user attachments, no MCS connections, no
|
|
* channels, no tokens, and no queued requests. As the domain object
|
|
* processes commands, the information base takes shape, and all subsequent
|
|
* activity follows that shape.
|
|
*
|
|
* By necessity, there is a lot of complex code in this module. This is to
|
|
* allow for all the timing problems that can occur in a distributed
|
|
* network, such as MCS provides for. In order to reduce the complexity
|
|
* as much as possible, this class does NOT worry about certains things,
|
|
* as follows:
|
|
*
|
|
* The Domain class does NOT include code to perform any kind of flow
|
|
* control. When a send data command comes in to a domain, it is sent out
|
|
* to any attachment that is to receive it. It is assumed that any
|
|
* buffering and flow control is handled by the attachments.
|
|
*
|
|
* For the most part the domain class does NOT distinguish between user
|
|
* attachments and MCS connections. To the domain, they are merely
|
|
* referred to as "attachments". MCS connections can be either upward or
|
|
* downward attachments. User attachments can only be downward
|
|
* attachments. In the case where a user detaches and the domain needs to
|
|
* know if the whole attachment is gone or just one user, it can check an
|
|
* entry in its attachment dictionary to determine the type. Most of the
|
|
* time it does not care. Most confirms and indications are routed to user
|
|
* attachments in exactly the same way they are routed to MCS connections.
|
|
*
|
|
* Domain objects do not worry about memory management. They merely pass
|
|
* packet objects from place to place. They NEVER look at the contents
|
|
* of the packet objects. It is assumed that the attachments have
|
|
* allocated memory for the user data that is being passed around.
|
|
*
|
|
* Where possible, behavior that is specific to channels and tokens has
|
|
* been relegated to those classes. It is necessary for the domain to
|
|
* handle channel and token behavior for IDs that do not exist.
|
|
*
|
|
* Private Instance Variables:
|
|
* Merge_State
|
|
* This is current merge state that the domain is in. These states
|
|
* are detailed in "domain.h".
|
|
* Outstanding_Merge_Requests
|
|
* This is a counter showing the number of outstanding merge requests.
|
|
* The domain object uses this to know when an in-process merge is
|
|
* complete.
|
|
* Number_Of_Users
|
|
* This is the number of users in the domain.
|
|
* Number_Of_Channels
|
|
* This is the number of channels in the domain.
|
|
* Number_Of_Tokens
|
|
* This is the number of tokens in the domain.
|
|
* Domain_Parameters
|
|
* This is a structure that contains the currently negotiated domain
|
|
* parameters. These parameters are used to validate requests, such
|
|
* as the adding of a new user.
|
|
* Domain_Parameters_Locked
|
|
* This is a boolean flag that indicates whether or not the domain
|
|
* parameters have been locked into place yet. This locking will
|
|
* occur when the domain object accepts its first MCS connection.
|
|
* m_pConnToTopProvider
|
|
* This is a pointer to the attachment that represents the link to the
|
|
* top provider. Note that this provider may be several hops away
|
|
* from the top provider, so this really just points in the direction
|
|
* of the top provider. If this pointer is NULL, this THIS is the
|
|
* top provider.
|
|
* m_AttachmentList
|
|
* This is a list of the downward attachments that this domain is
|
|
* aware of. Remeber that this list can contain any combination of
|
|
* user attachments and MCS connections. They are treated equally
|
|
* for most things.
|
|
* m_AttachUserQueue
|
|
* This is a list of outstanding attach user requests. It is necessary
|
|
* to remember these requests so that they can answered in the same
|
|
* order in which they arrived.
|
|
* m_MergeQueue
|
|
* During a merge operation, this queue is used to remember how to
|
|
* route merge confirms back to their originators. The assumption is
|
|
* made that an upward provider will always respond to merge requests
|
|
* in the same order that they were received in (a valid assumption
|
|
* for our implementation). Also note that this implementation
|
|
* currently only merges one resource type at a time, so only one queue
|
|
* is necessary. For example, user IDs are merged, then static
|
|
* channels, and so on.
|
|
* m_ChannelList2
|
|
* This is a list of channel objects that correspond to active channels
|
|
* within this domain. When a channel object exists, the domain lets
|
|
* it handle all channel related activity (such as approving who can
|
|
* join a channel).
|
|
* m_TokenList2
|
|
* This is a list of token objects that correspond to active tokens
|
|
* within this domain. When a token object exists, the domain lets it
|
|
* handle all token related activity (such as approving who can inhibit
|
|
* the token).
|
|
* m_nDomainHeight
|
|
* This instance variable contains the height of the domain from the
|
|
* point-of-view of this provider. If there are two layers of
|
|
* providers below this one, then the height will be two.
|
|
* m_DomainHeightList2
|
|
* This is a list of domain heights that were registered from all
|
|
* downward attachments. This allows the current provider to
|
|
* automatically update domain height when a downward attachment is
|
|
* lost.
|
|
* Random_Channel_Generator
|
|
* This object is used by this domain to generate random channel IDs.
|
|
*
|
|
* Private Member Functions:
|
|
* LockDomainParameters
|
|
* This member function is used to change the values of the locally
|
|
* maintained domain parameters structure. Passing NULL to it causes
|
|
* it to set a default set of parameters. The second parameter allows
|
|
* the caller to specify whether or not these new parameters are
|
|
* "locked" into the domain (meaning that they cannot change since they
|
|
* have been locked in by acceptance of the first connection).
|
|
* AllocateDynamicChannel
|
|
* This routine randomly selects a channel ID from the dynamic range.
|
|
* ValidateUserID
|
|
* This routine checks to see if the specified user is in the sub-tree
|
|
* of this domain. It can optionally check to see if the user is at
|
|
* a specific attachment in the sub-tree.
|
|
* PurgeDomain
|
|
* This routine purges the entire domain. This means terminating all
|
|
* attachments, and freeing up all resources. This results in
|
|
* returning the domain to its initialized state.
|
|
* DeleteAttachment
|
|
* This routine deletes a specified attachment and frees up all
|
|
* resources associated with that attachment.
|
|
* DeleteUser
|
|
* This routine deletes a user from the domain. This takes care of
|
|
* deleting the attachment too if this were a locally attach user.
|
|
* DeleteChannel
|
|
* This routine deletes a specific channel from the information base.
|
|
* DeleteToken
|
|
* This routine deletes a specific token from the information base.
|
|
* ReclaimResources
|
|
* This routine iterates through both the channel list and the token
|
|
* list, asking each if is still valid (and removing those that are
|
|
* not). This allows for automatic "garbage collection" when users
|
|
* or attachments are lost.
|
|
* MergeInformationBase
|
|
* This routine issues the appropriate merge requests to a pending
|
|
* top provider during a domain merger operation. It is also a state
|
|
* machine in that it remembers what has already been merged, so that
|
|
* the next time it is called, it can merge the next set of resources.
|
|
* SetMergeState
|
|
* This routine sets the merge state of the object, and if necessary,
|
|
* issues a MergeDomainIndication to all downward attachments.
|
|
* AddChannel
|
|
* This routine is used to add a new channel to the current channel
|
|
* list during a merge operation.
|
|
* AddToken
|
|
* This routine is used to add a new token to the current token list
|
|
* during a merge operation.
|
|
* CalculateDomainHeight
|
|
* This routine calculates the height of the current domain, and takes
|
|
* appropriate action if the height limit has been exceeded.
|
|
*
|
|
* Caveats:
|
|
* None.
|
|
*
|
|
* Author:
|
|
* James P. Galvin, Jr.
|
|
*/
|
|
|
|
#include "plgxprt.h"
|
|
|
|
/*
|
|
* External Interfaces
|
|
*/
|
|
|
|
|
|
/*
|
|
* These macros are used when requesting a random dynamic channel ID.
|
|
*/
|
|
#define DYNAMIC_CHANNEL_LOW_EXTENT 1001
|
|
#define DYNAMIC_CHANNEL_HIGH_EXTENT 65535L
|
|
|
|
/*
|
|
* These two static structure are used by all instances of the domain class
|
|
* as the minimum and maximum supported values for the domain parameters.
|
|
*/
|
|
static DomainParameters Static_Minimum_Domain_Parameters =
|
|
{
|
|
MINIMUM_MAXIMUM_CHANNELS,
|
|
MINIMUM_MAXIMUM_USERS,
|
|
MINIMUM_MAXIMUM_TOKENS,
|
|
MINIMUM_NUMBER_OF_PRIORITIES,
|
|
MINIMUM_MINIMUM_THROUGHPUT,
|
|
MINIMUM_MAXIMUM_DOMAIN_HEIGHT,
|
|
MINIMUM_MAXIMUM_PDU_SIZE,
|
|
MINIMUM_PROTOCOL_VERSION
|
|
};
|
|
|
|
static DomainParameters Static_Maximum_Domain_Parameters =
|
|
{
|
|
(UShort) MAXIMUM_MAXIMUM_CHANNELS,
|
|
(UShort) MAXIMUM_MAXIMUM_USERS,
|
|
(UShort) MAXIMUM_MAXIMUM_TOKENS,
|
|
MAXIMUM_NUMBER_OF_PRIORITIES,
|
|
MAXIMUM_MINIMUM_THROUGHPUT,
|
|
MAXIMUM_MAXIMUM_DOMAIN_HEIGHT,
|
|
MAXIMUM_MAXIMUM_PDU_SIZE,
|
|
MAXIMUM_PROTOCOL_VERSION
|
|
};
|
|
|
|
/*
|
|
* This is now set to 0 to indicate that this provider does not perform
|
|
* any type of throughput enforcement.
|
|
*/
|
|
#define DEFAULT_THROUGHPUT_ENFORCEMENT_INTERVAL 0
|
|
|
|
/*
|
|
* These macros define the number of buckets to used for each of the hash
|
|
* dictionaries maintained by this class.
|
|
*/
|
|
#define CHANNEL_LIST_NUMBER_OF_BUCKETS 16
|
|
|
|
|
|
/*
|
|
* Domain ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
* This is the constructor for the domain class. It merely initailizes
|
|
* all instance variables to indicate an "empty" state. It also sets
|
|
* the initial state of the domain parameters array.
|
|
*/
|
|
Domain::Domain()
|
|
:
|
|
m_AttachmentList(),
|
|
m_ChannelList2(CHANNEL_LIST_NUMBER_OF_BUCKETS),
|
|
m_TokenList2(),
|
|
m_DomainHeightList2(),
|
|
m_pConnToTopProvider(NULL),
|
|
Merge_State(MERGE_INACTIVE),
|
|
Outstanding_Merge_Requests(0),
|
|
Number_Of_Users(0),
|
|
Number_Of_Channels(0),
|
|
Number_Of_Tokens(0),
|
|
m_nDomainHeight(0)
|
|
{
|
|
/*
|
|
* Set the domain parameters to their default values.
|
|
*/
|
|
LockDomainParameters (NULL, FALSE);
|
|
}
|
|
|
|
/*
|
|
* ~Domain ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
* This is the destructor for the domain class. All it does is purge the
|
|
* entire domain, which means to return it to its initial state (all
|
|
* attachments are broken).
|
|
*/
|
|
Domain::~Domain ()
|
|
{
|
|
PurgeDomain (REASON_USER_REQUESTED);
|
|
}
|
|
|
|
/*
|
|
* BOOL IsTopProvider ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
* This routine returns TRUE if this is the top provider, and FALSE
|
|
* otherwise.
|
|
*/
|
|
|
|
/*
|
|
* Void GetDomainParameters ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
* This routine returns the currently active minimum and maximum domain
|
|
* parameter values (which will be different depending on whether or not
|
|
* the domain parameters have been locked yet).
|
|
*/
|
|
Void Domain::GetDomainParameters (
|
|
PDomainParameters domain_parameters,
|
|
PDomainParameters min_domain_parameters,
|
|
PDomainParameters max_domain_parameters)
|
|
{
|
|
/*
|
|
* Load the currently in-use set of domain parameters.
|
|
*/
|
|
if (domain_parameters != NULL)
|
|
*domain_parameters = Domain_Parameters;
|
|
|
|
/*
|
|
* See if domain parameters are already locked in for this domain.
|
|
*/
|
|
if (Domain_Parameters_Locked)
|
|
{
|
|
/*
|
|
* The domain parameters for this domain have already been locked
|
|
* during the creation of a previous connection. Return those values
|
|
* as both the minimum and maximum values (no deviation will be
|
|
* permitted).
|
|
*/
|
|
if (min_domain_parameters != NULL)
|
|
*min_domain_parameters = Domain_Parameters;
|
|
if (max_domain_parameters != NULL)
|
|
*max_domain_parameters = Domain_Parameters;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Domain parameters have not yet been locked. Therefore, return the
|
|
* minimum and maximum values imposed by this implementation.
|
|
*/
|
|
if (min_domain_parameters != NULL)
|
|
*min_domain_parameters = Static_Minimum_Domain_Parameters;
|
|
if (max_domain_parameters != NULL)
|
|
*max_domain_parameters = Static_Maximum_Domain_Parameters;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void BindConnAttmnt ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
* This routine allows an attachment to bind to the domain. It takes all
|
|
* actions appropriate to the addition of a new attachment (upward or
|
|
* downward).
|
|
*/
|
|
Void Domain::BindConnAttmnt (
|
|
PConnection pOrigConn,
|
|
BOOL upward_connection,
|
|
PDomainParameters domain_parameters)
|
|
{
|
|
CAttachment *pAtt;
|
|
PUser pUser;
|
|
PChannel channel;
|
|
PToken token;
|
|
|
|
/*
|
|
* Check the hierarchical direction of the requested attachment.
|
|
*/
|
|
if (upward_connection)
|
|
{
|
|
/*
|
|
* This is to be an upward connection. We must now check to make
|
|
* sure that we don't already have an upward connection.
|
|
*/
|
|
if (NULL == m_pConnToTopProvider)
|
|
{
|
|
/*
|
|
* This attachment is the new Top Provider.
|
|
*/
|
|
TRACE_OUT(("Domain::BindConnAttmnt: accepting upward attachment"));
|
|
m_pConnToTopProvider = pOrigConn;
|
|
|
|
/*
|
|
* Tell all channel objects who the new Top Provider is.
|
|
*/
|
|
m_ChannelList2.Reset();
|
|
while (NULL != (channel = m_ChannelList2.Iterate()))
|
|
{
|
|
channel->SetTopProvider(m_pConnToTopProvider);
|
|
}
|
|
|
|
/*
|
|
* Tell all token objects who the new Top Provider is.
|
|
*/
|
|
m_TokenList2.Reset();
|
|
while (NULL != (token = m_TokenList2.Iterate()))
|
|
{
|
|
token->SetTopProvider(m_pConnToTopProvider);
|
|
}
|
|
|
|
/*
|
|
* If the domain parameters have not yet been locked, then lock
|
|
* these into place.
|
|
*/
|
|
if (Domain_Parameters_Locked == FALSE)
|
|
{
|
|
TRACE_OUT(("Domain::BindConnAttmnt: locking domain parameters"));
|
|
LockDomainParameters (domain_parameters, TRUE);
|
|
|
|
/*
|
|
* Send a SetDomainParameters to each downward attachment.
|
|
* This will allow those objects to adjust their construction
|
|
* of send data PDUs to conform to the arbitrated maximum PDU
|
|
* size.
|
|
*/
|
|
m_AttachmentList.Reset();
|
|
while (NULL != (pUser = m_AttachmentList.IterateUser()))
|
|
{
|
|
pUser->SetDomainParameters(&Domain_Parameters);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Since we have bound to a provider above us, it is necessary to
|
|
* inform that provider of our height in the domain (otherwise
|
|
* the new Top Provider would have no way of knowing what the
|
|
* total height of the domain is). This is done by issuing an
|
|
* erect domain request upward.
|
|
*/
|
|
m_pConnToTopProvider->ErectDomainRequest(m_nDomainHeight, DEFAULT_THROUGHPUT_ENFORCEMENT_INTERVAL);
|
|
|
|
/*
|
|
* Now that this provider has become the former top provider of
|
|
* a lower domain, it is necessary to issue a plumb domain
|
|
* indication to all downward attachments. The primary reason
|
|
* for this is to assure that there are no cycles in the domain.
|
|
*/
|
|
m_AttachmentList.Reset();
|
|
while (NULL != (pAtt = m_AttachmentList.Iterate()))
|
|
{
|
|
pAtt->PlumbDomainIndication(Domain_Parameters.max_height);
|
|
}
|
|
|
|
/*
|
|
* We now have a new top provider, which means that we must begin
|
|
* an information base merger.
|
|
*/
|
|
MergeInformationBase ();
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* We already have an upward connection (or one pending).
|
|
* Therefore, this attachment must be rejected.
|
|
*/
|
|
ERROR_OUT(("Domain::BindConnAttmnt: domain not hierarchical"));
|
|
pOrigConn->DisconnectProviderUltimatum(REASON_PROVIDER_INITIATED);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* This is to be a downward connection. We must now check to see if
|
|
* we already have a record of the specified connection.
|
|
*/
|
|
if (! m_AttachmentList.FindConn(pOrigConn))
|
|
{
|
|
/*
|
|
* This does represent a new downward connection. So put it into
|
|
* the attachment list.
|
|
*/
|
|
TRACE_OUT(("Domain::BindConnAttmnt: accepting downward attachment"));
|
|
m_AttachmentList.AppendConn(pOrigConn);
|
|
|
|
/*
|
|
* If the domain parameters have not yet been locked, then lock
|
|
* these into place.
|
|
*/
|
|
if (Domain_Parameters_Locked == FALSE)
|
|
{
|
|
TRACE_OUT(("Domain::BindConnAttmnt: locking domain parameters"));
|
|
LockDomainParameters (domain_parameters, TRUE);
|
|
|
|
/*
|
|
* Send a SetDomainParameters to each downward attachment.
|
|
* This will allow those objects to adjust their construction
|
|
* of send data PDUs to conform to the arbitrated maximum PDU
|
|
* size.
|
|
*/
|
|
m_AttachmentList.Reset();
|
|
while (NULL != (pUser = m_AttachmentList.IterateUser()))
|
|
{
|
|
pUser->SetDomainParameters(&Domain_Parameters);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The attachment is already listed in the attachment list, so
|
|
* print an error and ignore the request.
|
|
*/
|
|
ERROR_OUT(("Domain::BindConnAttmnt: attachment already exists"));
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void PlumbDomainIndication ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
* This member function originates at a hgher provider and travels downward
|
|
* in the domain. Each provider examines the height limit, and disconnects
|
|
* if it is zero. If not, then the indication is forwarded downward.
|
|
*/
|
|
Void Domain::PlumbDomainIndication (
|
|
PConnection pOrigConn,
|
|
ULong height_limit)
|
|
{
|
|
/*
|
|
* Make sure that this indication is from the top provider.
|
|
*/
|
|
if (pOrigConn == m_pConnToTopProvider)
|
|
{
|
|
/*
|
|
* Check the height limit to determine whether this provider is too
|
|
* far from the top or not.
|
|
*/
|
|
if (height_limit != 0)
|
|
{
|
|
CAttachment *pAtt;
|
|
/*
|
|
* We are okay, so decrement the height limit and forward the
|
|
* indication to all downward attachments.
|
|
*/
|
|
TRACE_OUT(("Domain::PlumbDomainIndication: forwarding indication downward"));
|
|
height_limit--;
|
|
m_AttachmentList.Reset();
|
|
while (NULL != (pAtt = m_AttachmentList.Iterate()))
|
|
{
|
|
pAtt->PlumbDomainIndication(height_limit);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* We are too far from the top (which may indicate the existence
|
|
* of a cycle in the domain). It is therefore necessary to
|
|
* purge the entire domain (from this provider down).
|
|
*/
|
|
WARNING_OUT(("Domain::PlumbDomainIndication: purging domain"));
|
|
PurgeDomain (REASON_PROVIDER_INITIATED);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* This indication was received from an attachment that is unknown to
|
|
* this domain. Ignore it.
|
|
*/
|
|
ERROR_OUT(("Domain::PlumbDomainIndication: invalid originator=0x%p", pOrigConn));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void ErectDomainRequest ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
* This member function is called whenever a lower provider detects a
|
|
* change in their domain height. This will be due to someone below this
|
|
* provider creating or breaking a connection.
|
|
*/
|
|
Void Domain::ErectDomainRequest (
|
|
PConnection pOrigConn,
|
|
ULONG_PTR height_in_domain,
|
|
ULong)
|
|
{
|
|
/*
|
|
* Make sure that this request comes from an attachment that the local
|
|
* provider is aware of.
|
|
*/
|
|
if (m_AttachmentList.FindConn(pOrigConn))
|
|
{
|
|
/*
|
|
* Put the domain height into the domain height list, and then call
|
|
* the subroutine responsible for determining whether any action is
|
|
* required as a result of change in the height.
|
|
*/
|
|
TRACE_OUT(("Domain::ErectDomainRequest: processing request"));
|
|
m_DomainHeightList2.Append(pOrigConn, height_in_domain);
|
|
CalculateDomainHeight ();
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The attachment is unknown to this provider. Ignore the request.
|
|
*/
|
|
ERROR_OUT(("Domain::ErectDomainRequest: invalid originator=0x%p", pOrigConn));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void MergeChannelsRequest ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
* This public member function is called by a former top provider during
|
|
* a domain merge operation. It travels upward to the top provider of
|
|
* the combined domain, where the merge can be processed. Any providers
|
|
* that it travels through on the way must remember how to route the
|
|
* confirm back to the originator.
|
|
*/
|
|
Void Domain::MergeChannelsRequest (
|
|
PConnection pOrigConn,
|
|
CChannelAttributesList *merge_channel_list,
|
|
CChannelIDList *purge_channel_list)
|
|
{
|
|
PChannelAttributes merge_channel;
|
|
Channel_Type channel_type;
|
|
ChannelID channel_id;
|
|
PChannel channel;
|
|
CChannelAttributesList merge_confirm_list;
|
|
|
|
/*
|
|
* Make sure that this request is coming from a legitimate downward
|
|
* attachment before processing it.
|
|
*/
|
|
if (m_AttachmentList.FindConn(pOrigConn))
|
|
{
|
|
/*
|
|
* Is this the top provider. If so the request can be processed
|
|
* locally. If not, it must be forwarded towards the top provider.
|
|
*/
|
|
if (IsTopProvider())
|
|
{
|
|
/*
|
|
* Iterate through the merge channel list, admitting all channels
|
|
* that can be admitted.
|
|
*/
|
|
merge_channel_list->Reset();
|
|
while (NULL != (merge_channel = merge_channel_list->Iterate()))
|
|
{
|
|
/*
|
|
* Get the address of the next channel attributes structure
|
|
* in the list. Then get the type and the ID of the channel
|
|
* being merged.
|
|
*/
|
|
channel_type = merge_channel->channel_type;
|
|
switch (channel_type)
|
|
{
|
|
case STATIC_CHANNEL:
|
|
channel_id = merge_channel->
|
|
u.static_channel_attributes.channel_id;
|
|
break;
|
|
|
|
case USER_CHANNEL:
|
|
channel_id = merge_channel->
|
|
u.user_channel_attributes.user_id;
|
|
break;
|
|
|
|
case PRIVATE_CHANNEL:
|
|
channel_id = merge_channel->
|
|
u.private_channel_attributes.channel_id;
|
|
break;
|
|
|
|
case ASSIGNED_CHANNEL:
|
|
channel_id = merge_channel->
|
|
u.assigned_channel_attributes.channel_id;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Check to see if the channel being merged exists in the
|
|
* upper domain information base.
|
|
*/
|
|
if (NULL != (channel = m_ChannelList2.Find(channel_id)))
|
|
{
|
|
if ((channel_type == STATIC_CHANNEL) &&
|
|
(channel->GetChannelType () == STATIC_CHANNEL))
|
|
{
|
|
/*
|
|
* It is a static channel. This means that the merge
|
|
* is okay (merging static channels is no problem).
|
|
*/
|
|
TRACE_OUT(("Domain::MergeChannelsRequest: static channel merge successful"));
|
|
|
|
/*
|
|
* Static channels are automatically joined.
|
|
* Note that sending an initiator ID of 0 tells the
|
|
* channel object not to issue a ChannelJoinConfirm,
|
|
* which is inappropriate during a merge.
|
|
*/
|
|
channel->ChannelJoinRequest(pOrigConn, 0, 0);
|
|
|
|
/*
|
|
* Put the channel attributes structure into the
|
|
* merge confirm list, meaning that the information
|
|
* associated with the successful merge will be
|
|
* repeated in the subsequent confirm.
|
|
*/
|
|
merge_confirm_list.Append(merge_channel);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The channel being merged is an in-use dynamic
|
|
* channel. Therefore, it must be rejected (this is
|
|
* NOT permitted).
|
|
*/
|
|
WARNING_OUT(("Domain::MergeChannelsRequest: dynamic channel in use - rejecting merge"));
|
|
|
|
/*
|
|
* Add the channel ID to the list of those channels
|
|
* to be purged frmo the lower domain.
|
|
*/
|
|
purge_channel_list->Append(channel_id);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* If the channel does not exist in the upper domain at
|
|
* all, then add it to the upper domain.
|
|
*/
|
|
AddChannel(pOrigConn, merge_channel, &merge_confirm_list, purge_channel_list);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Send the appropriate merge channels confirm to the originating
|
|
* user.
|
|
*/
|
|
pOrigConn->MergeChannelsConfirm(&merge_confirm_list, purge_channel_list);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* If this is not the top provider, then add the requesting
|
|
* attachment to the merge queue (which is used to route
|
|
* confirms back later), and forward the request upward towards
|
|
* the top provier.
|
|
*/
|
|
TRACE_OUT(("Domain::MergeChannelsRequest: forwarding request to Top Provider"));
|
|
m_MergeQueue.Append(pOrigConn);
|
|
|
|
m_pConnToTopProvider->MergeChannelsRequest(merge_channel_list, purge_channel_list);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* This request was received from an attachment that is unknown to
|
|
* this domain.
|
|
*/
|
|
ERROR_OUT(("Domain::MergeChannelsRequest: invalid originator=0x%p", pOrigConn));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void MergeChannelsConfirm ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
* This public member function is called in response to a previous channels
|
|
* merge request. It is forwarded back down the hierarchy until it reaches
|
|
* the former top provider that initiated the request. That former top
|
|
* provider will use the information contained therein to determine
|
|
* whether the merge on a particular channel was successful or not. If
|
|
* it was not, then the channel is purged from the lower domain, and a
|
|
* purge channels indication is sent downward to let everyone in the lower
|
|
* domain know of this.
|
|
*/
|
|
Void Domain::MergeChannelsConfirm (
|
|
PConnection pOrigConn,
|
|
CChannelAttributesList *merge_channel_list,
|
|
CChannelIDList *purge_channel_list)
|
|
{
|
|
PConnection pConn;
|
|
PChannelAttributes merge_channel;
|
|
Channel_Type channel_type;
|
|
ChannelID channel_id;
|
|
PChannel channel;
|
|
BOOL joined;
|
|
CChannelAttributesList merge_confirm_list;
|
|
CUidList purge_user_list;
|
|
CChannelIDList purge_normal_list;
|
|
|
|
/*
|
|
* Verify that the confirm came from the top provider.
|
|
*/
|
|
if (pOrigConn == m_pConnToTopProvider)
|
|
{
|
|
/*
|
|
* Now check the merge state. If the state is inactive, then that
|
|
* means that this provider is an intermediate provider (i.e. a
|
|
* provider that lies between the top provider and the former top
|
|
* provider of the lower domain). If the state is not inactive, then
|
|
* this must be the former top provider of the lower domain.
|
|
*/
|
|
if (Merge_State == MERGE_INACTIVE)
|
|
{
|
|
/*
|
|
* This is a legitimate merge channels confirm. We must forward
|
|
* the confirm to the downward attachment that originated the
|
|
* merge channel request. We remember who this is by pulling
|
|
* out the first entry in the merge queue. Check to make sure
|
|
* that there is an entry in the merge queue.
|
|
*/
|
|
if (NULL != (pConn = m_MergeQueue.Get()))
|
|
{
|
|
/*
|
|
* Get the attachment that is to receive the confirm and verify
|
|
* that it is still connected (the connection could have been
|
|
* lost since the request was forwarded upward).
|
|
*/
|
|
if (m_AttachmentList.FindConn(pConn))
|
|
{
|
|
/*
|
|
* Iterate through the merge channel list, adding each of
|
|
* the channels it contains into the local information
|
|
* base.
|
|
*/
|
|
merge_channel_list->Reset();
|
|
while (NULL != (merge_channel = merge_channel_list->Iterate()))
|
|
{
|
|
/*
|
|
* Get the next channel to be merge and then get its
|
|
* channel ID.
|
|
*/
|
|
channel_type = merge_channel->channel_type;
|
|
switch (channel_type)
|
|
{
|
|
case STATIC_CHANNEL:
|
|
channel_id = merge_channel->
|
|
u.static_channel_attributes.channel_id;
|
|
joined = TRUE;
|
|
break;
|
|
|
|
case USER_CHANNEL:
|
|
channel_id = merge_channel->
|
|
u.user_channel_attributes.user_id;
|
|
joined = merge_channel->
|
|
u.user_channel_attributes.joined;
|
|
break;
|
|
|
|
case PRIVATE_CHANNEL:
|
|
channel_id = merge_channel->
|
|
u.private_channel_attributes.channel_id;
|
|
joined = merge_channel->
|
|
u.private_channel_attributes.joined;
|
|
break;
|
|
|
|
case ASSIGNED_CHANNEL:
|
|
channel_id = merge_channel->
|
|
u.assigned_channel_attributes.channel_id;
|
|
joined = TRUE;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* See if the channel already exists in the local
|
|
* information base.
|
|
*/
|
|
if (NULL != (channel = m_ChannelList2.Find(channel_id)))
|
|
{
|
|
/*
|
|
* If the attachment is joined to this channel,
|
|
* then join it at this level too. Note that
|
|
* sending an initiator ID of 0 tells the channel
|
|
* object not to issue a ChannelJoinConfirm, which
|
|
* would be inappropriate during a merge.
|
|
*/
|
|
TRACE_OUT(("Domain::MergeChannelsConfirm: attempting to join merged channel"));
|
|
if (joined)
|
|
channel->ChannelJoinRequest(pConn, 0, 0);
|
|
|
|
/*
|
|
* Add the channel to the merge confirm list so
|
|
* that it will automatically be forwarded
|
|
* downward.
|
|
*/
|
|
merge_confirm_list.Append(merge_channel);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The channel does not exist in the local
|
|
* information base, so add it.
|
|
*/
|
|
AddChannel(pConn, merge_channel, &merge_confirm_list, purge_channel_list);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Forward the merge channel confirm on to the attachment
|
|
* from which the request originated.
|
|
*/
|
|
pConn->MergeChannelsConfirm(&merge_confirm_list, purge_channel_list);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The attachment from which the merge request originated
|
|
* has been lost. It may be necessary to send something
|
|
* to the Top Provider in order to guarantee the integrity
|
|
* of the domain. In some cases it may be necessary to
|
|
* purge the domain.
|
|
*/
|
|
WARNING_OUT(("Domain::MergeChannelsConfirm: forwarding attachment lost"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* There is no outstanding merge request that can be used to
|
|
* direct the confirm. This will happen only if a confirm
|
|
* is received without a previous merge having been sent.
|
|
* The proper response should be to send a RejectUltimatum
|
|
* to the offending upward attachment.
|
|
*/
|
|
ERROR_OUT(("Domain::MergeChannelsConfirm: merge queue empty"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* This confirm should not be received unless there is at least
|
|
* one outstanding merge request. Check to make sure that this
|
|
* is so.
|
|
*/
|
|
if (Outstanding_Merge_Requests != 0)
|
|
{
|
|
/*
|
|
* If there are any entries in the purge channel list, then
|
|
* it is necessary to issue a purge channels indication to all
|
|
* downward attachments.
|
|
*/
|
|
if (purge_channel_list->IsEmpty() == FALSE)
|
|
{
|
|
ChannelID chid;
|
|
UserID uid;
|
|
/*
|
|
* Iterate through the list of channels to be purged,
|
|
* putting each channel into either the "user list" or the
|
|
* "normal list". This separation is necessary for lower
|
|
* providers to be able to issue the appropriate
|
|
* indications.
|
|
*/
|
|
purge_channel_list->Reset();
|
|
while (NULL != (channel_id = purge_channel_list->Iterate()))
|
|
{
|
|
/*
|
|
* Get the channel ID of the next channel to be purged.
|
|
*/
|
|
TRACE_OUT(("Domain::MergeChannelsConfirm: merge rejected on channel ID = %04X", (UINT) channel_id));
|
|
|
|
/*
|
|
* Make sure the channel still exists locally before
|
|
* trying to purge it.
|
|
*/
|
|
if (m_ChannelList2.Find(channel_id))
|
|
{
|
|
/*
|
|
* Determine what type of channel is being purged
|
|
* and add it to the appropriate list. These lists
|
|
* will be used when issuing the purge channels
|
|
* indication below.
|
|
*/
|
|
if (ValidateUserID (channel_id, NULL))
|
|
purge_user_list.Append(channel_id);
|
|
else
|
|
purge_normal_list.Append(channel_id);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The channel to be purged could not be found in
|
|
* the local domain.
|
|
*/
|
|
ERROR_OUT(("Domain::MergeChannelsConfirm: no such channel"));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This loop simply transmits a PurgeChannelsIndication to
|
|
* all downward attachments in the lower domain.
|
|
*/
|
|
CAttachment *pAtt;
|
|
m_AttachmentList.Reset();
|
|
while (NULL != (pAtt = m_AttachmentList.Iterate()))
|
|
{
|
|
pAtt->PurgeChannelsIndication(&purge_user_list, &purge_normal_list);
|
|
}
|
|
|
|
/*
|
|
* Iterate through the list of channels to be purged,
|
|
* deleting each channel.
|
|
*/
|
|
purge_normal_list.Reset();
|
|
while (NULL != (chid = purge_normal_list.Iterate()))
|
|
{
|
|
DeleteChannel(chid);
|
|
}
|
|
|
|
/*
|
|
* Iterate through the list of users to be purged, deleting
|
|
* each user.
|
|
*/
|
|
purge_user_list.Reset();
|
|
while (NULL != (uid = purge_user_list.Iterate()))
|
|
{
|
|
DeleteUser(uid);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Decrement the number of outstanding requests. If this
|
|
* was the last outstanding request, then go back to the
|
|
* merge state machine to see if there is anything left to
|
|
* do.
|
|
*/
|
|
if (--Outstanding_Merge_Requests == 0)
|
|
MergeInformationBase ();
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* There are no merge requests pending, so this errant confirm
|
|
* must be ignored.
|
|
*/
|
|
ERROR_OUT(("Domain::MergeChannelsConfirm: no outstanding merge requests"));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* This confirm was received from someone besides the top provider.
|
|
*/
|
|
ERROR_OUT(("Domain::MergeChannelsConfirm: invalid originator=0x%p", pOrigConn));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void PurgeChannelsIndication ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
* This public member function is called in response to channels being
|
|
* purged from the lower domain during an information base merge operation.
|
|
* The purge is forwarded downward to all attachments. Then the channel
|
|
* are deleted from the local information base. For each user channel
|
|
* all resources in use by that user will be reclaimed.
|
|
*/
|
|
Void Domain::PurgeChannelsIndication (
|
|
PConnection pOrigConn,
|
|
CUidList *purge_user_list,
|
|
CChannelIDList *purge_channel_list)
|
|
{
|
|
CAttachment *pAtt;
|
|
UserID uid;
|
|
ChannelID chid;
|
|
|
|
/*
|
|
* Make sure this indication came from the top provider.
|
|
*/
|
|
if (pOrigConn == m_pConnToTopProvider)
|
|
{
|
|
/*
|
|
* This loop re-transmits the purge channel indication to all
|
|
* downward attachments.
|
|
*/
|
|
m_AttachmentList.Reset();
|
|
while (NULL != (pAtt = m_AttachmentList.Iterate()))
|
|
{
|
|
pAtt->PurgeChannelsIndication(purge_user_list, purge_channel_list);
|
|
}
|
|
|
|
/*
|
|
* Iterate through the list of channels to be purged, deleting each
|
|
* channel.
|
|
*/
|
|
purge_channel_list->Reset();
|
|
while (NULL != (chid = purge_channel_list->Iterate()))
|
|
{
|
|
/*
|
|
* See if the specified channel is in the local information base.
|
|
* If it is not, ignore it (this is a normal condition during a
|
|
* purge operation).
|
|
*/
|
|
if (m_ChannelList2.Find(chid))
|
|
{
|
|
/*
|
|
* Check to see if the channel ID corresponds to a user ID
|
|
* channel. If it does, report the error and do nothing. If
|
|
* it is not a user ID channel, then delete the channel.
|
|
*/
|
|
if (ValidateUserID(chid, NULL) == FALSE)
|
|
{
|
|
/*
|
|
* Delete the channel.
|
|
*/
|
|
DeleteChannel(chid);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The specified channel is in the Channel List, but it
|
|
* does not refer to a user channel. This indicates that
|
|
* an error has occurred at the upward provider. Ignore
|
|
* the indication.
|
|
*/
|
|
ERROR_OUT(("Domain::PurgeChannelsIndication: UserChannel in purge_channel_list"));
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Iterate through the list of users to be purged, deleting
|
|
* each one.
|
|
*/
|
|
purge_user_list->Reset();
|
|
while (NULL != (uid = purge_user_list->Iterate()))
|
|
{
|
|
/*
|
|
* See if the specified user is in the local information base.
|
|
* If it is not, ignore it (this is a normal condition during a
|
|
* purge operation).
|
|
*/
|
|
if (m_ChannelList2.Find(uid))
|
|
{
|
|
/*
|
|
* Check to see if the user ID corresponds to a valid user in
|
|
* the sub-tree of this provider.
|
|
*/
|
|
if (ValidateUserID(uid, NULL))
|
|
{
|
|
/*
|
|
* Delete the user from the local information base.
|
|
*/
|
|
DeleteUser(uid);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The specified ID is in the Channel List, but it does not
|
|
* refer to a user channel. This indicates that an error
|
|
* has occurred at the upward provider. Ignore the
|
|
* indication.
|
|
*/
|
|
ERROR_OUT(("Domain::PurgeChannelsIndication: non-UserChannel in purge_user_list"));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* This indication was received from someone besides the top provider.
|
|
*/
|
|
ERROR_OUT(("Domain::PurgeChannelsIndication: invalid originator=0x%p", pOrigConn));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void MergeTokensRequest ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
* This public member function is called by a former top provider during
|
|
* a domain merge operation. It travels upward to the top provider of
|
|
* the combined domain, where the merge can be processed. Any providers
|
|
* that it travels through on the way must remember how to route the
|
|
* confirm back to the originator.
|
|
*/
|
|
Void Domain::MergeTokensRequest (
|
|
PConnection pOrigConn,
|
|
CTokenAttributesList *merge_token_list,
|
|
CTokenIDList *purge_token_list)
|
|
{
|
|
PTokenAttributes merge_token;
|
|
TokenState token_state;
|
|
TokenID token_id;
|
|
PToken token;
|
|
CUidList *owner_list;
|
|
UserID uid;
|
|
CTokenAttributesList merge_confirm_list;
|
|
|
|
/*
|
|
* Make sure that this request is coming from a legitimate downward
|
|
* attachment before processing it.
|
|
*/
|
|
if (m_AttachmentList.FindConn(pOrigConn))
|
|
{
|
|
/*
|
|
* Is this the top provider. If so the request can be processed
|
|
* locally. If not, it must be forwarded toward the top provider.
|
|
*/
|
|
if (IsTopProvider())
|
|
{
|
|
/*
|
|
* Iterate through the merge token list, attempting to add each
|
|
* token in sequence.
|
|
*/
|
|
merge_token_list->Reset();
|
|
while (NULL != (merge_token = merge_token_list->Iterate()))
|
|
{
|
|
/*
|
|
* Get the address of the structure containing the next token
|
|
* to merge. Then get the token ID from the structure.
|
|
*/
|
|
token_state = merge_token->token_state;
|
|
switch (token_state)
|
|
{
|
|
case TOKEN_GRABBED:
|
|
token_id = merge_token->
|
|
u.grabbed_token_attributes.token_id;
|
|
break;
|
|
|
|
case TOKEN_INHIBITED:
|
|
token_id = merge_token->
|
|
u.inhibited_token_attributes.token_id;
|
|
break;
|
|
|
|
case TOKEN_GIVING:
|
|
token_id = merge_token->
|
|
u.giving_token_attributes.token_id;
|
|
break;
|
|
|
|
case TOKEN_GIVEN:
|
|
token_id = merge_token->
|
|
u.given_token_attributes.token_id;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Check to see if the requested token is in the local
|
|
* information base.
|
|
*/
|
|
if (NULL != (token = m_TokenList2.Find(token_id)))
|
|
{
|
|
/*
|
|
* If the token already exists within this domain, then
|
|
* we need to compare the state of the local token and
|
|
* the state of the token being merged. If they are
|
|
* both inhibited, then the merge operation can proceed
|
|
* successfully. However, if either one is something
|
|
* besides inhibited, then the merge request will be
|
|
* rejected.
|
|
*/
|
|
if ((token_state == TOKEN_INHIBITED) &&
|
|
(token->GetTokenState () == TOKEN_INHIBITED))
|
|
{
|
|
/*
|
|
* Add each inhibiting user from the former lower
|
|
* domain to the token for this domain.
|
|
*/
|
|
TRACE_OUT(("Domain::MergeTokensRequest: merging inhibiting user IDs"));
|
|
owner_list = merge_token->
|
|
u.inhibited_token_attributes.inhibitors;
|
|
owner_list->Reset();
|
|
while (NULL != (uid = owner_list->Iterate()))
|
|
{
|
|
token->TokenInhibitRequest (NULL, uid, token_id);
|
|
}
|
|
|
|
/*
|
|
* Add the token attributes structure to the merge
|
|
* list, so that it will be included as part of the
|
|
* merge tokens confirm.
|
|
*/
|
|
merge_confirm_list.Append(merge_token);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The token is in use in the upper domain, and a merge
|
|
* is not possible. So add the token ID to the purge
|
|
* list, so that it will be purged from the lower
|
|
* domain.
|
|
*/
|
|
WARNING_OUT(("Domain::MergeTokensRequest: token in use - rejecting merge"));
|
|
purge_token_list->Append(token_id);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The token does not exist in the local information base.
|
|
* Attempt to add it.
|
|
*/
|
|
AddToken (merge_token, &merge_confirm_list,
|
|
purge_token_list);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Issue the merge tokens confirm to the originator of the request.
|
|
*/
|
|
pOrigConn->MergeTokensConfirm(&merge_confirm_list, purge_token_list);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* This must be an intermediate provider in the upper domain.
|
|
* Forward the request upward to be handled by the Top Provider
|
|
* of the upper domain. Also append the identity of the
|
|
* requestor to the merge queue, so that the pending response
|
|
* can be routed appropriately.
|
|
*/
|
|
TRACE_OUT(("Domain::MergeTokensRequest: forwarding request to Top Provider"));
|
|
m_MergeQueue.Append(pOrigConn);
|
|
m_pConnToTopProvider->MergeTokensRequest(merge_token_list, purge_token_list);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* This request is coming from a provider that is unknown in this
|
|
* domain. Simply ignore the request.
|
|
*/
|
|
ERROR_OUT(("Domain::MergeTokensRequest: invalid originator=0x%p", pOrigConn));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void MergeTokensConfirm ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
* This MCS command is initially invoked by the Top Provider of the upper
|
|
* domain during a domain merge operation. It travels downward until it
|
|
* reaches the former Top Provider of the lower domain. It contains
|
|
* notification of whether or not the merge of the token was successful.
|
|
*/
|
|
Void Domain::MergeTokensConfirm (
|
|
PConnection pOrigConn,
|
|
CTokenAttributesList *merge_token_list,
|
|
CTokenIDList *purge_token_list)
|
|
{
|
|
PConnection pConn;
|
|
PTokenAttributes merge_token;
|
|
TokenState token_state;
|
|
TokenID token_id;
|
|
PToken token;
|
|
CUidList *owner_list;
|
|
UserID uid;
|
|
CTokenAttributesList merge_confirm_list;
|
|
|
|
/*
|
|
* Check to make sure that it came from the Top Provider.
|
|
*/
|
|
if (pOrigConn == m_pConnToTopProvider)
|
|
{
|
|
/*
|
|
* Now check the merge state. If the state is inactive, then that
|
|
* means that this provider is an intermediate provider (i.e. a
|
|
* provider that lies between the top provider and the former top
|
|
* provider of the lower domain). If the state is not inactive, then
|
|
* this must be the former top provider of the lower domain.
|
|
*/
|
|
if (Merge_State == MERGE_INACTIVE)
|
|
{
|
|
/*
|
|
* Since this came from the Top Provider, it should be a response
|
|
* to an outstanding merge request that passed through this
|
|
* provider. If so, then the merge queue will not be empty. Check
|
|
* this before proceeding with the request.
|
|
*/
|
|
if (NULL != (pConn = m_MergeQueue.Get()))
|
|
{
|
|
/*
|
|
* Get the identity of the provider to which this confirm must
|
|
* forwarded.
|
|
*/
|
|
|
|
/*
|
|
* If the provider is still attached to this provider, then
|
|
* forward the merge confirm.
|
|
*/
|
|
if (m_AttachmentList.FindConn(pConn))
|
|
{
|
|
/*
|
|
* Iterate through the merge token list, attempting to add
|
|
* each token in sequence.
|
|
*/
|
|
merge_token_list->Reset();
|
|
while (NULL != (merge_token = merge_token_list->Iterate()))
|
|
{
|
|
/*
|
|
* Get the address of the structure containing the next
|
|
* token to merge. Then get the token ID from the
|
|
* structure.
|
|
*/
|
|
token_state = merge_token->token_state;
|
|
switch (token_state)
|
|
{
|
|
case TOKEN_GRABBED:
|
|
token_id = merge_token->
|
|
u.grabbed_token_attributes.token_id;
|
|
break;
|
|
|
|
case TOKEN_INHIBITED:
|
|
token_id = merge_token->
|
|
u.inhibited_token_attributes.token_id;
|
|
break;
|
|
|
|
case TOKEN_GIVING:
|
|
token_id = merge_token->
|
|
u.giving_token_attributes.token_id;
|
|
break;
|
|
|
|
case TOKEN_GIVEN:
|
|
token_id = merge_token->
|
|
u.given_token_attributes.token_id;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Check to see if the requested token is in the local
|
|
* information base.
|
|
*/
|
|
if (NULL != (token = m_TokenList2.Find(token_id)))
|
|
{
|
|
/*
|
|
* The token already exists in the information base
|
|
* of this intermediate provider. The only valid
|
|
* case where this could happen is if the token
|
|
* being merged is inhibited in both upper and
|
|
* lower domains. Check this.
|
|
*/
|
|
if ((token_state == TOKEN_INHIBITED) &&
|
|
(token->GetTokenState () == TOKEN_INHIBITED))
|
|
{
|
|
/*
|
|
* Add each inhibiting user from the former
|
|
* lower domain to the token for this domain.
|
|
*/
|
|
TRACE_OUT(("Domain::MergeTokensConfirm: merging inhibiting user IDs"));
|
|
owner_list = merge_token->
|
|
u.inhibited_token_attributes.inhibitors;
|
|
owner_list->Reset();
|
|
while (NULL != (uid = owner_list->Iterate()))
|
|
{
|
|
token->TokenInhibitRequest(NULL, uid, token_id);
|
|
}
|
|
|
|
/*
|
|
* Add the token attributes structure to the
|
|
* merge list, so that it will be included as
|
|
* part of the merge tokens confirm.
|
|
*/
|
|
merge_confirm_list.Append(merge_token);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The states of the tokens in the upper and
|
|
* lower domain are invalid. This should have
|
|
* been resolved by the Top Provider before
|
|
* issuing this merge request. Report the
|
|
* error and continue.
|
|
*/
|
|
ERROR_OUT(("Domain::MergeTokensConfirm: bad token in merge confirm"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The token does not exist in the local
|
|
* information base. Attempt to add it.
|
|
*/
|
|
AddToken (merge_token, &merge_confirm_list,
|
|
purge_token_list);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Forward merge confirm toward the former top provider
|
|
* of the lower domain.
|
|
*/
|
|
pConn->MergeTokensConfirm(&merge_confirm_list, purge_token_list);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The provider from which the outstanding request came
|
|
* must have been lost since the request was initially
|
|
* forwarded upward. We need to issue some notification
|
|
* of this upward, depending on the response within the
|
|
* confirm.
|
|
*/
|
|
ERROR_OUT(("Domain::MergeTokensConfirm: forwarding attachment lost"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* There is no outstanding request with which this confirm is
|
|
* associated. Something is wrong above. All this provider
|
|
* can do is ignore the errant confirm.
|
|
*/
|
|
ERROR_OUT (("Domain::MergeTokensConfirm: no outstanding merge requests"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* If we have received a confirm from the top provider, hen there
|
|
* should be at least one outstanding merge request. Make sure
|
|
* this is true before proceeding.
|
|
*/
|
|
if (Outstanding_Merge_Requests != 0)
|
|
{
|
|
/*
|
|
* If there are any entries in the purge token list, it is
|
|
* necessary to issue a purge tokens indication to all
|
|
* downward attachments.
|
|
*/
|
|
if (purge_token_list->IsEmpty() == FALSE)
|
|
{
|
|
/*
|
|
* Issue a PurgeTokensIndication downward to all
|
|
* attachments.
|
|
*/
|
|
CAttachment *pAtt;
|
|
m_AttachmentList.Reset();
|
|
while (NULL != (pAtt = m_AttachmentList.Iterate()))
|
|
{
|
|
pAtt->PurgeTokensIndication(this, purge_token_list);
|
|
}
|
|
|
|
/*
|
|
* Iterate through the list of tokens to be purged,
|
|
* removing each from the local information base.
|
|
*/
|
|
purge_token_list->Reset();
|
|
while (NULL != (token_id = purge_token_list->Iterate()))
|
|
{
|
|
DeleteToken (token_id);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Decrement the number of outstanding merge requests. If
|
|
* there are now no more, then proceed to the next state
|
|
* in the merger state machine.
|
|
*/
|
|
if (--Outstanding_Merge_Requests == 0)
|
|
MergeInformationBase ();
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* We have received a merge confirm when there are no
|
|
* outstanding merge requests. Ignore the confirm.
|
|
*/
|
|
ERROR_OUT(("Domain::MergeTokensConfirm: no outstanding merge requests"));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* This merge confirm has been received from someone besides the top
|
|
* provider. Ignore it.
|
|
*/
|
|
ERROR_OUT(("Domain::MergeTokensConfirm: invalid originator=0x%p", pOrigConn));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void PurgeTokensIndication ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
* This member function is first invoked by the former Top Provider of
|
|
* the lower domain during a merge operation. This indicates that a
|
|
* token merge into the upper domain was rejected. After verifying that
|
|
* this MCS command is valid, is should simply be repeated downward to all
|
|
* attachments.
|
|
*/
|
|
Void Domain::PurgeTokensIndication (
|
|
PConnection pOrigConn,
|
|
CTokenIDList *purge_token_list)
|
|
{
|
|
CAttachment *pAtt;
|
|
TokenID token_id;
|
|
|
|
/*
|
|
* Check to make sure that this MCS command came from the Top Provider.
|
|
*/
|
|
if (pOrigConn == m_pConnToTopProvider)
|
|
{
|
|
/*
|
|
* This is a valid command. Iterate through the attachment list,
|
|
* forwarding the command to everyone below this provider in the
|
|
* domain hierarchy.
|
|
*/
|
|
TRACE_OUT(("Domain::PurgeTokensIndication: forwarding indication to all attachments"));
|
|
m_AttachmentList.Reset();
|
|
while (NULL != (pAtt = m_AttachmentList.Iterate()))
|
|
{
|
|
pAtt->PurgeTokensIndication(this, purge_token_list);
|
|
}
|
|
|
|
/*
|
|
* Iterate through the list of tokens to be purged, deleting each one.
|
|
*/
|
|
purge_token_list->Reset();
|
|
while (NULL != (token_id = purge_token_list->Iterate()))
|
|
{
|
|
/*
|
|
* See if the specified token is in the local information base.
|
|
* If it is not ignore it (this is a normal condition during a
|
|
* purge operation). If it is, then delete it.
|
|
*/
|
|
if (m_TokenList2.Find(token_id))
|
|
DeleteToken (token_id);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* This indication was received from someone besides the Top Provider.
|
|
* Ignore it.
|
|
*/
|
|
ERROR_OUT(("Domain::PurgeTokensIndication: invalid originator=0x%p", pOrigConn));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void DisconnectProviderUltimatum ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
* This MCS command is generated whenever an attachment becomes invalid.
|
|
* The local provider must respond by breaking all ties to the attachment.
|
|
* If the attachment is to the Top Provider, this will cause the domain
|
|
* to completely eradicate itself (return to the initialized state).
|
|
*
|
|
* Note that when an attachment is lost, it is not removed from the
|
|
* merge queue. This allows this provider to continue to route
|
|
* outstanding merge confirms appropriately, even when one of the
|
|
* attachments is lost. Removing the attachments from the merge queue
|
|
* here will result in outstanding merge confirms being directed to the
|
|
* wrong attachments.
|
|
*/
|
|
Void Domain::DisconnectProviderUltimatum (
|
|
CAttachment *pOrigAtt,
|
|
Reason reason)
|
|
{
|
|
/*
|
|
* If we lost the connection to the Top Provider, we have no choice but
|
|
* to purge the entire domain. Ways of preventing this drastic action
|
|
* are being studied, but for now this implementation conforms to the
|
|
* definition of T.125.
|
|
*/
|
|
if (pOrigAtt == m_pConnToTopProvider)
|
|
{
|
|
ASSERT(pOrigAtt->IsConnAttachment());
|
|
TRACE_OUT(("Domain::DisconnectProviderUltimatum: purging entire domain"));
|
|
m_pConnToTopProvider = NULL;
|
|
PurgeDomain (reason);
|
|
}
|
|
|
|
/*
|
|
* If we lose a downward attachment, then we must free up all resources
|
|
* associated with that attachment. This is handled by a private member
|
|
* function.
|
|
*/
|
|
if (m_AttachmentList.Find(pOrigAtt))
|
|
{
|
|
TRACE_OUT(("Domain::DisconnectProviderUltimatum: deleting downward attachment=0x%p", pOrigAtt));
|
|
DeleteAttachment(pOrigAtt, reason);
|
|
}
|
|
|
|
/*
|
|
* If we lost an attachment that has an outstanding AttachUserRequest,
|
|
* go ahead and remove it from the attach user queue. Note that this
|
|
* works differently from the merge queue. With AttachUserConfirms, it
|
|
* makes no difference what order they are processed in, so we can do
|
|
* this. With Merge???Confirms, they MUST be processed in order, so
|
|
* we leave the lost attachment in the queue, and allow the confirm
|
|
* command handler to deal with the fact that the attachment is no
|
|
* longer valid.
|
|
*/
|
|
while (m_AttachUserQueue.Remove(pOrigAtt))
|
|
{
|
|
TRACE_OUT(("Domain::DisconnectProviderUltimatum: pending user attachment deleted=0x%p", pOrigAtt));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void RejectUltimatum ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
* This member function is called when a provider detects a PDU that it
|
|
* cannot correctly process, the default behavior is to disconnect the
|
|
* connection that conveys the PDU.
|
|
*/
|
|
Void Domain::RejectUltimatum (
|
|
PConnection pOrigConn,
|
|
Diagnostic,
|
|
PUChar,
|
|
ULong)
|
|
{
|
|
/*
|
|
* Send a disconnect provider ultimatum to the attachment that has accused
|
|
* us of wrongdoing.
|
|
*/
|
|
pOrigConn->DisconnectProviderUltimatum(REASON_PROVIDER_INITIATED);
|
|
|
|
/*
|
|
* Simulate the reception of a disconnect provider ultimatum from that
|
|
* same attachment. This will cause the connection to be cleanly broken
|
|
* on both sides.
|
|
*/
|
|
DisconnectProviderUltimatum(pOrigConn, REASON_PROVIDER_INITIATED);
|
|
}
|
|
|
|
/*
|
|
* Void AttachUserRequest ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
* This MCS command is initiated by a user attachment, when a new user
|
|
* wishes to attach to this domain. It is forwarded upward to the Top
|
|
* Provider of the domain, who ultimately has to process the request.
|
|
*/
|
|
Void Domain::AttachUserRequest (
|
|
CAttachment *pOrigAtt)
|
|
{
|
|
UserID user_id;
|
|
PChannel channel;
|
|
|
|
/*
|
|
* Check to see if this is the Top Provider or not. If it is, then the
|
|
* request can be processed locally. If not, then the request must be
|
|
* forwarded upward to the Top Provider.
|
|
*/
|
|
if (IsTopProvider())
|
|
{
|
|
/*
|
|
* This is the Top Provider, so process the request here. Check to
|
|
* see if the arbitrated domain parameters allow the addition of
|
|
* a new user to the domain.
|
|
*/
|
|
if (Number_Of_Users < Domain_Parameters.max_user_ids)
|
|
{
|
|
/*
|
|
* Also check to see if the arbitrated domain parameters allow the
|
|
* addition of a new channel to the domain (since a user is also
|
|
* channel).
|
|
*/
|
|
if (Number_Of_Channels < Domain_Parameters.max_channel_ids)
|
|
{
|
|
/*
|
|
* Adding a new user is not a problem. Get a unique ID to use
|
|
* as the user ID, and then create a new UserChannel object.
|
|
*/
|
|
user_id = AllocateDynamicChannel ();
|
|
DBG_SAVE_FILE_LINE
|
|
channel = new UserChannel(user_id, pOrigAtt, this, m_pConnToTopProvider,
|
|
&m_ChannelList2, &m_AttachmentList);
|
|
if (channel != NULL)
|
|
{
|
|
/*
|
|
* Add the new channel object to the channel list. Note
|
|
* that it is not necessary for this object to issue the
|
|
* attach user confirm, because that was handled by the
|
|
* constructor of the UserChannel object.
|
|
*/
|
|
TRACE_OUT(("Domain::AttachUserRequest: adding user ID = %04X", (UINT) user_id));
|
|
m_ChannelList2.Insert(user_id, channel);
|
|
Number_Of_Users++;
|
|
Number_Of_Channels++;
|
|
|
|
/*
|
|
* If this represents an attachment that did not previously
|
|
* exist, then this must be a local user attachment. Add
|
|
* it to the attachment list as such.
|
|
*/
|
|
if (! m_AttachmentList.Find(pOrigAtt))
|
|
{
|
|
ASSERT(pOrigAtt->IsUserAttachment());
|
|
m_AttachmentList.Append(pOrigAtt);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The allocation of the UserChannel object failed. Issue
|
|
* an unsuccessful attach user confirm.
|
|
*/
|
|
ERROR_OUT(("Domain::AttachUserRequest: user allocation failed"));
|
|
pOrigAtt->AttachUserConfirm(RESULT_UNSPECIFIED_FAILURE, 0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The negotiated domain parameters will not allow a new
|
|
* channel to be added to the domain. Reject the request.
|
|
*/
|
|
ERROR_OUT(("Domain::AttachUserRequest: too many channels"));
|
|
pOrigAtt->AttachUserConfirm(RESULT_TOO_MANY_CHANNELS, 0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The negotiated domain parameters will not allow a new user
|
|
* to be added to the domain. Reject the request.
|
|
*/
|
|
ERROR_OUT(("Domain::AttachUserRequest: too many users"));
|
|
pOrigAtt->AttachUserConfirm(RESULT_TOO_MANY_USERS, 0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* This is not the Top Provider, so the request must be forwarded
|
|
* upward toward the Top Provider. Add the originator of the request
|
|
* to the attach user queue, so that this provider can properly route
|
|
* the returning confirm (when it arrives).
|
|
*/
|
|
TRACE_OUT(("Domain::AttachUserRequest: adding attachment to attach user queue"));
|
|
m_AttachUserQueue.Append(pOrigAtt);
|
|
|
|
m_pConnToTopProvider->AttachUserRequest();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void AttachUserConfirm ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
* This MCS command is initially generated by the Top Provider upon
|
|
* receipt of an AttachUserRequest. It contains the result of that
|
|
* request. If the result is successful, then it also contains the user
|
|
* ID for the new user. This confirm needs to be routed all the was back
|
|
* to the user attachment that originated the request.
|
|
*/
|
|
Void Domain::AttachUserConfirm (
|
|
PConnection pOrigConn,
|
|
Result result,
|
|
UserID uidInitiator)
|
|
{
|
|
CAttachment *pAtt;
|
|
PChannel channel;
|
|
CUidList detach_user_list;
|
|
|
|
/*
|
|
* Make sure that the request originated with the Top Provider.
|
|
*/
|
|
if (pOrigConn == m_pConnToTopProvider)
|
|
{
|
|
/*
|
|
* The reception of this confirm means that there should be an
|
|
* outstanding request. Make sure this is the case before proceeding.*
|
|
*/
|
|
if (NULL != (pAtt = m_AttachUserQueue.Get()))
|
|
{
|
|
/*
|
|
* There is an outstanding request. Get the identity of the
|
|
* attachment from which the request originated.
|
|
*/
|
|
|
|
/*
|
|
* If the result was successful, then it is necessary for this
|
|
* provider to create a UserChannel object in the local information
|
|
* base for the new user.
|
|
*/
|
|
if (result == RESULT_SUCCESSFUL)
|
|
{
|
|
/*
|
|
* Make sure the channel ID is not already in use before
|
|
* proceeding.
|
|
*/
|
|
if (! m_ChannelList2.Find(uidInitiator))
|
|
{
|
|
/*
|
|
* Create a new UserChannel object, using the ID generated
|
|
* by the Top Provider.
|
|
*/
|
|
DBG_SAVE_FILE_LINE
|
|
channel = new UserChannel(uidInitiator, pAtt, this, m_pConnToTopProvider,
|
|
&m_ChannelList2, &m_AttachmentList);
|
|
if (channel != NULL)
|
|
{
|
|
/*
|
|
* Add the UserChannel object to the channel list.
|
|
*/
|
|
TRACE_OUT(("Domain::AttachUserConfirm: adding user ID = %04X", (UINT) uidInitiator));
|
|
if(m_ChannelList2.Insert(uidInitiator, channel))
|
|
{
|
|
Number_Of_Users++;
|
|
Number_Of_Channels++;
|
|
|
|
/*
|
|
* If the user's attachment is not already in the
|
|
* attachment list, then this must be a new local
|
|
* attachment. Add it to the attachment list as such.
|
|
*/
|
|
if (! m_AttachmentList.Find(pAtt))
|
|
{
|
|
ASSERT(pAtt->IsUserAttachment());
|
|
m_AttachmentList.Append(pAtt);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
delete channel;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The local provider was unable to allocate the
|
|
* UserChannel object. This means that the new user
|
|
* must be removed from the domain. To do this, send
|
|
* a DetachUserRequest to the Top Provider and an
|
|
* unsuccessful AttachUserConfirm to the originator
|
|
* of the request.
|
|
*/
|
|
ERROR_OUT(("Domain::AttachUserConfirm: user allocation failed"));
|
|
detach_user_list.Append(uidInitiator);
|
|
m_pConnToTopProvider->DetachUserRequest(REASON_PROVIDER_INITIATED, &detach_user_list);
|
|
pAtt->AttachUserConfirm(RESULT_UNSPECIFIED_FAILURE, 0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The ID associated with this confirm is already in use.
|
|
* This indicates that something is wrong above. This
|
|
* provider has no choice but to ignore the confirm.
|
|
*/
|
|
WARNING_OUT(("Domain::AttachUserConfirm: channel ID already in use"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Since the result of the attach was not successful, this
|
|
* provider does not have to add anything to its channel list.
|
|
* The only required action is to forward the confirm to the
|
|
* originating user.
|
|
*/
|
|
TRACE_OUT(("Domain::AttachUserConfirm: echoing failed confirm"));
|
|
pAtt->AttachUserConfirm(result, uidInitiator);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The attach user queue is empty. This probably indicates that
|
|
* the connection to the user who originated the request was lost
|
|
* before the confirm got back. This provider doesn't need to
|
|
* do anything except issue a DetachUserRequest (if the confirm
|
|
* indicates that the attach operation was successful).
|
|
*/
|
|
WARNING_OUT(("Domain::AttachUserConfirm: attach user queue empty"));
|
|
|
|
if (result == RESULT_SUCCESSFUL)
|
|
{
|
|
TRACE_OUT (("Domain::AttachUserConfirm: sending DetachUserRequest"));
|
|
detach_user_list.Append(uidInitiator);
|
|
m_pConnToTopProvider->DetachUserRequest(REASON_DOMAIN_DISCONNECTED, &detach_user_list);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* This confirm was received from someone besides the Top Provider.
|
|
* Ignore it.
|
|
*/
|
|
ERROR_OUT(("Domain::AttachUserConfirm: invalid originator=0x%p", pOrigConn));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void DetachUserRequest ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
* This MCS command is initiated by a user attachment that wishes to leave
|
|
* the domain. After validation, delete the user from the information base
|
|
* pass the request upward.
|
|
*/
|
|
Void Domain::DetachUserRequest (
|
|
CAttachment *pOrigAtt,
|
|
Reason reason,
|
|
CUidList *user_id_list)
|
|
{
|
|
UserID uid;
|
|
CUidList detach_user_list;
|
|
|
|
/*
|
|
* Iterate through the list of users named to be deleted.
|
|
*/
|
|
user_id_list->Reset();
|
|
while (NULL != (uid = user_id_list->Iterate()))
|
|
{
|
|
/*
|
|
* Make sure the user really exists in the sub-tree from which this
|
|
* request originated.
|
|
*/
|
|
if (ValidateUserID(uid, pOrigAtt))
|
|
{
|
|
/*
|
|
* Delete the user from the local information base.
|
|
*/
|
|
DeleteUser(uid);
|
|
|
|
/*
|
|
* Put the user ID into the list of validated user IDs.
|
|
*/
|
|
detach_user_list.Append(uid);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* There is no such user in the sub-tree from which this request
|
|
* originated.
|
|
*/
|
|
WARNING_OUT(("Domain::DetachUserRequest: invalid user ID"));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check to see if there are any users to be deleted. If so, then process
|
|
* the request.
|
|
*/
|
|
if (detach_user_list.IsEmpty() == FALSE)
|
|
{
|
|
/*
|
|
* Check to see if this is the Top Provider.
|
|
*/
|
|
if (IsTopProvider())
|
|
{
|
|
/*
|
|
* This is the Top Provider, so issue a detach user indication to
|
|
* all downward attachments.
|
|
*/
|
|
TRACE_OUT(("Domain::DetachUserRequest: sending DetachUserIndication to all attachments"));
|
|
CAttachment *pAtt;
|
|
m_AttachmentList.Reset();
|
|
while (NULL != (pAtt = m_AttachmentList.Iterate()))
|
|
{
|
|
pAtt->DetachUserIndication(reason, &detach_user_list);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* This is not the Top Provider, so forward the detach user
|
|
* request upward.
|
|
*/
|
|
TRACE_OUT(("Domain::DetachUserRequest: forwarding DetachUserRequest to Top Provider"));
|
|
m_pConnToTopProvider->DetachUserRequest(reason, &detach_user_list);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The user ID list contained no valid entries, so ignore the request.
|
|
*/
|
|
ERROR_OUT(("Domain::DetachUserRequest: no valid user IDs"));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void DetachUserIndication ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
* This MCS command is initially sent by the Top Provider in response to
|
|
* a user detaching from the domain (willingly or otherwise). It is
|
|
* forwarded downward in the hierarchy where it will eventually reach all
|
|
* providers and their user attachments.
|
|
*/
|
|
Void Domain::DetachUserIndication (
|
|
PConnection pOrigConn,
|
|
Reason reason,
|
|
CUidList *user_id_list)
|
|
{
|
|
UserID uid;
|
|
|
|
/*
|
|
* Verify that the indication came from the Top Provider.
|
|
*/
|
|
if (pOrigConn == m_pConnToTopProvider)
|
|
{
|
|
/*
|
|
* The first thing to do is repeat the indication to all downward
|
|
* attachments. Keep in mind that this sends the detach indication
|
|
* to user attachments as well as remote connections.
|
|
*/
|
|
TRACE_OUT(("Domain::DetachUserIndication: forwarding DetachUserIndication to all attachments"));
|
|
CAttachment *pAtt;
|
|
m_AttachmentList.Reset();
|
|
while (NULL != (pAtt = m_AttachmentList.Iterate()))
|
|
{
|
|
pAtt->DetachUserIndication(reason, user_id_list);
|
|
}
|
|
|
|
/*
|
|
* Iterate through the list of users, deleting those that are in
|
|
* the sub-tree of this provider.
|
|
*/
|
|
user_id_list->Reset();
|
|
while (NULL != (uid = user_id_list->Iterate()))
|
|
{
|
|
/*
|
|
* Check to see if this user is somewhere in the sub-tree of this
|
|
* provider. If so it is necessary to delete the user channel from
|
|
* the channel list. Note that it is perfectly normal to receive a
|
|
* detach user indication for a user that is not in the sub-tree of
|
|
* the receiving provider.
|
|
*/
|
|
if (ValidateUserID(uid, NULL) )
|
|
{
|
|
/*
|
|
* Delete the user from the local information base.
|
|
*/
|
|
DeleteUser(uid);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* This indication was received from someone besides the Top Provider.
|
|
* Ignore the indication.
|
|
*/
|
|
ERROR_OUT(("Domain::DetachUserIndication: invalid originator=0x%p", pOrigConn));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void ChannelJoinRequest ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
* This MCS command is initially sent by a user attachment that wishes
|
|
* to join a channel. It flows upward in the hierarchy until it reaches
|
|
* a provider who is already joined to the channel. That provider (which
|
|
* is not necessarily the Top Provider), will issue a channel join
|
|
* confirm, indicating whether or not the join was successful.
|
|
*/
|
|
Void Domain::ChannelJoinRequest (
|
|
CAttachment *pOrigAtt,
|
|
UserID uidInitiator,
|
|
ChannelID channel_id)
|
|
{
|
|
PChannel channel;
|
|
ChannelID requested_id;
|
|
|
|
/*
|
|
* Make sure the requesting user really exists in the sub-tree from which
|
|
* this request originated.
|
|
*/
|
|
if (ValidateUserID(uidInitiator, pOrigAtt))
|
|
{
|
|
/*
|
|
* See if the channel already exists in the local information base.
|
|
* If so, then let the Channel object handle the join request.
|
|
*/
|
|
if (NULL != (channel = m_ChannelList2.Find(channel_id)))
|
|
{
|
|
TRACE_OUT(("Domain::ChannelJoinRequest: sending join request to channel object"));
|
|
channel->ChannelJoinRequest(pOrigAtt, uidInitiator, channel_id);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The channel does not already exist in the channel list. Check
|
|
* to see if this is the Top Provider. If so, we can try to
|
|
* add the channel to the list. If this is not the Top Provider,
|
|
* then we simply forward the request upward.
|
|
*/
|
|
if (IsTopProvider())
|
|
{
|
|
/*
|
|
* Save the value of the channel the user originally attempted
|
|
* to join. This may change if this is a request to join
|
|
* channel 0 (an assigned channel).
|
|
*/
|
|
requested_id = channel_id;
|
|
|
|
/*
|
|
* We already know the channel does not exist in the channel
|
|
* list. Therefore, this is a valid request only if the
|
|
* channel being joined is a static channel or channel 0 (which
|
|
* is interpreted as a request for an assigned channel).
|
|
* Dynamic channels (those above 1000) can only be joined if
|
|
* they already exist.
|
|
*/
|
|
if (requested_id <= 1000)
|
|
{
|
|
/*
|
|
* See if the arbitrated domain parameters will allow the
|
|
* addition of a new channel.
|
|
*/
|
|
if (Number_Of_Channels < Domain_Parameters.max_channel_ids)
|
|
{
|
|
/*
|
|
* If this is a request for an assigned channel, then
|
|
* allocate a random channel ID in the dynamic range.
|
|
* Then create a new Channel object.
|
|
*/
|
|
if (requested_id == 0)
|
|
channel_id = AllocateDynamicChannel ();
|
|
DBG_SAVE_FILE_LINE
|
|
channel = new Channel(channel_id, this, m_pConnToTopProvider,
|
|
&m_ChannelList2, &m_AttachmentList);
|
|
if (channel != NULL)
|
|
{
|
|
/*
|
|
* The creation of the new channel was successful.
|
|
* Add it to the channel list.
|
|
*/
|
|
TRACE_OUT(("Domain::ChannelJoinRequest: adding channel ID = %04X", (UINT) channel_id));
|
|
m_ChannelList2.Insert(channel_id, channel);
|
|
Number_Of_Channels++;
|
|
|
|
/*
|
|
* When new channels are created, they are
|
|
* initially empty. So we must join the
|
|
* originating attachment to the newly created
|
|
* attachment. This will also cause a channel
|
|
* join confirm to be issued to the originator.
|
|
*/
|
|
channel->ChannelJoinRequest(pOrigAtt, uidInitiator, requested_id);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Allocation of the Channel object failed. We
|
|
* must therefore issue an unsuccessful channel
|
|
* join confirm to the originating attachment.
|
|
*/
|
|
ERROR_OUT(("Domain::ChannelJoinRequest: channel allocation failed"));
|
|
pOrigAtt->ChannelJoinConfirm(RESULT_UNSPECIFIED_FAILURE, uidInitiator, requested_id, 0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Domain parmeters will not allow the addition of
|
|
* any more channels. Fail the request.
|
|
*/
|
|
ERROR_OUT(("Domain::ChannelJoinRequest: join denied - too many channels"));
|
|
pOrigAtt->ChannelJoinConfirm(RESULT_TOO_MANY_CHANNELS, uidInitiator, requested_id, 0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* There has been an attempt to join a dynamic channel
|
|
* that doesn't already exist. This is not allowed, so
|
|
* fail the request.
|
|
*/
|
|
WARNING_OUT(("Domain::ChannelJoinRequest: attempt to join non-existent dynamic channel"));
|
|
pOrigAtt->ChannelJoinConfirm(RESULT_NO_SUCH_CHANNEL, uidInitiator, requested_id, 0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The channel does not exist locally, and this is not the
|
|
* Top Provider. That means this is someone else problem.
|
|
* Issue the request upward toward the Top Provider.
|
|
*/
|
|
TRACE_OUT(("Domain::ChannelJoinRequest: forwarding join request to Top Provider"));
|
|
m_pConnToTopProvider->ChannelJoinRequest(uidInitiator, channel_id);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* There is no such user in the sub-tree from which this request
|
|
* originated. Ignore the request.
|
|
*/
|
|
ERROR_OUT(("Domain::ChannelJoinRequest: invalid originator=0x%p", pOrigAtt));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void ChannelJoinConfirm ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
* This MCS command originates from a provider who receives a channel
|
|
* join request, and has enough information to respond. This is not
|
|
* necessarily the Top Provider. An intermediate can respond if the
|
|
* channel exists in its information base. This confirm is forwarded
|
|
* back to the original requestor, letting it know whether or not the
|
|
* join was successful.
|
|
*/
|
|
Void Domain::ChannelJoinConfirm (
|
|
PConnection pOrigConn,
|
|
Result result,
|
|
UserID uidInitiator,
|
|
ChannelID requested_id,
|
|
ChannelID channel_id)
|
|
{
|
|
PChannel channel;
|
|
CChannelIDList channel_leave_list;
|
|
|
|
/*
|
|
* Verify that the confirm came from the Top Provider.
|
|
*/
|
|
if (pOrigConn == m_pConnToTopProvider)
|
|
{
|
|
/*
|
|
* Make sure that the requesting user is still somewhere in the
|
|
* sub-tree of this provider.
|
|
*/
|
|
if (ValidateUserID (uidInitiator, NULL) )
|
|
{
|
|
/*
|
|
* Found out which downward attachment leads to the requesting
|
|
* user.
|
|
*/
|
|
if (NULL != (channel = m_ChannelList2.Find(uidInitiator)))
|
|
{
|
|
CAttachment *pAtt = channel->GetAttachment();
|
|
ASSERT(pAtt);
|
|
/*
|
|
* Was the result successful. If is was, then the local provider
|
|
* needs to make sure the channel is in the local channel list.
|
|
* If its not already there, it will have to be created.
|
|
*/
|
|
if (result == RESULT_SUCCESSFUL)
|
|
{
|
|
/*
|
|
* See if the named channel already exists in the channel list.
|
|
*/
|
|
if (NULL != (channel = m_ChannelList2.Find(channel_id)))
|
|
{
|
|
/*
|
|
* A Channel object already exists for the named channel.
|
|
* Let it handle the join confirm.
|
|
*/
|
|
TRACE_OUT(("Domain::ChannelJoinConfirm: sending confirm to channel object"));
|
|
channel->ChannelJoinConfirm(pAtt, result, uidInitiator, requested_id, channel_id);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The new channel will have to be created.
|
|
*/
|
|
DBG_SAVE_FILE_LINE
|
|
channel = new Channel(channel_id, this, m_pConnToTopProvider,
|
|
&m_ChannelList2, &m_AttachmentList);
|
|
if (channel != NULL)
|
|
{
|
|
/*
|
|
* Add the newly created channel to the channel list,
|
|
* and then let the Channel object handle the join
|
|
* confirm.
|
|
*/
|
|
TRACE_OUT(("Domain::ChannelJoinConfirm: adding channel ID = %04X", (UINT) channel_id));
|
|
m_ChannelList2.Insert(channel_id, channel);
|
|
Number_Of_Channels++;
|
|
|
|
channel->ChannelJoinConfirm(pAtt, result, uidInitiator, requested_id, channel_id);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The allocation of the Channel object failed. It
|
|
* is therefore necessary for this provider to cause
|
|
* the channel to be deleted from the domain. It
|
|
* does this by issuing a channel leave request to
|
|
* the Top Provider, and an unsuccessful channel
|
|
* join confirm to the originating user.
|
|
*/
|
|
ERROR_OUT(("Domain::ChannelJoinConfirm: channel allocation failed"));
|
|
channel_leave_list.Append(channel_id);
|
|
m_pConnToTopProvider->ChannelLeaveRequest(&channel_leave_list);
|
|
pAtt->ChannelJoinConfirm(RESULT_UNSPECIFIED_FAILURE, uidInitiator, requested_id, 0);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The result was not successful, so this provider does not
|
|
* have to worry about creating the channel. It merely
|
|
* forwards the join confirm to the originating user.
|
|
*/
|
|
TRACE_OUT(("Domain::ChannelJoinConfirm: forwarding ChannelJoinConfirm to user"));
|
|
pAtt->ChannelJoinConfirm(result, uidInitiator, requested_id, channel_id);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ERROR_OUT(("Domain::ChannelJoinConfirm: cannot find the channel"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The named initiator does not exist in the sub-tree of this
|
|
* provider. This could happen if the user is detached before
|
|
* the confirm returns. It will be necessary to issue a channel
|
|
* leave request upward (if the join was successful).
|
|
*/
|
|
WARNING_OUT(("Domain::ChannelJoinConfirm: initiator not found"));
|
|
|
|
if (result == RESULT_SUCCESSFUL)
|
|
{
|
|
TRACE_OUT(("Domain::ChannelJoinConfirm: sending ChannelLeaveRequest to Top Provider"));
|
|
channel_leave_list.Append(channel_id);
|
|
m_pConnToTopProvider->ChannelLeaveRequest(&channel_leave_list);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* This confirm was received from someone besides the Top Provider.
|
|
* Ignore the indication.
|
|
*/
|
|
ERROR_OUT(("Domain::ChannelJoinConfirm: invalid originator=0x%p", pOrigConn));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void ChannelLeaveRequest ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
* This MCS command is initially issued by a user that wishes to leave a
|
|
* channel. This request will stop cascading upward when it reaches a
|
|
* provider that has more attachments joined to the channel than the
|
|
* one that is leaving. If the requesting user is the only joined to
|
|
* a channel, this request will flow all the way to the Top Provider.
|
|
*/
|
|
Void Domain::ChannelLeaveRequest (
|
|
CAttachment *pOrigAtt,
|
|
CChannelIDList *channel_id_list)
|
|
{
|
|
ChannelID chid;
|
|
PChannel channel;
|
|
CChannelIDList channel_leave_list;
|
|
|
|
/*
|
|
* Make sure that the attachment leaving the channel really does exist.
|
|
*/
|
|
if (m_AttachmentList.Find(pOrigAtt))
|
|
{
|
|
/*
|
|
* Iterate through the list of channels to be left, processing each
|
|
* one independently.
|
|
*/
|
|
channel_id_list->Reset();
|
|
while (NULL != (chid = channel_id_list->Iterate()))
|
|
{
|
|
/*
|
|
* Check to make sure that the channel being left really does
|
|
* exist.
|
|
*/
|
|
if (NULL != (channel = m_ChannelList2.Find(chid)))
|
|
{
|
|
/*
|
|
* Let the Channel object deal with this request. After
|
|
* sending the leave request to the channel, it is necessary to
|
|
* check the validity of the channel object determine if it
|
|
* should be deleted as a result of this leave operation.
|
|
*/
|
|
TRACE_OUT(("Domain::ChannelLeaveRequest: processing leave request for channel ID = %04X", (UINT) chid));
|
|
channel_leave_list.Clear();
|
|
channel_leave_list.Append(chid);
|
|
channel->ChannelLeaveRequest(pOrigAtt, &channel_leave_list);
|
|
if (channel->IsValid () == FALSE)
|
|
DeleteChannel(chid);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The named channel does not exist in the information base.
|
|
* Ignore the request.
|
|
*/
|
|
WARNING_OUT(("Domain::ChannelLeaveRequest: received leave request for non-existent channel"));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* This request originated from an attachment that does not exist
|
|
* in the sub-tree of this provider.
|
|
*/
|
|
ERROR_OUT(("Domain::ChannelLeaveRequest: invalid originator=0x%p", pOrigAtt));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void ChannelConveneRequest ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
* This MCS command is initially sent by a user that wishes to convene a
|
|
* new private channel. It is forwarded upward to the Top Provider who
|
|
* will attempt to create the private channel.
|
|
*/
|
|
Void Domain::ChannelConveneRequest (
|
|
CAttachment *pOrigAtt,
|
|
UserID uidInitiator)
|
|
{
|
|
ChannelID channel_id;
|
|
PChannel channel;
|
|
|
|
/*
|
|
* Make sure the requesting user really exists in the sub-tree from which
|
|
* this request originated.
|
|
*/
|
|
if (ValidateUserID(uidInitiator, pOrigAtt))
|
|
{
|
|
/*
|
|
* If this is the Top Provider, then the request can be serviced
|
|
* locally. If not, then it must be forwarded upward.
|
|
*/
|
|
if (IsTopProvider())
|
|
{
|
|
/*
|
|
* See if the arbitrated domain parameters will allow the
|
|
* addition of a new channel.
|
|
*/
|
|
if (Number_Of_Channels < Domain_Parameters.max_channel_ids)
|
|
{
|
|
/*
|
|
* Since this is a request for a private channel, it is
|
|
* necessary to allocate a channel ID from the dynamic range.
|
|
* Then, create the private channel.
|
|
*/
|
|
channel_id = AllocateDynamicChannel ();
|
|
DBG_SAVE_FILE_LINE
|
|
channel = new PrivateChannel(channel_id, uidInitiator, this, m_pConnToTopProvider,
|
|
&m_ChannelList2, &m_AttachmentList);
|
|
if (channel != NULL)
|
|
{
|
|
/*
|
|
* The creation of the new private channel was successful.
|
|
* Add it to the channel list. Note that the channel
|
|
* object itself will issue the channel convene confirm.
|
|
*/
|
|
TRACE_OUT(("Domain::ChannelConveneRequest: adding channel ID = %04X", (UINT) channel_id));
|
|
m_ChannelList2.Insert(channel_id, channel);
|
|
Number_Of_Channels++;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Allocation of the PrivateChannel object failed. We
|
|
* must therefore issue an unsuccessful channel
|
|
* convene confirm to the originating attachment.
|
|
*/
|
|
ERROR_OUT(("Domain::ChannelConveneRequest: channel allocation failed"));
|
|
pOrigAtt->ChannelConveneConfirm(RESULT_UNSPECIFIED_FAILURE, uidInitiator, 0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Domain parmeters will not allow the addition of
|
|
* any more channels. Fail the request.
|
|
*/
|
|
ERROR_OUT(("Domain::ChannelConveneRequest: join denied - too many channels"));
|
|
pOrigAtt->ChannelConveneConfirm(RESULT_TOO_MANY_CHANNELS, uidInitiator, 0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* This is not the Top Provider. That means this is someone elses
|
|
* problem. Issue the request upward toward the Top Provider.
|
|
*/
|
|
TRACE_OUT(("Domain::ChannelConveneRequest: forwarding convene request to Top Provider"));
|
|
m_pConnToTopProvider->ChannelConveneRequest(uidInitiator);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* There is no such user in the sub-tree from which this request
|
|
* originated. Ignore the request.
|
|
*/
|
|
ERROR_OUT(("Domain::ChannelConveneRequest: invalid originator=0x%p", pOrigAtt));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void ChannelConveneConfirm ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
* This MCS command is initially sent by the Top Provider in response to
|
|
* a previously received ChannelConveneRequest. This command contains the
|
|
* results of the request.
|
|
*/
|
|
Void Domain::ChannelConveneConfirm (
|
|
PConnection pOrigConn,
|
|
Result result,
|
|
UserID uidInitiator,
|
|
ChannelID channel_id)
|
|
{
|
|
PChannel channel;
|
|
|
|
/*
|
|
* Verify that the confirm came from the Top Provider.
|
|
*/
|
|
if (pOrigConn == m_pConnToTopProvider)
|
|
{
|
|
/*
|
|
* Make sure that the requesting user is still somewhere in the
|
|
* sub-tree of this provider.
|
|
*/
|
|
if (ValidateUserID (uidInitiator, NULL) )
|
|
{
|
|
/*
|
|
* Found out which downward attachment leads to the requesting
|
|
* user.
|
|
*/
|
|
if (NULL != (channel = m_ChannelList2.Find(uidInitiator)))
|
|
{
|
|
CAttachment *pAtt = channel->GetAttachment();
|
|
ASSERT(pAtt);
|
|
/*
|
|
* Was the result successful. If is was, then the local provider
|
|
* needs to create the new private channel in the local information
|
|
* base.
|
|
*/
|
|
if (result == RESULT_SUCCESSFUL)
|
|
{
|
|
/*
|
|
* See if the named channel already exists in the channel list.
|
|
* Note that it is an error to receive a channel convene
|
|
* confirm for a channel that already exists. This would
|
|
* indicate a logic error somewhere in the domain hierarchy
|
|
* above this provider.
|
|
*/
|
|
if (! m_ChannelList2.Find(channel_id))
|
|
{
|
|
/*
|
|
* The new private channel has to be created.
|
|
*/
|
|
DBG_SAVE_FILE_LINE
|
|
channel = new PrivateChannel(channel_id, uidInitiator, this, m_pConnToTopProvider,
|
|
&m_ChannelList2, &m_AttachmentList);
|
|
if (channel != NULL)
|
|
{
|
|
/*
|
|
* Add the newly created channel to the channel list.
|
|
* Let the Channel object handle the convene confirm.
|
|
*/
|
|
TRACE_OUT(("Domain::ChannelConveneConfirm: adding channel ID = %04X", (UINT) channel_id));
|
|
if(m_ChannelList2.Insert(channel_id, channel))
|
|
{
|
|
Number_Of_Channels++;
|
|
}
|
|
else
|
|
{
|
|
delete channel;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The allocation of the Channel object failed. It
|
|
* is therefore necessary for this provider to cause
|
|
* the channel to be deleted from the domain. It
|
|
* does this by issuing a channel disband request to
|
|
* the Top Provider, and an unsuccessful channel
|
|
* convene confirm to the originating user.
|
|
*/
|
|
ERROR_OUT(("Domain::ChannelConveneConfirm: channel allocation failed"));
|
|
m_pConnToTopProvider->ChannelDisbandRequest(uidInitiator, channel_id);
|
|
pAtt->ChannelConveneConfirm(RESULT_UNSPECIFIED_FAILURE, uidInitiator, 0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* A Channel object already exists for the named channel.
|
|
* This is an error, so report the problem, and ignore
|
|
* the confirm.
|
|
*/
|
|
ERROR_OUT(("Domain::ChannelConveneConfirm: channel already exists in channel list"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The result was not successful, so this provider does not
|
|
* have to worry about creating the channel. It merely
|
|
* forwards the join confirm to the originating user.
|
|
*/
|
|
TRACE_OUT(("Domain::ChannelConveneConfirm: forwarding ChannelConveneConfirm to user"));
|
|
pAtt->ChannelConveneConfirm(result, uidInitiator, channel_id);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ERROR_OUT(("Domain::ChannelConveneConfirm: cannot find the channel"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The named initiator does not exist in the sub-tree of this
|
|
* provider. This could happen if the user is detached before
|
|
* the confirm returns. Note that since a DetachUserIndication
|
|
* will automatically be issued upward for the lost channel
|
|
* manager, it is unnecessary for this provider to take any
|
|
* special action to eliminate the unowned private channel.
|
|
*/
|
|
ERROR_OUT(("Domain::ChannelConveneConfirm: initiator not found"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* This confirm was received from someone besides the Top Provider.
|
|
* Ignore the indication.
|
|
*/
|
|
ERROR_OUT(("Domain::ChannelConveneConfirm: invalid originator=0x%p", pOrigConn));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void ChannelDisbandRequest ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
* This MCS command is initially sent by a user that wishes to disband a
|
|
* private channel that it previously created. If the channel is in the
|
|
* local information base, the request is sent to it. Otherwise, the
|
|
* request is ignored.
|
|
*/
|
|
Void Domain::ChannelDisbandRequest (
|
|
CAttachment *pOrigAtt,
|
|
UserID uidInitiator,
|
|
ChannelID channel_id)
|
|
{
|
|
PChannel channel;
|
|
|
|
/*
|
|
* Make sure the requesting user really exists in the sub-tree from which
|
|
* this request originated.
|
|
*/
|
|
if (ValidateUserID(uidInitiator, pOrigAtt))
|
|
{
|
|
/*
|
|
* Check to see if the specified channel exists in the Channel List.
|
|
*/
|
|
if (NULL != (channel = m_ChannelList2.Find(channel_id)))
|
|
{
|
|
/*
|
|
* Send the disband request to the channel object to handle it.
|
|
* Then ask the channel object if this request has resulted in a
|
|
* need for the channel to be deleted. This will occur when the
|
|
* disband request is handled at the Top Provider.
|
|
*/
|
|
if (channel->GetChannelType() == PRIVATE_CHANNEL)
|
|
{
|
|
PrivateChannel *pPrivChnl = (PrivateChannel *) channel;
|
|
TRACE_OUT(("Domain::ChannelDisbandRequest: sending disband request to channel object"));
|
|
pPrivChnl->ChannelDisbandRequest(pOrigAtt, uidInitiator, channel_id);
|
|
}
|
|
else
|
|
{
|
|
ERROR_OUT(("Domain::ChannelDisbandRequest: it should be private chanel"));
|
|
}
|
|
if (channel->IsValid () == FALSE)
|
|
DeleteChannel (channel_id);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The channel does not exist in the information base. That means
|
|
* that this request is invalid, and should be ignored.
|
|
*/
|
|
ERROR_OUT(("Domain::ChannelDisbandRequest: channel does not exist"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* There is no such user in the sub-tree from which this request
|
|
* originated. Ignore the request.
|
|
*/
|
|
ERROR_OUT(("Domain::ChannelDisbandRequest: invalid originator=0x%p", pOrigAtt));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void ChannelDisbandIndication ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
* This MCS command is initially sent by the Top Provider when it decides
|
|
* to delete a private channel from the domain. It travels downward to
|
|
* all attachments and connections that contain an admitted user or the
|
|
* channel manager in their sub-tree.
|
|
*/
|
|
Void Domain::ChannelDisbandIndication (
|
|
PConnection pOrigConn,
|
|
ChannelID channel_id)
|
|
{
|
|
PChannel channel;
|
|
|
|
/*
|
|
* Verify that the indication came from the Top Provider.
|
|
*/
|
|
if (pOrigConn == m_pConnToTopProvider)
|
|
{
|
|
/*
|
|
* Check to see if the specified channel exists in the Channel List.
|
|
*/
|
|
if (NULL != (channel = m_ChannelList2.Find(channel_id)))
|
|
{
|
|
/*
|
|
* Send the disband indication to the channel object to handle it.
|
|
* Then delete the object from the local information base, as it is
|
|
* no longer needed.
|
|
*/
|
|
if (channel->GetChannelType() == PRIVATE_CHANNEL)
|
|
{
|
|
PrivateChannel *pPrivChnl = (PrivateChannel *) channel;
|
|
TRACE_OUT(("Domain::ChannelDisbandIndication: sending disband indication to channel object"));
|
|
pPrivChnl->ChannelDisbandIndication(channel_id);
|
|
}
|
|
else
|
|
{
|
|
ERROR_OUT(("Domain::ChannelDisbandIndication: it should be private chanel"));
|
|
}
|
|
if (channel->IsValid () == FALSE)
|
|
DeleteChannel (channel_id);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The channel does not exist in the information base. That means
|
|
* that this indication is invalid, and should be ignored.
|
|
*/
|
|
ERROR_OUT(("Domain::ChannelDisbandIndication: channel does not exist"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* This indication was received from someone besides the Top Provider.
|
|
* Ignore the indication.
|
|
*/
|
|
ERROR_OUT(("Domain::ChannelDisbandIndication: invalid originator=0x%p", pOrigConn));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void ChannelAdmitRequest ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
* This MCS command is initially sent by the manager of a private channel
|
|
* when it wishes to expand the authorized user list of that channel. If
|
|
* the channel is in the local information base, the request is sent to it.
|
|
* Otherwise, the request is ignored.
|
|
*/
|
|
Void Domain::ChannelAdmitRequest (
|
|
CAttachment *pOrigAtt,
|
|
UserID uidInitiator,
|
|
ChannelID channel_id,
|
|
CUidList *user_id_list)
|
|
{
|
|
PChannel channel;
|
|
|
|
/*
|
|
* Make sure the requesting user really exists in the sub-tree from which
|
|
* this request originated.
|
|
*/
|
|
if (ValidateUserID(uidInitiator, pOrigAtt))
|
|
{
|
|
/*
|
|
* Check to see if the specified channel exists in the Channel List.
|
|
*/
|
|
if (NULL != (channel = m_ChannelList2.Find(channel_id)))
|
|
{
|
|
if (channel->GetChannelType() == PRIVATE_CHANNEL)
|
|
{
|
|
PrivateChannel *pPrivChnl = (PrivateChannel *) channel;
|
|
/*
|
|
* Send the admit request to the channel object to handle it.
|
|
*/
|
|
TRACE_OUT(("Domain::ChannelAdmitRequest: sending admit request to channel object"));
|
|
pPrivChnl->ChannelAdmitRequest(pOrigAtt, uidInitiator, channel_id, user_id_list);
|
|
}
|
|
else
|
|
{
|
|
ERROR_OUT(("Domain::ChannelAdmitRequest: it should be private chanel"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The channel does not exist in the information base. That means
|
|
* that this request is invalid, and should be ignored.
|
|
*/
|
|
ERROR_OUT(("Domain::ChannelAdmitRequest: channel does not exist"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* There is no such user in the sub-tree from which this request
|
|
* originated. Ignore the request.
|
|
*/
|
|
ERROR_OUT(("Domain::ChannelAdmitRequest: invalid originator=0x%p", pOrigAtt));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void ChannelAdmitIndication ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
* This MCS command is initially sent by the Top Provider when it receives
|
|
* a channel admit indication from the manager of a private channel. This
|
|
* indication is broadcast downward to all providers that contain an
|
|
* admitted user somewhere in their sub-tree. A side-effect of this
|
|
* indication is that a private channel will be created in the information
|
|
* base if one does not already exist.
|
|
*/
|
|
Void Domain::ChannelAdmitIndication (
|
|
PConnection pOrigConn,
|
|
UserID uidInitiator,
|
|
ChannelID channel_id,
|
|
CUidList *user_id_list)
|
|
{
|
|
PChannel channel;
|
|
|
|
/*
|
|
* Verify that the indication came from the Top Provider.
|
|
*/
|
|
if (pOrigConn == m_pConnToTopProvider)
|
|
{
|
|
/*
|
|
* Check to see if the specified channel exists in the Channel List.
|
|
*/
|
|
if (NULL != (channel = m_ChannelList2.Find(channel_id)))
|
|
{
|
|
if (channel->GetChannelType() == PRIVATE_CHANNEL)
|
|
{
|
|
PrivateChannel *pPrivChnl = (PrivateChannel *) channel;
|
|
/*
|
|
* Send the admit indication to the channel object to handle it.
|
|
*/
|
|
TRACE_OUT(("Domain::ChannelAdmitIndication: sending admit indication to channel object"));
|
|
pPrivChnl->ChannelAdmitIndication(pOrigConn, uidInitiator, channel_id, user_id_list);
|
|
}
|
|
else
|
|
{
|
|
ERROR_OUT(("Domain::ChannelAdmitIndication: it should be private chanel"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Since the private channel does not exist in the information
|
|
* base, it will be necessary to create one. After it is created,
|
|
* it can handle the channel admit indication.
|
|
*/
|
|
DBG_SAVE_FILE_LINE
|
|
channel = new PrivateChannel(channel_id, uidInitiator, this, m_pConnToTopProvider,
|
|
&m_ChannelList2, &m_AttachmentList);
|
|
if (channel != NULL)
|
|
{
|
|
PrivateChannel *pPrivChnl = (PrivateChannel *) channel;
|
|
/*
|
|
* Put the newly created private channel into the domain
|
|
* information base.
|
|
*/
|
|
TRACE_OUT(("Domain::ChannelAdmitIndication: adding channel ID = %04X", (UINT) channel_id));
|
|
if(m_ChannelList2.Insert(channel_id, channel))
|
|
{
|
|
Number_Of_Channels++;
|
|
|
|
/*
|
|
* Send the admit indication to the new channel object to
|
|
* handle it.
|
|
*/
|
|
pPrivChnl->ChannelAdmitIndication(pOrigConn, uidInitiator, channel_id, user_id_list);
|
|
}
|
|
else
|
|
{
|
|
delete channel;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* We have been told by the Top Provider to create a private
|
|
* channel, but we can't due to a resource shortage. We also
|
|
* can't purge the channel from the domain since the channel
|
|
* manager does not exist in the sub-tree of this provider.
|
|
* We are therefore out of sync with the Top Provider, and
|
|
* there is nothing we can do about it (except for possibly
|
|
* disconnecting from the Top Provider and purging the entire
|
|
* domain from this node downward).
|
|
*/
|
|
ERROR_OUT(("Domain::ChannelAdmitIndication: channel allocation failure"));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* This indication was received from someone besides the Top Provider.
|
|
* Ignore the indication.
|
|
*/
|
|
WARNING_OUT(("Domain::ChannelAdmitIndication: invalid originator=0x%p", pOrigConn));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void ChannelExpelRequest ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
* This MCS command is initially sent by the manager of a private channel
|
|
* when it wishes to shrink the authorized user list of that channel. If
|
|
* the channel is in the local information base, the request is sent to it.
|
|
* Otherwise, the request is ignored.
|
|
*/
|
|
Void Domain::ChannelExpelRequest (
|
|
CAttachment *pOrigAtt,
|
|
UserID uidInitiator,
|
|
ChannelID channel_id,
|
|
CUidList *user_id_list)
|
|
{
|
|
PChannel channel;
|
|
|
|
/*
|
|
* Make sure the requesting user really exists in the sub-tree from which
|
|
* this request originated.
|
|
*/
|
|
if (ValidateUserID(uidInitiator, pOrigAtt))
|
|
{
|
|
/*
|
|
* Check to see if the specified channel exists in the Channel List.
|
|
*/
|
|
if (NULL != (channel = m_ChannelList2.Find(channel_id)))
|
|
{
|
|
if (channel->GetChannelType() == PRIVATE_CHANNEL)
|
|
{
|
|
PrivateChannel *pPrivChnl = (PrivateChannel *) channel;
|
|
/*
|
|
* Send the admit request to the channel object to handle it.
|
|
*/
|
|
TRACE_OUT(("Domain::ChannelExpelRequest: "
|
|
"sending expel request to channel object"));
|
|
pPrivChnl->ChannelExpelRequest(pOrigAtt, uidInitiator, channel_id, user_id_list);
|
|
}
|
|
else
|
|
{
|
|
ERROR_OUT(("Domain::ChannelExpelRequest: it should be private chanel"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The channel does not exist in the information base. That means
|
|
* that this request is invalid, and should be ignored.
|
|
*/
|
|
WARNING_OUT(("Domain::ChannelExpelRequest: channel does not exist"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* There is no such user in the sub-tree from which this request
|
|
* originated. Ignore the request.
|
|
*/
|
|
WARNING_OUT(("Domain::ChannelExpelRequest: invalid originator=0x%p", pOrigAtt));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void ChannelExpelIndication ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
* This MCS command is initially sent by the Top Provider when it receives
|
|
* a request from the manager of a private channel to reduce the
|
|
* authorized user list. It travels downward to all attachments and
|
|
* connections that contain an admitted user or the channel manager in
|
|
* their sub-tree.
|
|
*/
|
|
Void Domain::ChannelExpelIndication (
|
|
PConnection pOrigConn,
|
|
ChannelID channel_id,
|
|
CUidList *user_id_list)
|
|
{
|
|
PChannel channel;
|
|
|
|
/*
|
|
* Verify that the indication came from the Top Provider.
|
|
*/
|
|
if (pOrigConn == m_pConnToTopProvider)
|
|
{
|
|
/*
|
|
* Check to see if the specified channel exists in the Channel List.
|
|
*/
|
|
if (NULL != (channel = m_ChannelList2.Find(channel_id)))
|
|
{
|
|
if (channel->GetChannelType() == PRIVATE_CHANNEL)
|
|
{
|
|
PrivateChannel *pPrivChnl = (PrivateChannel *) channel;
|
|
/*
|
|
* Send the expel indication to the channel object to handle it.
|
|
* Then check to see if the channel is still valid (delete it
|
|
* if not). This would occur if the expel results in an empty
|
|
* admitted user list, and the channel manager is also not in the
|
|
* sub-tree of this provider.
|
|
*/
|
|
TRACE_OUT(("Domain::ChannelExpelIndication: sending expel indication to channel object"));
|
|
pPrivChnl->ChannelExpelIndication(pOrigConn, channel_id, user_id_list);
|
|
}
|
|
else
|
|
{
|
|
ERROR_OUT(("Domain::ChannelExpelIndication: it should be private chanel"));
|
|
}
|
|
if (channel->IsValid () == FALSE)
|
|
DeleteChannel (channel_id);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The channel does not exist in the information base. That means
|
|
* that this indication is invalid, and should be ignored.
|
|
*/
|
|
ERROR_OUT(("Domain::ChannelExpelIndication: channel does not exist"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* This indication was received from someone besides the Top Provider.
|
|
* Ignore the indication.
|
|
*/
|
|
ERROR_OUT(("Domain::ChannelExpelIndication: invalid originator=0x%p", pOrigConn));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void SendDataRequest ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
* This MCS command is initially issued by a user attachment that wishes
|
|
* to send data on a particular channel in this domain. The request will
|
|
* flow upward all the way to the Top Provider. It will also cause
|
|
* send data indications to be sent downward to all other attachments
|
|
* that are joined to the channel.
|
|
*/
|
|
Void Domain::SendDataRequest (
|
|
CAttachment *pOrigAtt,
|
|
UINT type,
|
|
PDataPacket data_packet)
|
|
{
|
|
PChannel channel;
|
|
|
|
/*
|
|
* Make sure the requesting user really exists in the sub-tree from which
|
|
* this request originated.
|
|
*/
|
|
if (ValidateUserID(data_packet->GetInitiator(), pOrigAtt))
|
|
{
|
|
/*
|
|
* See if the channel exists in the local information base. If it does
|
|
* then let the Channel object handle the routing of the data. If
|
|
* it does not exist, then simply forward the request upward to be
|
|
* handled by the next higher provider (unless this is the Top
|
|
* Provider).
|
|
*/
|
|
if (NULL != (channel = m_ChannelList2.Find(data_packet->GetChannelID())))
|
|
channel->SendDataRequest(pOrigAtt, type, data_packet);
|
|
|
|
else if (! IsTopProvider())
|
|
m_pConnToTopProvider->SendDataRequest(data_packet);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* There is no such user in the sub-tree from which this request
|
|
* originated. Ignore the request.
|
|
*/
|
|
WARNING_OUT (("Domain::SendDataRequest: invalid originator=0x%p, uidInitiator=%d", pOrigAtt, data_packet->GetInitiator()));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void SendDataIndication ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
* This MCS command is issued by all providers that receive a send data
|
|
* request on a channel to which one of their attachments is joined. It
|
|
* delivers data in a non-uniform fashion to all users joined to the
|
|
* named channel.
|
|
*/
|
|
Void Domain::SendDataIndication (
|
|
PConnection pOrigConn,
|
|
UINT type,
|
|
PDataPacket data_packet)
|
|
{
|
|
PChannel channel;
|
|
|
|
/*
|
|
* Verify that the indication came from the Top Provider.
|
|
*/
|
|
if (pOrigConn == m_pConnToTopProvider)
|
|
{
|
|
/*
|
|
* See if the channel exists in the local information base. If it does
|
|
* then let the Channel object handle the routing of the data. If
|
|
* it does not exist, then ignore the request.
|
|
*/
|
|
if (NULL != (channel = m_ChannelList2.Find(data_packet->GetChannelID())))
|
|
channel->SendDataIndication(pOrigConn, type, data_packet);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* This indication was received from someone besides the Top Provider.
|
|
* Ignore the indication.
|
|
*/
|
|
WARNING_OUT (("Domain::SendDataIndication: invalid originator=0x%p, initiator=%d", pOrigConn, data_packet->GetInitiator()));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void TokenGrabRequest ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
* This MCS command is initially sent by a user attachment that wishes
|
|
* to grab a token. It flows upward to the Top Provider, who attempts
|
|
* to satisfy the request.
|
|
*/
|
|
Void Domain::TokenGrabRequest (
|
|
CAttachment *pOrigAtt,
|
|
UserID uidInitiator,
|
|
TokenID token_id)
|
|
{
|
|
PToken token;
|
|
|
|
/*
|
|
* Make sure the requesting user really exists in the sub-tree from which
|
|
* this request originated.
|
|
*/
|
|
if (ValidateUserID(uidInitiator, pOrigAtt))
|
|
{
|
|
/*
|
|
* If the token already exists in the token list, then let the Token
|
|
* object deal with the request.
|
|
*/
|
|
if (NULL != (token = m_TokenList2.Find(token_id)))
|
|
{
|
|
TRACE_OUT(("Domain::TokenGrabRequest: sending grab request to token object"));
|
|
token->TokenGrabRequest(pOrigAtt, uidInitiator, token_id);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The token does not exist yet. Check to see if this is the Top
|
|
* Provider. If it is, then the request can be processed locally.
|
|
* Otherwise, forward the request upward.
|
|
*/
|
|
if (IsTopProvider())
|
|
{
|
|
/*
|
|
* Check to see if the arbitrated domain parameters will allow
|
|
* the addition of another token.
|
|
*/
|
|
if (Number_Of_Tokens < Domain_Parameters.max_token_ids)
|
|
{
|
|
/*
|
|
* Try to create a new Token object.
|
|
*/
|
|
DBG_SAVE_FILE_LINE
|
|
token = new Token(token_id, this, m_pConnToTopProvider, &m_ChannelList2, &m_AttachmentList);
|
|
if (token != NULL)
|
|
{
|
|
/*
|
|
* Put the newly created Token object into the token
|
|
* list. Then pass the grab request to it.
|
|
*/
|
|
TRACE_OUT(("Domain::TokenGrabRequest: adding token ID = %04X", (UINT) token_id));
|
|
m_TokenList2.Append(token_id, token);
|
|
Number_Of_Tokens++;
|
|
token->TokenGrabRequest(pOrigAtt, uidInitiator, token_id);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The allocation of the Token object failed. It is
|
|
* therefore necessary to fail the request.
|
|
*/
|
|
ERROR_OUT(("Domain::TokenGrabRequest: token allocation failed"));
|
|
pOrigAtt->TokenGrabConfirm(RESULT_UNSPECIFIED_FAILURE, uidInitiator, token_id, TOKEN_NOT_IN_USE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The arbitrated domain parameters will not allow the
|
|
* creation of another token in this domain. So fail
|
|
* the request.
|
|
*/
|
|
ERROR_OUT(("Domain::TokenGrabRequest: grab denied - too many tokens"));
|
|
pOrigAtt->TokenGrabConfirm(RESULT_TOO_MANY_TOKENS, uidInitiator, token_id, TOKEN_NOT_IN_USE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* This is not the Top Provider. Forward the request upward.
|
|
*/
|
|
TRACE_OUT(("Domain::TokenGrabRequest: forwarding grab request to Top Provider"));
|
|
m_pConnToTopProvider->TokenGrabRequest(uidInitiator, token_id);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* There is no such user in the sub-tree from which this request
|
|
* originated. Ignore the request.
|
|
*/
|
|
ERROR_OUT(("Domain::TokenGrabRequest: invalid originator=0x%p", pOrigAtt));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void TokenGrabConfirm ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
* This MCS command is initially sent by the Top Provider upon receipt of
|
|
* a grab request. It is sent back to the initiating user, containing
|
|
* the result of the request.
|
|
*/
|
|
Void Domain::TokenGrabConfirm (
|
|
PConnection pOrigConn,
|
|
Result result,
|
|
UserID uidInitiator,
|
|
TokenID token_id,
|
|
TokenStatus token_status)
|
|
{
|
|
PToken token;
|
|
|
|
/*
|
|
* Verify that the confirm came from the Top Provider.
|
|
*/
|
|
if (pOrigConn == m_pConnToTopProvider)
|
|
{
|
|
/*
|
|
* See if the token already exists in the local information base. If
|
|
* so, let it handle this.
|
|
*/
|
|
if (NULL != (token = m_TokenList2.Find(token_id)))
|
|
{
|
|
TRACE_OUT(("Domain::TokenGrabConfirm: sending grab confirm to token object"));
|
|
token->TokenGrabConfirm(result, uidInitiator, token_id, token_status);
|
|
}
|
|
else
|
|
{
|
|
PChannel channel;
|
|
/*
|
|
* Make sure that the requesting user is still somewhere in the
|
|
* sub-tree of this provider.
|
|
*/
|
|
if (ValidateUserID (uidInitiator, NULL) )
|
|
{
|
|
/*
|
|
* Determine which attachment leads to the initiating user.
|
|
*/
|
|
if (NULL != (channel = m_ChannelList2.Find(uidInitiator)))
|
|
{
|
|
CAttachment *pAtt = channel->GetAttachment();
|
|
ASSERT(pAtt);
|
|
/*
|
|
* If the result of the request is successful, then it is
|
|
* necessary to create the token in the local information base.
|
|
*/
|
|
if (result == RESULT_SUCCESSFUL)
|
|
{
|
|
/*
|
|
* Create the token.
|
|
*/
|
|
DBG_SAVE_FILE_LINE
|
|
token = new Token(token_id, this, m_pConnToTopProvider, &m_ChannelList2, &m_AttachmentList);
|
|
if (token != NULL)
|
|
{
|
|
/*
|
|
* Put the newly created Token object into the token
|
|
* list. Then pass the grab confirm to it.
|
|
*/
|
|
TRACE_OUT(("Domain::TokenGrabConfirm: adding token ID = %04X", (UINT) token_id));
|
|
m_TokenList2.Append(token_id, token);
|
|
Number_Of_Tokens++;
|
|
token->TokenGrabConfirm(result, uidInitiator, token_id, token_status);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The creation of the token failed. It is therefore
|
|
* necessary to send a failed confirm to the initiating
|
|
* user, as well as a token release request to the Top
|
|
* Provider.
|
|
*/
|
|
ERROR_OUT(("Domain::TokenGrabConfirm: token creation failed"));
|
|
m_pConnToTopProvider->TokenReleaseRequest(uidInitiator, token_id);
|
|
pAtt->TokenGrabConfirm(RESULT_UNSPECIFIED_FAILURE, uidInitiator, token_id, TOKEN_NOT_IN_USE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The confirm was unsuccessful, so there is no need to
|
|
* create a token in the information base. Just forward
|
|
* the confirm to the initiating user.
|
|
*/
|
|
TRACE_OUT(("Domain::TokenGrabConfirm: forwarding failed grab confirm"));
|
|
pAtt->TokenGrabConfirm(result, uidInitiator, token_id, token_status);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ERROR_OUT(("Domain::TokenGrabConfirm: cannot find channel"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The named initiator does not exist in the sub-tree of this
|
|
* provider. Ignore the confirm.
|
|
*/
|
|
ERROR_OUT(("Domain::TokenGrabConfirm: invalid initiator, uidInitiator=%u", (UINT) uidInitiator));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* This confirm was received from someone besides the Top Provider.
|
|
* Ignore the confirm.
|
|
*/
|
|
ERROR_OUT(("Domain::TokenGrabConfirm: invalid originator=0x%p", pOrigConn));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void TokenInhibitRequest ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
* This MCS command is initially sent by a user attachment that wishes
|
|
* to inhibit a token. It flows upward to the Top Provider, who attempts
|
|
* to satisfy the request.
|
|
*/
|
|
Void Domain::TokenInhibitRequest (
|
|
CAttachment *pOrigAtt,
|
|
UserID uidInitiator,
|
|
TokenID token_id)
|
|
{
|
|
PToken token;
|
|
|
|
/*
|
|
* Make sure the requesting user really exists in the sub-tree from which
|
|
* this request originated.
|
|
*/
|
|
if (ValidateUserID(uidInitiator, pOrigAtt))
|
|
{
|
|
/*
|
|
* If the token already exists in the token list, then let the Token
|
|
* object deal with the request.
|
|
*/
|
|
if (NULL != (token = m_TokenList2.Find(token_id)))
|
|
{
|
|
TRACE_OUT(("Domain::TokenInhibitRequest: sending inhibit request to token object"));
|
|
token->TokenInhibitRequest(pOrigAtt, uidInitiator, token_id);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The token does not exist yet. Check to see if this is the Top
|
|
* Provider. If it is, then the request can be processed locally.
|
|
* Otherwise, forward the request upward.
|
|
*/
|
|
if (IsTopProvider())
|
|
{
|
|
/*
|
|
* Check to see if the arbitrated domain parameters will allow
|
|
* the addition of another token.
|
|
*/
|
|
if (Number_Of_Tokens < Domain_Parameters.max_token_ids)
|
|
{
|
|
/*
|
|
* Try to create a new Token object.
|
|
*/
|
|
DBG_SAVE_FILE_LINE
|
|
token = new Token(token_id, this, m_pConnToTopProvider, &m_ChannelList2, &m_AttachmentList);
|
|
if (token != NULL)
|
|
{
|
|
/*
|
|
* Put the newly created Token object into the token
|
|
* list. Then pass the inhibit request to it.
|
|
*/
|
|
TRACE_OUT(("Domain::TokenInhibitRequest: adding token ID = %04X", (UINT) token_id));
|
|
m_TokenList2.Append(token_id, token);
|
|
Number_Of_Tokens++;
|
|
token->TokenInhibitRequest(pOrigAtt, uidInitiator, token_id);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The allocation of the Token object failed. It is
|
|
* therefore necessary to fail the request.
|
|
*/
|
|
ERROR_OUT(("Domain::TokenInhibitRequest: token allocation failed"));
|
|
pOrigAtt->TokenInhibitConfirm(RESULT_UNSPECIFIED_FAILURE, uidInitiator, token_id, TOKEN_NOT_IN_USE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The arbitrated domain parameters will not allow the
|
|
* creation of another token in this domain. So fail
|
|
* the request.
|
|
*/
|
|
ERROR_OUT(("Domain::TokenInhibitRequest: inhibit denied - too many tokens"));
|
|
pOrigAtt->TokenInhibitConfirm(RESULT_TOO_MANY_TOKENS, uidInitiator, token_id, TOKEN_NOT_IN_USE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* This is not the Top Provider. Forward the request upward.
|
|
*/
|
|
TRACE_OUT(("Domain::TokenInhibitRequest: forwarding inhibit request to Top Provider"));
|
|
m_pConnToTopProvider->TokenInhibitRequest(uidInitiator, token_id);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* There is no such user in the sub-tree from which this request
|
|
* originated. Ignore the request.
|
|
*/
|
|
ERROR_OUT(("Domain::TokenInhibitRequest: invalid originator=0x%p", pOrigAtt));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void TokenInhibitConfirm ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
* This MCS command is initially sent by the Top Provider upon receipt of
|
|
* a inhibit request. It is sent back to the initiating user, containing
|
|
* the result of the request.
|
|
*/
|
|
Void Domain::TokenInhibitConfirm (
|
|
PConnection pOrigConn,
|
|
Result result,
|
|
UserID uidInitiator,
|
|
TokenID token_id,
|
|
TokenStatus token_status)
|
|
{
|
|
PToken token;
|
|
|
|
/*
|
|
* Verify that the confirm came from the Top Provider.
|
|
*/
|
|
if (pOrigConn == m_pConnToTopProvider)
|
|
{
|
|
/*
|
|
* See if the token already exists in the local information base. If
|
|
* so, let it handle this.
|
|
*/
|
|
if (NULL != (token = m_TokenList2.Find(token_id)))
|
|
{
|
|
TRACE_OUT(("Domain::TokenInhibitConfirm: sending inhibit confirm to token object"));
|
|
token->TokenInhibitConfirm(result, uidInitiator, token_id, token_status);
|
|
}
|
|
else
|
|
{
|
|
PChannel channel;
|
|
/*
|
|
* Make sure that the requesting user is still somewhere in the
|
|
* sub-tree of this provider.
|
|
*/
|
|
if (ValidateUserID (uidInitiator, NULL) )
|
|
{
|
|
/*
|
|
* Determine which attachment leads to the requesting user.
|
|
*/
|
|
if (NULL != (channel = m_ChannelList2.Find(uidInitiator)))
|
|
{
|
|
CAttachment *pAtt = channel->GetAttachment();
|
|
ASSERT(pAtt);
|
|
/*
|
|
* If the result of the request is successful, then it is
|
|
* necessary to create the token in the local information base.
|
|
*/
|
|
if (result == RESULT_SUCCESSFUL)
|
|
{
|
|
/*
|
|
* Create the token.
|
|
*/
|
|
DBG_SAVE_FILE_LINE
|
|
token = new Token(token_id, this, m_pConnToTopProvider, &m_ChannelList2, &m_AttachmentList);
|
|
if (token != NULL)
|
|
{
|
|
/*
|
|
* Put the newly created Token object into the token
|
|
* list. Then pass the inhibit confirm to it.
|
|
*/
|
|
TRACE_OUT(("Domain::TokenInhibitConfirm: adding token ID = %04X", (UINT) token_id));
|
|
m_TokenList2.Append(token_id, token);
|
|
Number_Of_Tokens++;
|
|
token->TokenInhibitConfirm(result, uidInitiator, token_id, token_status);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The creation of the token failed. It is therefore
|
|
* necessary to send a failed confirm to the initiating
|
|
* user, as well as a token release request to the Top
|
|
* Provider.
|
|
*/
|
|
ERROR_OUT(("Domain::TokenInhibitConfirm: token creation failed"));
|
|
m_pConnToTopProvider->TokenReleaseRequest(uidInitiator, token_id);
|
|
pAtt->TokenInhibitConfirm(RESULT_UNSPECIFIED_FAILURE, uidInitiator, token_id, TOKEN_NOT_IN_USE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The confirm was unsuccessful, so there is no need to
|
|
* create a token in the information base. Just forward
|
|
* the confirm to the initiating user.
|
|
*/
|
|
ERROR_OUT(("Domain::TokenInhibitConfirm: forwarding failed inhibit confirm"));
|
|
pAtt->TokenInhibitConfirm(result, uidInitiator, token_id, token_status);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ERROR_OUT(("Domain::TokenInhibitConfirm: cannot find channel"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The named initiator does not exist in the sub-tree of this
|
|
* provider. Ignore the confirm.
|
|
*/
|
|
ERROR_OUT(("Domain::TokenInhibitConfirm: initiator not valid, uidInitiator=%u", (UINT) uidInitiator));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* This confirm was received from someone besides the Top Provider.
|
|
* Ignore the confirm.
|
|
*/
|
|
ERROR_OUT(("Domain::TokenInhibitConfirm: invalid originator=0x%p", pOrigConn));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void TokenGiveRequest ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
*/
|
|
Void Domain::TokenGiveRequest (
|
|
CAttachment *pOrigAtt,
|
|
PTokenGiveRecord pTokenGiveRec)
|
|
{
|
|
PToken token;
|
|
|
|
/*
|
|
* Make sure the requesting user really exists in the sub-tree from which
|
|
* this request originated.
|
|
*/
|
|
if (ValidateUserID(pTokenGiveRec->uidInitiator, pOrigAtt))
|
|
{
|
|
/*
|
|
* If the token already exists in the token list, then let the
|
|
* Token object deal with the request.
|
|
*/
|
|
if (NULL != (token = m_TokenList2.Find(pTokenGiveRec->token_id)))
|
|
{
|
|
TRACE_OUT(("Domain::TokenGiveRequest: sending give request to token object"));
|
|
token->TokenGiveRequest(pOrigAtt, pTokenGiveRec);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Check to see if this is the Top Provider. If it is, then the
|
|
* request can be processed locally. Otherwise, forward the
|
|
* request upward.
|
|
*/
|
|
if (IsTopProvider())
|
|
{
|
|
/*
|
|
* The token does not exist in this domain. Report this and
|
|
* send the appropriate give confirm back to the originating
|
|
* user.
|
|
*/
|
|
ERROR_OUT(("Domain::TokenGiveRequest: token does not exist"));
|
|
pOrigAtt->TokenGiveConfirm(RESULT_TOKEN_NOT_POSSESSED,
|
|
pTokenGiveRec->uidInitiator, pTokenGiveRec->token_id, TOKEN_NOT_IN_USE);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* This is not the Top Provider. Forward the request upward.
|
|
*/
|
|
TRACE_OUT(("Domain::TokenGiveRequest: forwarding give request to Top Provider"));
|
|
m_pConnToTopProvider->TokenGiveRequest(pTokenGiveRec);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* There is no such user in the sub-tree from which this request
|
|
* originated. Ignore the request.
|
|
*/
|
|
ERROR_OUT(("Domain::TokenGiveRequest: invalid originator=0x%p", pOrigAtt));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void TokenGiveIndication ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
*/
|
|
Void Domain::TokenGiveIndication (
|
|
PConnection pOrigConn,
|
|
PTokenGiveRecord pTokenGiveRec)
|
|
{
|
|
PToken token;
|
|
TokenID token_id = pTokenGiveRec->token_id;
|
|
|
|
/*
|
|
* Verify that the indication came from the Top Provider.
|
|
*/
|
|
if (pOrigConn == m_pConnToTopProvider)
|
|
{
|
|
/*
|
|
* See if the token already exists in the local information base. If
|
|
* so, let it handle this.
|
|
*/
|
|
if (NULL != (token = m_TokenList2.Find(token_id)))
|
|
{
|
|
TRACE_OUT(("Domain::TokenGiveIndication: sending give indication to token object"));
|
|
token->TokenGiveIndication(pTokenGiveRec);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Make sure that the specified receiver is somewhere in the
|
|
* sub-tree of this provider.
|
|
*/
|
|
if (ValidateUserID (pTokenGiveRec->receiver_id, NULL) )
|
|
{
|
|
/*
|
|
* Create the token.
|
|
*/
|
|
DBG_SAVE_FILE_LINE
|
|
token = new Token(token_id, this, m_pConnToTopProvider, &m_ChannelList2, &m_AttachmentList);
|
|
if (token != NULL)
|
|
{
|
|
/*
|
|
* Put the newly created Token object into the token
|
|
* list. Then pass the give indication to it.
|
|
*/
|
|
TRACE_OUT(("Domain::TokenGiveIndication: adding token ID = %04X", (UINT) token_id));
|
|
if(m_TokenList2.Append(token_id, token))
|
|
{
|
|
Number_Of_Tokens++;
|
|
token->TokenGiveIndication(pTokenGiveRec);
|
|
}
|
|
else
|
|
{
|
|
delete token;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The creation of the token failed. It is therefore
|
|
* necessary to send a failed give response to the Top
|
|
* Provider.
|
|
*/
|
|
ERROR_OUT(("Domain::TokenGiveIndication: token creation failed"));
|
|
m_pConnToTopProvider->TokenGiveResponse(RESULT_UNSPECIFIED_FAILURE,
|
|
pTokenGiveRec->uidInitiator, token_id);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The specified receiver does not exist in the sub-tree of
|
|
* this provider. It is not necessary for this provider to
|
|
* take special action, since the detach user indication for
|
|
* the receiver will clean up.
|
|
*/
|
|
ERROR_OUT(("Domain::TokenGiveIndication: receiver not valid"));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* This indication was received from someone besides the Top Provider.
|
|
* Ignore the indication.
|
|
*/
|
|
ERROR_OUT(("Domain::TokenGiveIndication: invalid originator=0x%p", pOrigConn));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void TokenGiveResponse ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
*/
|
|
Void Domain::TokenGiveResponse (
|
|
CAttachment *pOrigAtt,
|
|
Result result,
|
|
UserID receiver_id,
|
|
TokenID token_id)
|
|
{
|
|
PToken token;
|
|
|
|
/*
|
|
* Make sure the requesting user really exists in the sub-tree from which
|
|
* this response originated.
|
|
*/
|
|
if (ValidateUserID(receiver_id, pOrigAtt))
|
|
{
|
|
/*
|
|
* If the token already exists in the token list, then let the
|
|
* Token object deal with the response.
|
|
*/
|
|
if (NULL != (token = m_TokenList2.Find(token_id)))
|
|
{
|
|
/*
|
|
* Send the give response to the token object. Then check to
|
|
* see if it is still valid (delete it if not).
|
|
*/
|
|
TRACE_OUT(("Domain::TokenGiveResponse: sending give response to token object"));
|
|
token->TokenGiveResponse(result, receiver_id, token_id);
|
|
if (token->IsValid () == FALSE)
|
|
DeleteToken (token_id);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The token is not in the information base, which means that it
|
|
* cannot be being given to the initiator of this response.
|
|
* Ignore the response.
|
|
*/
|
|
ERROR_OUT(("Domain::TokenGiveResponse: no such token"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* There is no such user in the sub-tree from which this response
|
|
* originated. Ignore the response.
|
|
*/
|
|
ERROR_OUT(("Domain::TokenGiveResponse: invalid originator=0x%p", pOrigAtt));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void TokenGiveConfirm ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
*/
|
|
Void Domain::TokenGiveConfirm (
|
|
PConnection pOrigConn,
|
|
Result result,
|
|
UserID uidInitiator,
|
|
TokenID token_id,
|
|
TokenStatus token_status)
|
|
{
|
|
PToken token;
|
|
|
|
/*
|
|
* Verify that the confirm came from the Top Provider.
|
|
*/
|
|
if (pOrigConn == m_pConnToTopProvider)
|
|
{
|
|
/*
|
|
* See if the token already exists in the local information base. If
|
|
* so, let it handle this.
|
|
*/
|
|
if (NULL != (token = m_TokenList2.Find(token_id)))
|
|
{
|
|
/*
|
|
* Send the give confirm to the token object. Then check to
|
|
* see if it is still valid (delete it if not).
|
|
*/
|
|
TRACE_OUT(("Domain::TokenGiveConfirm: sending give confirm to token object"));
|
|
token->TokenGiveConfirm(result, uidInitiator, token_id, token_status);
|
|
if (token->IsValid () == FALSE)
|
|
DeleteToken (token_id);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Make sure that the requesting user is still somewhere in the
|
|
* sub-tree of this provider.
|
|
*/
|
|
if (ValidateUserID (uidInitiator, NULL) )
|
|
{
|
|
PChannel channel;
|
|
/*
|
|
* Determine which attachment leads to the requesting user.
|
|
* Then forward the confirm in that direction.
|
|
*/
|
|
TRACE_OUT(("Domain::TokenGiveConfirm: forwarding give confirm"));
|
|
if (NULL != (channel = m_ChannelList2.Find(uidInitiator)))
|
|
{
|
|
CAttachment *pAtt = channel->GetAttachment();
|
|
if (pAtt)
|
|
{
|
|
pAtt->TokenGiveConfirm(result, uidInitiator, token_id, token_status);
|
|
}
|
|
else
|
|
{
|
|
ERROR_OUT(("Domain::TokenGiveConfirm: cannot get attachment"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ERROR_OUT(("Domain::TokenGiveConfirm: cannot find channel"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The named initiator does not exist in the sub-tree of this
|
|
* provider. Ignore the confirm.
|
|
*/
|
|
ERROR_OUT(("Domain::TokenGiveConfirm: initiator not valid"));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* This confirm was received from someone besides the Top Provider.
|
|
* Ignore the indication.
|
|
*/
|
|
ERROR_OUT(("Domain::TokenGiveConfirm: invalid originator=0x%p", pOrigConn));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void TokenPleaseRequest ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
*/
|
|
Void Domain::TokenPleaseRequest (
|
|
CAttachment *pOrigAtt,
|
|
UserID uidInitiator,
|
|
TokenID token_id)
|
|
{
|
|
PToken token;
|
|
|
|
/*
|
|
* Make sure the requesting user really exists in the sub-tree from which
|
|
* this request originated.
|
|
*/
|
|
if (ValidateUserID(uidInitiator, pOrigAtt))
|
|
{
|
|
/*
|
|
* If the token already exists in the token list, then let the
|
|
* Token object deal with the request.
|
|
*/
|
|
if (NULL != (token = m_TokenList2.Find(token_id)))
|
|
{
|
|
TRACE_OUT(("Domain::TokenPleaseRequest: sending please request to token object"));
|
|
token->TokenPleaseRequest(uidInitiator, token_id);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Check to see if this is the Top Provider. If it is, then the
|
|
* request can be processed locally. Otherwise, forward the
|
|
* request upward.
|
|
*/
|
|
if (IsTopProvider())
|
|
{
|
|
/*
|
|
* The token being released is not owned by anyone. Report the
|
|
* incident to the diagnostic window, but do nothing. This
|
|
* simply indicates that someone has issued a please request
|
|
* for a token that no one owns.
|
|
*/
|
|
ERROR_OUT(("Domain::TokenPleaseRequest: token does not exist"));
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* This is not the Top Provider. Forward the request upward.
|
|
*/
|
|
TRACE_OUT(("Domain::TokenPleaseRequest: forwarding please request to Top Provider"));
|
|
m_pConnToTopProvider->TokenPleaseRequest(uidInitiator, token_id);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* There is no such user in the sub-tree from which this request
|
|
* originated. Ignore the request.
|
|
*/
|
|
ERROR_OUT(("Domain::TokenPleaseRequest: invalid originator=0x%p", pOrigAtt));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void TokenPleaseIndication ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
*/
|
|
Void Domain::TokenPleaseIndication (
|
|
PConnection pOrigConn,
|
|
UserID uidInitiator,
|
|
TokenID token_id)
|
|
{
|
|
PToken token;
|
|
|
|
/*
|
|
* Verify that the indication came from the Top Provider.
|
|
*/
|
|
if (pOrigConn == m_pConnToTopProvider)
|
|
{
|
|
/*
|
|
* See if the token already exists in the local information base. If
|
|
* so, let it handle this.
|
|
*/
|
|
if (NULL != (token = m_TokenList2.Find(token_id)))
|
|
{
|
|
TRACE_OUT(("Domain::TokenPleaseIndication: sending please indication to token object"));
|
|
token->TokenPleaseIndication(uidInitiator, token_id);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Since token please indication is only sent downward to providers
|
|
* that have owners in their sub-tree, it should not be possible
|
|
* to get here. This indicates that this provider received the
|
|
* indication with NO owners in its sub-tree. Report the error
|
|
* and ignore the indication.
|
|
*/
|
|
ERROR_OUT(("Domain::TokenPleaseIndication: invalid token"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* This indication was received from someone besides the Top Provider.
|
|
* Ignore the indication.
|
|
*/
|
|
ERROR_OUT(("Domain::TokenPleaseIndication: invalid originator=0x%p", pOrigConn));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void TokenReleaseRequest ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
* This MCS command is initially sent by a user attachment that wishes
|
|
* to release a token. It flows upward to the Top Provider, who attempts
|
|
* to satisfy the request.
|
|
*/
|
|
Void Domain::TokenReleaseRequest (
|
|
CAttachment *pOrigAtt,
|
|
UserID uidInitiator,
|
|
TokenID token_id)
|
|
{
|
|
PToken token;
|
|
|
|
/*
|
|
* Make sure the requesting user really exists in the sub-tree from which
|
|
* this request originated.
|
|
*/
|
|
if (ValidateUserID(uidInitiator, pOrigAtt))
|
|
{
|
|
/*
|
|
* If the token already exists in the token list, then let the
|
|
* Token object deal with the request.
|
|
*/
|
|
if (NULL != (token = m_TokenList2.Find(token_id)))
|
|
{
|
|
/*
|
|
* Send the release request to the token object. Then check to
|
|
* see if it is still valid (delete it if not).
|
|
*/
|
|
TRACE_OUT(("Domain::TokenReleaseRequest: sending release request to token object"));
|
|
token->TokenReleaseRequest(pOrigAtt, uidInitiator, token_id);
|
|
if (token->IsValid () == FALSE)
|
|
DeleteToken (token_id);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Check to see if this is the Top Provider. If it is, then the
|
|
* request can be processed locally. Otherwise, forward the
|
|
* request upward.
|
|
*/
|
|
if (IsTopProvider())
|
|
{
|
|
/*
|
|
* The token being released is not owned by anyone. Return
|
|
* a failure to the initiating user.
|
|
*/
|
|
ERROR_OUT(("Domain::TokenReleaseRequest: token does not exist"));
|
|
pOrigAtt->TokenReleaseConfirm(RESULT_TOKEN_NOT_POSSESSED, uidInitiator, token_id, TOKEN_NOT_IN_USE);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* This is not the Top Provider. Forward the request upward.
|
|
*/
|
|
TRACE_OUT(("Domain::TokenReleaseRequest: forwarding release request to Top Provider"));
|
|
m_pConnToTopProvider->TokenReleaseRequest(uidInitiator, token_id);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* There is no such user in the sub-tree from which this request
|
|
* originated. Ignore the request.
|
|
*/
|
|
ERROR_OUT(("Domain::TokenReleaseRequest: invalid originator=0x%p", pOrigAtt));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void TokenReleaseConfirm ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
* This MCS command is initially sent by the Top Provider upon receipt of
|
|
* a release request. It is sent back to the initiating user, containing
|
|
* the result of the request.
|
|
*/
|
|
Void Domain::TokenReleaseConfirm (
|
|
PConnection pOrigConn,
|
|
Result result,
|
|
UserID uidInitiator,
|
|
TokenID token_id,
|
|
TokenStatus token_status)
|
|
{
|
|
PToken token;
|
|
|
|
/*
|
|
* Verify that the confirm came from the Top Provider.
|
|
*/
|
|
if (pOrigConn == m_pConnToTopProvider)
|
|
{
|
|
/*
|
|
* See if the token already exists in the local information base. If
|
|
* so, let it handle this.
|
|
*/
|
|
if (NULL != (token = m_TokenList2.Find(token_id)))
|
|
{
|
|
/*
|
|
* Send the release confirm to the token object. Then check to
|
|
* see if it is still valid (delete it if not).
|
|
*/
|
|
TRACE_OUT(("Domain::TokenReleaseConfirm: sending release confirm to token object"));
|
|
token->TokenReleaseConfirm(result, uidInitiator, token_id, token_status);
|
|
if (token->IsValid () == FALSE)
|
|
DeleteToken (token_id);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Make sure that the requesting user is still somewhere in the
|
|
* sub-tree of this provider.
|
|
*/
|
|
if (ValidateUserID (uidInitiator, NULL) )
|
|
{
|
|
PChannel channel;
|
|
/*
|
|
* Determine which attachment leads to the requesting user.
|
|
* Then forward the confirm in that direction.
|
|
*/
|
|
TRACE_OUT(("Domain::TokenReleaseConfirm: forwarding release confirm"));
|
|
if (NULL != (channel = m_ChannelList2.Find(uidInitiator)))
|
|
{
|
|
CAttachment *pAtt = channel->GetAttachment();
|
|
if (pAtt)
|
|
{
|
|
pAtt->TokenReleaseConfirm(result, uidInitiator, token_id, token_status);
|
|
}
|
|
else
|
|
{
|
|
ERROR_OUT(("Domain::TokenReleaseConfirm: cannot get attachment"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ERROR_OUT(("Domain::TokenReleaseConfirm: cannot find channel"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The named initiator does not exist in the sub-tree of this
|
|
* provider. Ignore the confirm.
|
|
*/
|
|
WARNING_OUT(("Domain::TokenReleaseConfirm: initiator not valid"));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* This confirm was received from someone besides the Top Provider.
|
|
* Ignore the indication.
|
|
*/
|
|
ERROR_OUT(("Domain::TokenReleaseConfirm: invalid originator=0x%p", pOrigConn));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void TokenTestRequest ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
* This MCS command is initially sent by a user attachment that wishes
|
|
* to test a token. It flows upward to the Top Provider, who attempts
|
|
* to satisfy the request.
|
|
*/
|
|
Void Domain::TokenTestRequest (
|
|
CAttachment *pOrigAtt,
|
|
UserID uidInitiator,
|
|
TokenID token_id)
|
|
{
|
|
PToken token;
|
|
|
|
/*
|
|
* Make sure the requesting user really exists in the sub-tree from which
|
|
* this request originated.
|
|
*/
|
|
if (ValidateUserID(uidInitiator, pOrigAtt))
|
|
{
|
|
/*
|
|
* If the token already exists in the token list, then let the Token
|
|
* object deal with the request.
|
|
*/
|
|
if (NULL != (token = m_TokenList2.Find(token_id)))
|
|
{
|
|
TRACE_OUT(("Domain::TokenTestRequest: sending test request to token object"));
|
|
token->TokenTestRequest(pOrigAtt, uidInitiator, token_id);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Check to see if this is the Top Provider. If it is, then the
|
|
* request can be processed locally. Otherwise, forward the
|
|
* request upward.
|
|
*/
|
|
if (IsTopProvider())
|
|
{
|
|
/*
|
|
* If the token is not in the list, send a confirm back to
|
|
* the initiating user telling it that the token is not in use.
|
|
*/
|
|
ERROR_OUT(("Domain::TokenTestRequest: no such token - available"));
|
|
pOrigAtt->TokenTestConfirm(uidInitiator, token_id, TOKEN_NOT_IN_USE);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* This is not the Top Provider. Forward the request upward.
|
|
*/
|
|
TRACE_OUT(("Domain::TokenTestRequest: forwarding test request to Top Provider"));
|
|
m_pConnToTopProvider->TokenTestRequest(uidInitiator, token_id);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* There is no such user in the sub-tree from which this request
|
|
* originated. Ignore the request.
|
|
*/
|
|
ERROR_OUT(("Domain::TokenTestRequest: invalid originator=0x%p", pOrigAtt));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void TokenTestConfirm ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
* This MCS command is initially sent by the Top Provider upon receipt of
|
|
* a test request. It is sent back to the initiating user, containing
|
|
* the result of the request.
|
|
*/
|
|
Void Domain::TokenTestConfirm (
|
|
PConnection pOrigConn,
|
|
UserID uidInitiator,
|
|
TokenID token_id,
|
|
TokenStatus token_status)
|
|
{
|
|
PToken token;
|
|
|
|
/*
|
|
* Verify that the confirm came from the Top Provider.
|
|
*/
|
|
if (pOrigConn == m_pConnToTopProvider)
|
|
{
|
|
/*
|
|
* See if the token already exists in the local information base. If
|
|
* so, let it handle this.
|
|
*/
|
|
if (NULL != (token = m_TokenList2.Find(token_id)))
|
|
{
|
|
TRACE_OUT(("Domain::TokenTestConfirm: sending test confirm to token object"));
|
|
token->TokenTestConfirm(uidInitiator, token_id, token_status);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Make sure that the requesting user is still somewhere in the
|
|
* sub-tree of this provider.
|
|
*/
|
|
if (ValidateUserID (uidInitiator, NULL) )
|
|
{
|
|
PChannel channel;
|
|
/*
|
|
* Determine which attachment leads to the requesting user.
|
|
* Then forward the confirm in that direction.
|
|
*/
|
|
TRACE_OUT(("Domain::TokenTestConfirm: forwarding test confirm"));
|
|
if (NULL != (channel = m_ChannelList2.Find(uidInitiator)))
|
|
{
|
|
CAttachment *pAtt = channel->GetAttachment();
|
|
if (pAtt)
|
|
{
|
|
pAtt->TokenTestConfirm(uidInitiator, token_id, token_status);
|
|
}
|
|
else
|
|
{
|
|
ERROR_OUT(("Domain::TokenTestConfirm: cannot get attachment"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ERROR_OUT(("Domain::TokenTestConfirm: cannot find channel"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The named initiator does not exist in the sub-tree of this
|
|
* provider. Ignore the confirm.
|
|
*/
|
|
ERROR_OUT(("Domain::TokenTestConfirm: initiator not valid uidInitiator=%u", (UINT) uidInitiator));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* This confirm was received from someone besides the Top Provider.
|
|
* Ignore the indication.
|
|
*/
|
|
ERROR_OUT(("Domain::TokenTestConfirm: invalid originator=0x%p", pOrigConn));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void LockDomainParameters ()
|
|
*
|
|
* Private
|
|
*
|
|
* Functional Description:
|
|
* This routine is used to initialize the values of the domain parameters
|
|
* instance variable.
|
|
*
|
|
* Formal Parameters:
|
|
* domain_parameters
|
|
* This is a pointer to the domain parameters structure from which the
|
|
* values are to be obtained. If it is set to NULL, then put a default
|
|
* set of parameters into the instance variable.
|
|
* parameters_locked
|
|
* This parameter indicates whether or not these parameters have been
|
|
* locked into the domain by acceptance of the first connection.
|
|
*
|
|
* Return Value:
|
|
* None.
|
|
*
|
|
* Side Effects:
|
|
* None.
|
|
*/
|
|
Void Domain::LockDomainParameters (
|
|
PDomainParameters domain_parameters,
|
|
BOOL parameters_locked)
|
|
{
|
|
/*
|
|
* If the structure pointer is valid, then copy the structure into the
|
|
* internal instance variable.
|
|
*/
|
|
if (domain_parameters != NULL)
|
|
Domain_Parameters = *domain_parameters;
|
|
else
|
|
{
|
|
/*
|
|
* Set default values for all domain parameters.
|
|
*/
|
|
Domain_Parameters.max_channel_ids = DEFAULT_MAXIMUM_CHANNELS;
|
|
Domain_Parameters.max_user_ids = DEFAULT_MAXIMUM_USERS;
|
|
Domain_Parameters.max_token_ids = DEFAULT_MAXIMUM_TOKENS;
|
|
Domain_Parameters.number_priorities = DEFAULT_NUMBER_OF_PRIORITIES;
|
|
Domain_Parameters.min_throughput = DEFAULT_MINIMUM_THROUGHPUT;
|
|
Domain_Parameters.max_height = DEFAULT_MAXIMUM_DOMAIN_HEIGHT;
|
|
Domain_Parameters.max_mcspdu_size = DEFAULT_MAXIMUM_PDU_SIZE;
|
|
Domain_Parameters.protocol_version = DEFAULT_PROTOCOL_VERSION;
|
|
|
|
if (g_fWinsockDisabled)
|
|
{
|
|
Domain_Parameters.number_priorities = DEFAULT_NUM_PLUGXPRT_PRIORITIES;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Indicate whether or not these parameters are locked.
|
|
*/
|
|
Domain_Parameters_Locked = parameters_locked;
|
|
}
|
|
|
|
/*
|
|
* ChannelID AllocateDynamicChannel ()
|
|
*
|
|
* Private
|
|
*
|
|
* Functional Description:
|
|
* This member function is used to allocate an unused channel ID in the
|
|
* dynamic range (1001 - 65535). It uses a random number generator to
|
|
* perform this task.
|
|
*
|
|
* Formal Parameters:
|
|
* None.
|
|
*
|
|
* Return Value:
|
|
* A channel ID in the dynamic range that is guaranteed to be unused.
|
|
*
|
|
* Side Effects:
|
|
* None.
|
|
*/
|
|
ChannelID Domain::AllocateDynamicChannel ()
|
|
{
|
|
ChannelID channel_id;
|
|
|
|
/*
|
|
* Stay in this loop until a unused channel ID is found. Note that this
|
|
* loop make sthe assumption that there will be at least one unused ID
|
|
* in there somewhere.
|
|
*/
|
|
while (TRUE)
|
|
{
|
|
/*
|
|
* Get a random number in the dynamic channel range.
|
|
*/
|
|
channel_id = (ChannelID) Random_Channel_Generator.GetRandomChannel ();
|
|
|
|
/*
|
|
* If it is not is use, then break out of the loop and return the
|
|
* channel ID.
|
|
*/
|
|
if (! m_ChannelList2.Find(channel_id))
|
|
break;
|
|
}
|
|
|
|
return (channel_id);
|
|
}
|
|
|
|
/*
|
|
* BOOL ValidateUserID ()
|
|
*
|
|
* Private
|
|
*
|
|
* Functional Description:
|
|
* This function is used to validate a user ID. It can be used in one of
|
|
* two ways. If the passed in attachment is NULL, then this routine will
|
|
* check to see if the ID corresponds to a user ID anywhere in the sub-tree
|
|
* of this provider. If the passed in attachment is not NULL, then this
|
|
* routine checks to see if the ID is valid user ID associated with that
|
|
* particular attachment.
|
|
*
|
|
* Formal Parameters:
|
|
* user_id
|
|
* This is the ID to be checked for validity.
|
|
* attachment
|
|
* This is the attachment that is presumably associated with the user
|
|
* ID. If NULL, we are checking for validity irrespective of
|
|
* attachment.
|
|
*
|
|
* Return Value:
|
|
* This routine will return TRUE if the user ID valid. FALSE otherwise.
|
|
*
|
|
* Side Effects:
|
|
* None.
|
|
*/
|
|
BOOL Domain::ValidateUserID (
|
|
UserID user_id,
|
|
CAttachment *pAtt)
|
|
{
|
|
PChannel channel;
|
|
|
|
/*
|
|
* Is the user ID even contained in the channel list.
|
|
*/
|
|
if (NULL != (channel = m_ChannelList2.Find(user_id)))
|
|
{
|
|
/*
|
|
* It is in the channel list. Now check to see if it corresponds to
|
|
* a user ID channel.
|
|
*/
|
|
if (channel->GetChannelType () == USER_CHANNEL)
|
|
{
|
|
/*
|
|
* Check to make sure that the real user attachment matches the
|
|
* passed in one (unless the passed in one is NULL, in which
|
|
* case it automatically matches).
|
|
*/
|
|
if ((pAtt == NULL) || (pAtt == channel->GetAttachment()))
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return (FALSE);
|
|
}
|
|
|
|
/*
|
|
* Void PurgeDomain ()
|
|
*
|
|
* Private
|
|
*
|
|
* Functional Description:
|
|
* This function is used to purge the entire domain. This can happen for
|
|
* two reasons. Either the Top Provider is lost, or the local user has
|
|
* asked for the domain to be deleted. Either way, this function breaks
|
|
* all attachments, and frees up all resources in use by the domain.
|
|
*
|
|
* Formal Parameters:
|
|
* None.
|
|
*
|
|
* Return Value:
|
|
* None.
|
|
*
|
|
* Side Effects:
|
|
* The domain information base is returned to its initial state.
|
|
*/
|
|
Void Domain::PurgeDomain (
|
|
Reason reason)
|
|
{
|
|
CAttachment *pAtt;
|
|
PChannel channel;
|
|
CUidList user_list;
|
|
UserID uid;
|
|
|
|
/*
|
|
* If there is a Top Provider, send a disconnect to it.
|
|
*/
|
|
if (m_pConnToTopProvider != NULL)
|
|
{
|
|
TRACE_OUT(("Domain::PurgeDomain: disconnecting top provider"));
|
|
m_pConnToTopProvider->DisconnectProviderUltimatum (reason);
|
|
m_pConnToTopProvider = NULL;
|
|
}
|
|
|
|
/*
|
|
* Send disconnects to all downward attachments. Then clear out the
|
|
* attachment list.
|
|
*/
|
|
TRACE_OUT(("Domain::PurgeDomain: disconnecting all downward attachments"));
|
|
while (NULL != (pAtt = m_AttachmentList.Get()))
|
|
{
|
|
pAtt->DisconnectProviderUltimatum(reason);
|
|
|
|
/*
|
|
* If there are any pending attach user requests on the attachment
|
|
* that was just broken, delete them. Note that this is a loop
|
|
* because there can be more than one.
|
|
*/
|
|
while (m_AttachUserQueue.Remove(pAtt));
|
|
}
|
|
|
|
/*
|
|
* Send a disconnect to all attachments that represent attach user requests
|
|
* in process. Then clear the queue out.
|
|
*/
|
|
while (NULL != (pAtt = m_AttachUserQueue.Get()))
|
|
{
|
|
pAtt->DisconnectProviderUltimatum(reason);
|
|
}
|
|
|
|
/*
|
|
* Clear the merge queue. The actual attachments have already been broken
|
|
* above.
|
|
*/
|
|
m_MergeQueue.Clear();
|
|
|
|
/*
|
|
* We cannot just delete all channels and tokens, because doing so would
|
|
* cause them to issue various indications to attachments that are no
|
|
* longer valid. To get around this, we must delete all attachments (which
|
|
* was done above) and all user objects from the channel list, and then
|
|
* reclaim unowned resources. This will cause all static, assigned, and
|
|
* private channels, as well as tokens, to delete themselves.
|
|
*/
|
|
m_ChannelList2.Reset();
|
|
while (NULL != (channel = m_ChannelList2.Iterate(&uid)))
|
|
{
|
|
if (channel->GetChannelType () == USER_CHANNEL)
|
|
user_list.Append(uid);
|
|
}
|
|
|
|
/*
|
|
* Delete all users from the channel list. Since there are no valid users
|
|
* in the domain, all resources that are tied to users will be reclaimed
|
|
* below.
|
|
*/
|
|
user_list.Reset();
|
|
while (NULL != (uid = user_list.Iterate()))
|
|
{
|
|
DeleteChannel((ChannelID) uid);
|
|
}
|
|
|
|
/*
|
|
* Reclaim unowned resources. Since all resources (channels and tokens)
|
|
* are tied to the existence of either attachments or users, this call
|
|
* will result in all channels and tokens being cleanly deleted (since
|
|
* there aren't any attachments or users).
|
|
*/
|
|
ReclaimResources ();
|
|
|
|
/*
|
|
* Reset the state to all initial values.
|
|
*/
|
|
Merge_State = MERGE_INACTIVE;
|
|
Outstanding_Merge_Requests = 0;
|
|
Number_Of_Users = 0;
|
|
Number_Of_Channels = 0;
|
|
Number_Of_Tokens = 0;
|
|
m_nDomainHeight = 0;
|
|
m_DomainHeightList2.Clear();
|
|
LockDomainParameters (NULL, FALSE);
|
|
}
|
|
|
|
/*
|
|
* Void DeleteAttachment ()
|
|
*
|
|
* Private
|
|
*
|
|
* Functional Description:
|
|
* This function is used to free up all resources that are "bound" to
|
|
* particular attachment. It also deletes the downward attachment.
|
|
*
|
|
* Formal Parameters:
|
|
* attachment
|
|
* This is the attachment to be deleted.
|
|
* reason
|
|
* This is the reason for the deletion. This is merely passed on in
|
|
* any MCS commands that are sent as a result of this deletion.
|
|
*
|
|
* Return Value:
|
|
* None.
|
|
*
|
|
* Side Effects:
|
|
* Resources in the domain information base are freed up.
|
|
*/
|
|
Void Domain::DeleteAttachment (
|
|
CAttachment *pAtt,
|
|
Reason reason)
|
|
{
|
|
ChannelID chid;
|
|
PChannel channel;
|
|
CUidList user_deletion_list;
|
|
CChannelIDList channel_deletion_list;
|
|
CChannelIDList channel_leave_list;
|
|
|
|
/*
|
|
* Check to make sure the attachment is real before proceeding.
|
|
*/
|
|
if (m_AttachmentList.Remove(pAtt))
|
|
{
|
|
/*
|
|
* Remove the attachment from the downward attachment list.
|
|
*/
|
|
|
|
/*
|
|
* Iterate through the channel list building two lists, as follows:
|
|
*
|
|
* 1. A list of users who lie in the direction of the lost attachment.
|
|
* These users must be deleted from the information base, and
|
|
* their detachment reported appropriately.
|
|
* 2. A list of channels that must be deleted as a result of the lost
|
|
* attachment. This list is created by sending a channel leave
|
|
* request to all channels, and then checking to see if they are
|
|
* still valid. All static and assigned channels that only had
|
|
* that attachment joined will be deleted as a result of this.
|
|
* This also results in the attachment being removed from all
|
|
* channel attachment lists, avoiding the possibility of sending
|
|
* data to an invalid attachment.
|
|
*/
|
|
m_ChannelList2.Reset();
|
|
while (NULL != (channel = m_ChannelList2.Iterate(&chid)))
|
|
{
|
|
/*
|
|
* Check to see if this is a user ID channel whose user lies on the
|
|
* other side of the lost attachment. If so, add the channel to
|
|
* the deletion list.
|
|
*/
|
|
if (channel->GetChannelType () == USER_CHANNEL)
|
|
{
|
|
if (channel->GetAttachment() == pAtt)
|
|
{
|
|
user_deletion_list.Append(chid);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Issue the leave request to the channel. Then check to see if it
|
|
* is still valid. If not, then add it to the deletion list.
|
|
*/
|
|
channel_leave_list.Clear();
|
|
channel_leave_list.Append(chid);
|
|
channel->ChannelLeaveRequest(pAtt, &channel_leave_list);
|
|
if (channel->IsValid () == FALSE)
|
|
channel_deletion_list.Append(chid);
|
|
}
|
|
|
|
/*
|
|
* Iterate through the channel list, deleting the channels it
|
|
* contains.
|
|
*/
|
|
channel_deletion_list.Reset();
|
|
while (NULL != (chid = channel_deletion_list.Iterate()))
|
|
{
|
|
DeleteChannel(chid);
|
|
}
|
|
|
|
/*
|
|
* If there are any users to be deleted, simulate a DetachUserRequest
|
|
* with the list of users to be deleted.
|
|
*/
|
|
if (user_deletion_list.IsEmpty() == FALSE)
|
|
DetachUserRequest(pAtt, reason, &user_deletion_list);
|
|
|
|
/*
|
|
* Check to see if the deleted attachment is represented in the
|
|
* domain height list. If it is, then this loss could result in a
|
|
* change in the overall domain height.
|
|
*/
|
|
if (m_DomainHeightList2.Remove((PConnection) pAtt))
|
|
{
|
|
/*
|
|
* The attachment is in the list. Remove it from the list, and
|
|
* call the subroutine that determines whether an overall height
|
|
* change has occurred that may require further activity.
|
|
*/
|
|
CalculateDomainHeight ();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The named attachment isn't even in the attachment list.
|
|
*/
|
|
ERROR_OUT(("Domain::DeleteAttachment: unknown attachment=0x%p", pAtt));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void DeleteUser ()
|
|
*
|
|
* Private
|
|
*
|
|
* Functional Description:
|
|
* This routine deletes a user from the information base. This is fairly
|
|
* complex task because there are a lot of dependencies on users within
|
|
* the MCS protocol. If the user being deleted is locall attached, then
|
|
* the attachment must be severed. Also, any resources that are being
|
|
* held by the user must be reclaimed. And finally, the user channel
|
|
* object that represents the user must be deleted from the local channel
|
|
* list.
|
|
*
|
|
* Formal Parameters:
|
|
* user_id
|
|
* This is the ID of the user being deleted.
|
|
*
|
|
* Return Value:
|
|
* None.
|
|
*
|
|
* Side Effects:
|
|
* None.
|
|
*/
|
|
Void Domain::DeleteUser (
|
|
UserID user_id)
|
|
{
|
|
CAttachment *pAtt;
|
|
ChannelID chid;
|
|
PChannel channel;
|
|
CChannelIDList deletion_list;
|
|
CChannelIDList channel_leave_list;
|
|
|
|
/*
|
|
* Make sure this is a valid user in the sub-tree of this provider before
|
|
* proceeding.
|
|
*/
|
|
if (ValidateUserID (user_id, NULL) )
|
|
{
|
|
/*
|
|
* Determine which attachment leads to the user in question.
|
|
*/
|
|
if (NULL != (channel = m_ChannelList2.Find(user_id)))
|
|
{
|
|
pAtt = channel->GetAttachment();
|
|
|
|
/*
|
|
* Delete the user channel now that it is no longer necessary.
|
|
*/
|
|
DeleteChannel (user_id);
|
|
|
|
/*
|
|
* Check to see if the user's attachment is still valid. It is
|
|
* possible that the user is being deleted as a result of losing the
|
|
* attachment that leads to it.
|
|
*/
|
|
if (m_AttachmentList.Find(pAtt) && pAtt->IsUserAttachment())
|
|
{
|
|
/*
|
|
* If this user was locally attached, then it is necessary to
|
|
* remove it from the attachment list, as well as making sure that
|
|
* no other channel objects attempt to reference it.
|
|
*/
|
|
/*
|
|
* Remove the attachment from the downward attachment list.
|
|
*/
|
|
TRACE_OUT(("Domain::DeleteUser: deleting local attachment"));
|
|
m_AttachmentList.Remove(pAtt);
|
|
((PUser) pAtt)->Release();
|
|
|
|
/*
|
|
* Iterate through the channel list issuing leave requests to
|
|
* each channel. This prevents a Channel object from trying to
|
|
* send data on an attachment that is no longer valid. This
|
|
* loop also builds a list of Channel objects that should be
|
|
* deleted as a result of this detachment.
|
|
*/
|
|
m_ChannelList2.Reset();
|
|
while (NULL != (channel = m_ChannelList2.Iterate(&chid)))
|
|
{
|
|
/*
|
|
* Issue the leave request to the channel. Then check to
|
|
* see if it is still valid. If not, then add it to the
|
|
* deletion list.
|
|
*/
|
|
channel_leave_list.Clear();
|
|
channel_leave_list.Append(chid);
|
|
channel->ChannelLeaveRequest(pAtt, &channel_leave_list);
|
|
if (channel->IsValid () == FALSE)
|
|
deletion_list.Append(chid);
|
|
}
|
|
|
|
/*
|
|
* Iterator through the deletion list, deleting the channels it
|
|
* contains.
|
|
*/
|
|
deletion_list.Reset();
|
|
while (NULL != (chid = deletion_list.Iterate()))
|
|
{
|
|
DeleteChannel(chid);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Reclaim all resources that may have been freed as a result of the
|
|
* deleted user.
|
|
*/
|
|
ReclaimResources ();
|
|
}
|
|
else
|
|
{
|
|
ERROR_OUT(("Domain::DeleteUser: cannot find channel"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The specified user ID is not valid.
|
|
*/
|
|
ERROR_OUT(("Domain::DeleteUser: unknown user ID"));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void DeleteChannel ()
|
|
*
|
|
* Private
|
|
*
|
|
* Functional Description:
|
|
* This function deleted a channel from the channel list. It also adjusts
|
|
* the appropriate channel counter (according to type), and reports the
|
|
* deletion.
|
|
*
|
|
* Formal Parameters:
|
|
* channel_id
|
|
* This is the ID of the channel to be deleted.
|
|
*
|
|
* Return Value:
|
|
* None.
|
|
*
|
|
* Side Effects:
|
|
* None.
|
|
*/
|
|
Void Domain::DeleteChannel (
|
|
ChannelID channel_id)
|
|
{
|
|
PChannel channel;
|
|
|
|
/*
|
|
* Make sure the channel being deleted is real before proceeding.
|
|
*/
|
|
if (NULL != (channel = m_ChannelList2.Remove(channel_id)))
|
|
{
|
|
/*
|
|
* Report the type of channel being deleted, and decrement the
|
|
* appropriate counter.
|
|
*/
|
|
Number_Of_Channels--;
|
|
switch (channel->GetChannelType ())
|
|
{
|
|
case STATIC_CHANNEL:
|
|
TRACE_OUT (("Domain::DeleteChannel: "
|
|
"deleting static channel ID = %04X", channel_id));
|
|
break;
|
|
|
|
case ASSIGNED_CHANNEL:
|
|
TRACE_OUT (("Domain::DeleteChannel: "
|
|
"deleting assigned channel ID = %04X", channel_id));
|
|
break;
|
|
|
|
case USER_CHANNEL:
|
|
TRACE_OUT (("Domain::DeleteChannel: "
|
|
"deleting user channel ID = %04X", channel_id));
|
|
Number_Of_Users--;
|
|
break;
|
|
|
|
case PRIVATE_CHANNEL:
|
|
TRACE_OUT (("Domain::DeleteChannel: "
|
|
"deleting private channel ID = %04X", channel_id));
|
|
break;
|
|
|
|
default:
|
|
ERROR_OUT (("Domain::DeleteChannel: "
|
|
"ERROR - deleting unknown channel ID = %04X",
|
|
channel_id));
|
|
Number_Of_Channels++;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Delete the channel object.
|
|
*/
|
|
delete channel;
|
|
}
|
|
else
|
|
{
|
|
ERROR_OUT(("Domain::DeleteChannel: unknown channel ID"));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void DeleteToken ()
|
|
*
|
|
* Private
|
|
*
|
|
* Functional Description:
|
|
* This function deletes a token from the token list. It also adjusts
|
|
* the token counter.
|
|
*
|
|
* Formal Parameters:
|
|
* token_id
|
|
* This is the ID of the token to be deleted.
|
|
*
|
|
* Return Value:
|
|
* None.
|
|
*
|
|
* Side Effects:
|
|
* None.
|
|
*/
|
|
Void Domain::DeleteToken (
|
|
TokenID token_id)
|
|
{
|
|
PToken token;
|
|
|
|
/*
|
|
* Check to make sure that the token being deleted is real before
|
|
* proceeding.
|
|
*/
|
|
if (NULL != (token = m_TokenList2.Remove(token_id)))
|
|
{
|
|
/*
|
|
* Remove the token from the token list and delete it.
|
|
*/
|
|
TRACE_OUT(("Domain::DeleteToken: deleting token ID = %04X", (UINT) token_id));
|
|
delete token;
|
|
|
|
/*
|
|
* Decrement the token counter.
|
|
*/
|
|
Number_Of_Tokens--;
|
|
}
|
|
else
|
|
{
|
|
ERROR_OUT(("Domain::DeleteToken: unknown token ID"));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void ReclaimResources ()
|
|
*
|
|
* Private
|
|
*
|
|
* Functional Description:
|
|
* This function walks through both the channel and token lists, removing
|
|
* all objects that are no longer valid. This function just queries each
|
|
* channel and token to see if it is still valid. This allows for the
|
|
* reclamation of resources when a user is deleted.
|
|
*
|
|
* Formal Parameters:
|
|
* None.
|
|
*
|
|
* Return Value:
|
|
* None.
|
|
*
|
|
* Side Effects:
|
|
* None.
|
|
*/
|
|
Void Domain::ReclaimResources ()
|
|
{
|
|
ChannelID chid;
|
|
PChannel channel;
|
|
CChannelIDList channel_deletion_list;
|
|
TokenID tid;
|
|
PToken token;
|
|
CTokenIDList token_deletion_list;
|
|
|
|
/*
|
|
* Iterate through the channel list, asking each channel if it is still
|
|
* valid. Any that are not will be deleted by the next loop.
|
|
*/
|
|
m_ChannelList2.Reset();
|
|
while (NULL != (channel = m_ChannelList2.Iterate(&chid)))
|
|
{
|
|
/*
|
|
* Check to see if the channel is still valid. If not, add it to the
|
|
* deletion list.
|
|
*/
|
|
if (channel->IsValid () == FALSE)
|
|
channel_deletion_list.Append(chid);
|
|
}
|
|
|
|
/*
|
|
* Delete all channels in the deletion list.
|
|
*/
|
|
channel_deletion_list.Reset();
|
|
while (NULL != (chid = channel_deletion_list.Iterate()))
|
|
{
|
|
DeleteChannel(chid);
|
|
}
|
|
|
|
/*
|
|
* Iterate through the token list, asking each token if it is still
|
|
* valid. Any that are not will be deleted by the next loop.
|
|
*/
|
|
m_TokenList2.Reset();
|
|
while (NULL != (token = m_TokenList2.Iterate(&tid)))
|
|
{
|
|
/*
|
|
* Check to see if the token is still valid. If the grabber or
|
|
* inhibitor was the only owner of the token, then it will be freed
|
|
* here.
|
|
*/
|
|
if (token->IsValid () == FALSE)
|
|
token_deletion_list.Append(tid);
|
|
}
|
|
|
|
/*
|
|
* Delete all tokens in the deletion list.
|
|
*/
|
|
while (NULL != (tid = token_deletion_list.Get()))
|
|
{
|
|
DeleteToken(tid);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void MergeInformationBase ()
|
|
*
|
|
* Private
|
|
*
|
|
* Functional Description:
|
|
* This function is essentially a state machine for the domain merger
|
|
* process. Domain merging is currently implemented to only try and
|
|
* merge one type of resource at a time. Each time this routine is
|
|
* called, the next type of resource is merged. After all resources have
|
|
* been merged, this provider ceases to be a Top Provider, and the merge
|
|
* state is returned to inactive.
|
|
*
|
|
* Formal Parameters:
|
|
* None.
|
|
*
|
|
* Return Value:
|
|
* None.
|
|
*
|
|
* Side Effects:
|
|
* Contents of the domain information are merged upward to the Top
|
|
* Provider of the upper domain.
|
|
*/
|
|
Void Domain::MergeInformationBase ()
|
|
{
|
|
MergeState merge_state;
|
|
Channel_Type channel_type;
|
|
PChannel channel;
|
|
PToken token;
|
|
|
|
/*
|
|
* This call is not valid unless there is a Top Provider to merge the
|
|
* information upward to.
|
|
*/
|
|
if (m_pConnToTopProvider != NULL)
|
|
{
|
|
/*
|
|
* As part of the transition to the next merge state, set the number
|
|
* of outstanding merge requests to 0.
|
|
*/
|
|
Outstanding_Merge_Requests = 0;
|
|
merge_state = Merge_State;
|
|
|
|
while (Outstanding_Merge_Requests == 0)
|
|
{
|
|
/*
|
|
* Each case of this switch statement sets two variables. The
|
|
* first is the merge state. This is bumped to the next state
|
|
* in the sequence. The second is the channel type. This is
|
|
* used for controlling which type of channel is being merged
|
|
* upward for this state.
|
|
*/
|
|
switch (merge_state)
|
|
{
|
|
case MERGE_INACTIVE:
|
|
TRACE_OUT(("Domain::MergeInformationBase: merging User IDs"));
|
|
merge_state = MERGE_USER_IDS;
|
|
channel_type = USER_CHANNEL;
|
|
break;
|
|
|
|
case MERGE_USER_IDS:
|
|
TRACE_OUT(("Domain::MergeInformationBase: merging Static Channels"));
|
|
merge_state = MERGE_STATIC_CHANNELS;
|
|
channel_type = STATIC_CHANNEL;
|
|
break;
|
|
|
|
case MERGE_STATIC_CHANNELS:
|
|
TRACE_OUT(("Domain::MergeInformationBase: merging Assigned Channels"));
|
|
merge_state = MERGE_ASSIGNED_CHANNELS;
|
|
channel_type = ASSIGNED_CHANNEL;
|
|
break;
|
|
|
|
case MERGE_ASSIGNED_CHANNELS:
|
|
TRACE_OUT(("Domain::MergeInformationBase: merging Private Channels"));
|
|
merge_state = MERGE_PRIVATE_CHANNELS;
|
|
channel_type = PRIVATE_CHANNEL;
|
|
break;
|
|
|
|
case MERGE_PRIVATE_CHANNELS:
|
|
TRACE_OUT(("Domain::MergeInformationBase: merging Tokens"));
|
|
merge_state = MERGE_TOKENS;
|
|
break;
|
|
|
|
case MERGE_TOKENS:
|
|
TRACE_OUT(("Domain::MergeInformationBase: domain merger complete"));
|
|
merge_state = MERGE_COMPLETE;
|
|
break;
|
|
|
|
default:
|
|
ERROR_OUT(("Domain::MergeInformationBase: invalid merge state"));
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* If the merge is now complete, then this provider must cease
|
|
* to be a Top Provider.
|
|
*/
|
|
if (merge_state == MERGE_COMPLETE)
|
|
{
|
|
/*
|
|
* Reset the merge state, and break out of this loop.
|
|
*/
|
|
merge_state = MERGE_INACTIVE;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Check to see if we are to merge tokens on this pass.
|
|
*/
|
|
if (merge_state == MERGE_TOKENS)
|
|
{
|
|
/*
|
|
* Iterate through the token list, sending merge requests to
|
|
* each Token object. Pass in the identity of the pending
|
|
* Top Provider, so that the Token object knows where to send
|
|
* the MergeTokensRequest. Increment the number of
|
|
* outstanding merge requests.
|
|
*/
|
|
m_TokenList2.Reset();
|
|
while (NULL != (token = m_TokenList2.Iterate()))
|
|
{
|
|
token->IssueMergeRequest ();
|
|
Outstanding_Merge_Requests++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* This must be a merge state for channels. Iterate through
|
|
* the channel list, sending a merge request to each Channel
|
|
* object whose type matches that specified by the merge
|
|
* state that we are. Increment the outstanding merge
|
|
* request counter each time one is sent.
|
|
*/
|
|
m_ChannelList2.Reset();
|
|
while (NULL != (channel = m_ChannelList2.Iterate()))
|
|
{
|
|
if (channel->GetChannelType () == channel_type)
|
|
{
|
|
channel->IssueMergeRequest ();
|
|
Outstanding_Merge_Requests++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SetMergeState (merge_state);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* This routine has been called when the domain is not in the
|
|
* appropriate state.
|
|
*/
|
|
ERROR_OUT(("Domain::MergeInformationBase: unable to merge at this time"));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void SetMergeState ()
|
|
*
|
|
* Private
|
|
*
|
|
* Functional Description:
|
|
* This function sets the merge state to the passed in value. It also
|
|
* detects the transition between MERGE_INACTIVE and any other state.
|
|
* This transition causes the domain to issue MergeDomainIndication to
|
|
* all downward attachments.
|
|
*
|
|
* Formal Parameters:
|
|
* merge_state
|
|
* This is the merge state that we are moving to.
|
|
*
|
|
* Return Value:
|
|
* None.
|
|
*
|
|
* Side Effects:
|
|
* None.
|
|
*/
|
|
Void Domain::SetMergeState (
|
|
MergeState merge_state)
|
|
{
|
|
CAttachment *pAtt;
|
|
|
|
/*
|
|
* Don't do anything unless the merge state is actually changing.
|
|
*/
|
|
if (Merge_State != merge_state)
|
|
{
|
|
/*
|
|
* If the old state is inactive, then that means that we are just
|
|
* beginning a domain merge operation. If this is the case, then
|
|
* iterate through the downward attachment list, telling all
|
|
* attachments about the domain merge.
|
|
*
|
|
* Note that a side effect of this call is that all MCS commands
|
|
* are shut off from the attachments that receive it. This allows the
|
|
* domain information base to remain stable during a merge operation.
|
|
*/
|
|
if (Merge_State == MERGE_INACTIVE)
|
|
{
|
|
m_AttachmentList.Reset();
|
|
while (NULL != (pAtt = m_AttachmentList.Iterate()))
|
|
{
|
|
pAtt->MergeDomainIndication(MERGE_DOMAIN_IN_PROGRESS);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Set the merge state.
|
|
*/
|
|
Merge_State = merge_state;
|
|
|
|
/*
|
|
* If the new state is inactive, then that means that we have just
|
|
* completed a domain merge operation. If this is the case, then
|
|
* iterate through the downward attachment list, telling all
|
|
* attachments about the completion of the merge.
|
|
*
|
|
* Note that a side effect of this call is to re-enable MCS commands
|
|
* from the attachments that receive it.
|
|
*/
|
|
if (Merge_State == MERGE_INACTIVE)
|
|
{
|
|
m_AttachmentList.Reset();
|
|
while (NULL != (pAtt = m_AttachmentList.Iterate()))
|
|
{
|
|
pAtt->MergeDomainIndication(MERGE_DOMAIN_COMPLETE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void AddChannel ()
|
|
*
|
|
* Private
|
|
*
|
|
* Functional Description:
|
|
* This function is used to add a channel to the channel list during a
|
|
* merge channel operation. This type of channel addition works a little
|
|
* differently, since we do not want to send confirms to the users, but
|
|
* rather, to former Top Provider of the lower domain.
|
|
*
|
|
* Formal Parameters:
|
|
* attachment
|
|
* This is the initial attachment that the channel is to have joined
|
|
* to it.
|
|
* merge_channel
|
|
* This is a pointer to a channel attributes structure containing the
|
|
* attributes of the channel to be added.
|
|
* merge_channel_list
|
|
* This is a list of channel attribute structures for those channels
|
|
* that were successfully merged into the domain information base. It
|
|
* will be used to issue the merge channels confirm downward.
|
|
* purge_channel_list
|
|
* This is a list of channel IDs for those channels that were not
|
|
* successfully merged into the domain information base. It will be
|
|
* used to issue the merge channels confirm downward.
|
|
*
|
|
* Return Value:
|
|
* None.
|
|
*
|
|
* Side Effects:
|
|
* None.
|
|
*/
|
|
Void Domain::AddChannel (
|
|
PConnection pConn,
|
|
PChannelAttributes merge_channel,
|
|
CChannelAttributesList *merge_channel_list,
|
|
CChannelIDList *purge_channel_list)
|
|
{
|
|
Channel_Type channel_type;
|
|
ChannelID channel_id=0;
|
|
BOOL joined;
|
|
ChannelID channel_manager=0;
|
|
CUidList *admitted_list;
|
|
PChannel channel=NULL;
|
|
CUidList detach_user_list;
|
|
CChannelIDList channel_leave_list;
|
|
|
|
/*
|
|
* Determine what has to be done according to the channel type.
|
|
*/
|
|
channel_type = merge_channel->channel_type;
|
|
switch (channel_type)
|
|
{
|
|
case STATIC_CHANNEL:
|
|
/*
|
|
* Get the channel ID from the attributes structure.
|
|
*/
|
|
channel_id = merge_channel->u.static_channel_attributes.channel_id;
|
|
|
|
/*
|
|
* If this is the Top Provider, check to see if current
|
|
* domain parameters will permit the merge.
|
|
*/
|
|
if ((m_pConnToTopProvider == NULL) &&
|
|
(Number_Of_Channels >= Domain_Parameters.max_channel_ids))
|
|
{
|
|
ERROR_OUT(("Domain::AddChannel: too many channels"));
|
|
channel = NULL;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Attempt to create a new Channel object.
|
|
*/
|
|
TRACE_OUT(("Domain::AddChannel: adding new channel ID = %04X", (UINT) channel_id));
|
|
DBG_SAVE_FILE_LINE
|
|
channel = new Channel(channel_id, this, m_pConnToTopProvider, &m_ChannelList2,
|
|
&m_AttachmentList, pConn);
|
|
/*
|
|
* Increment the number of channels if everything went okay.
|
|
*/
|
|
if (channel != NULL)
|
|
Number_Of_Channels++;
|
|
break;
|
|
|
|
case USER_CHANNEL:
|
|
/*
|
|
* Get the channel ID from the attributes structure.
|
|
*/
|
|
joined = merge_channel->u.user_channel_attributes.joined;
|
|
channel_id = merge_channel->u.user_channel_attributes.user_id;
|
|
|
|
/*
|
|
* If this is the Top Provider, check to see if current
|
|
* domain parameters will permit the merge.
|
|
*/
|
|
if ((m_pConnToTopProvider == NULL) &&
|
|
((Number_Of_Users >= Domain_Parameters.max_user_ids) ||
|
|
(Number_Of_Channels >= Domain_Parameters.max_channel_ids)))
|
|
{
|
|
ERROR_OUT(("Domain::AddChannel: too many users"));
|
|
channel = NULL;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Attempt to create a new UserChannel object to represent the
|
|
* merged user ID.
|
|
*/
|
|
TRACE_OUT(("Domain::AddChannel: adding new user ID = %04X", (UINT) channel_id));
|
|
|
|
DBG_SAVE_FILE_LINE
|
|
channel = new UserChannel(channel_id, pConn, this, m_pConnToTopProvider,
|
|
&m_ChannelList2, &m_AttachmentList, joined ? pConn : NULL);
|
|
/*
|
|
* Increment the number of users if everything went okay.
|
|
*/
|
|
if (channel != NULL)
|
|
{
|
|
Number_Of_Users++;
|
|
Number_Of_Channels++;
|
|
}
|
|
break;
|
|
|
|
case PRIVATE_CHANNEL:
|
|
/*
|
|
* Get the channel ID and the channel manager ID from the
|
|
* attributes structure.
|
|
*/
|
|
joined = merge_channel->u.private_channel_attributes.joined;
|
|
channel_id = merge_channel->u.private_channel_attributes.channel_id;
|
|
channel_manager = merge_channel->
|
|
u.private_channel_attributes.channel_manager;
|
|
admitted_list = merge_channel->
|
|
u.private_channel_attributes.admitted_list;
|
|
|
|
/*
|
|
* If this is the Top Provider, check to see if current
|
|
* domain parameters will permit the merge.
|
|
*/
|
|
if ((m_pConnToTopProvider == NULL) &&
|
|
(Number_Of_Channels >= Domain_Parameters.max_channel_ids))
|
|
{
|
|
ERROR_OUT(("Domain::AddChannel: too many channels"));
|
|
channel = NULL;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Attempt to create a new PrivateChannel object.
|
|
*/
|
|
TRACE_OUT(("Domain::AddChannel: adding new private channel ID = %04X", (UINT) channel_id));
|
|
|
|
DBG_SAVE_FILE_LINE
|
|
channel = new PrivateChannel(channel_id, channel_manager, this, m_pConnToTopProvider,
|
|
&m_ChannelList2, &m_AttachmentList, admitted_list,
|
|
joined ? pConn : NULL);
|
|
/*
|
|
* Increment the number of channels if everything went okay.
|
|
*/
|
|
if (channel != NULL)
|
|
Number_Of_Channels++;
|
|
break;
|
|
|
|
case ASSIGNED_CHANNEL:
|
|
/*
|
|
* Get the channel ID from the attributes structure.
|
|
*/
|
|
channel_id = merge_channel->
|
|
u.assigned_channel_attributes.channel_id;
|
|
|
|
/*
|
|
* If this is the Top Provider, check to see if current
|
|
* domain parameters will permit the merge.
|
|
*/
|
|
if ((m_pConnToTopProvider == NULL) &&
|
|
(Number_Of_Channels >= Domain_Parameters.max_channel_ids))
|
|
{
|
|
ERROR_OUT(("Domain::AddChannel: too many channels"));
|
|
channel = NULL;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Attempt to create a new Channel object.
|
|
*/
|
|
TRACE_OUT(("Domain::AddChannel: adding new channel ID = %04X", (UINT) channel_id));
|
|
|
|
DBG_SAVE_FILE_LINE
|
|
channel = new Channel(channel_id, this, m_pConnToTopProvider, &m_ChannelList2,
|
|
&m_AttachmentList, pConn);
|
|
/*
|
|
* Increment the number of channels if everything went okay.
|
|
*/
|
|
if (channel != NULL)
|
|
Number_Of_Channels++;
|
|
break;
|
|
}
|
|
|
|
if (channel != NULL)
|
|
{
|
|
/*
|
|
* The channel was successfully created. Add it to the channel list
|
|
* and add the channel structure to the merge channel list, which is
|
|
* used to issue the merge channels confirm downward.
|
|
*/
|
|
if(m_ChannelList2.Insert(channel_id, channel))
|
|
{
|
|
merge_channel_list->Append(merge_channel);
|
|
}
|
|
else
|
|
{
|
|
Number_Of_Channels--;
|
|
delete channel;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The channel merge operation has failed. We need to tell whoever
|
|
* is interested in this situation.
|
|
*/
|
|
WARNING_OUT(("Domain::AddChannel: channel merger failed"));
|
|
|
|
if (m_pConnToTopProvider != NULL)
|
|
{
|
|
/*
|
|
* If this is not the Top Provider, then the Top Provider needs
|
|
* to be told about the problem. If this is a user channel, then
|
|
* issue a detach user request. If it is a normal channel, issue
|
|
* a channel leave request. If it is a private channel, issue a
|
|
* channel disband request.
|
|
*/
|
|
switch (channel_type)
|
|
{
|
|
case STATIC_CHANNEL:
|
|
case ASSIGNED_CHANNEL:
|
|
TRACE_OUT(("Domain::AddChannel: sending ChannelLeaveRequest to Top Provider"));
|
|
channel_leave_list.Append(channel_id);
|
|
m_pConnToTopProvider->ChannelLeaveRequest(&channel_leave_list);
|
|
break;
|
|
|
|
case USER_CHANNEL:
|
|
TRACE_OUT(("Domain::AddChannel: sending DetachUserRequest to Top Provider"));
|
|
detach_user_list.Append(channel_id);
|
|
m_pConnToTopProvider->DetachUserRequest(REASON_PROVIDER_INITIATED, &detach_user_list);
|
|
break;
|
|
|
|
case PRIVATE_CHANNEL:
|
|
TRACE_OUT(("Domain::AddChannel: sending ChannelDisbandRequest to Top Provider"));
|
|
m_pConnToTopProvider->ChannelDisbandRequest(channel_manager, channel_id);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Since the merge has failed, we need to put the channel ID into the
|
|
* purge channel list (which is used to issue the merge channels
|
|
* confirm downward).
|
|
*/
|
|
purge_channel_list->Append(channel_id);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void AddToken ()
|
|
*
|
|
* Private
|
|
*
|
|
* Functional Description:
|
|
* This function is used to add a token to the token list during a
|
|
* merge token operation. This type of token addition works a little
|
|
* differently, since we do not want to send confirms to the owners of
|
|
* the token, but rather, to the former Top Provider of the lower domain.
|
|
*
|
|
* Formal Parameters:
|
|
* merge_token
|
|
* This is a pointer to a token attributes structure containing the
|
|
* attributes of the token to be added.
|
|
* merge_token_list
|
|
* This is a list of token attribute structures for those tokens
|
|
* that were successfully merged into the domain information base. It
|
|
* will be used to issue the merge tokens confirm downward.
|
|
* purge_token_list
|
|
* This is a list of token IDs for those tokens that were not
|
|
* successfully merged into the domain information base. It will be
|
|
* used to issue the merge tokens confirm downward.
|
|
*
|
|
* Return Value:
|
|
* None.
|
|
*
|
|
* Side Effects:
|
|
* None.
|
|
*/
|
|
Void Domain::AddToken (
|
|
PTokenAttributes merge_token,
|
|
CTokenAttributesList *merge_token_list,
|
|
CTokenIDList *purge_token_list)
|
|
{
|
|
TokenState token_state;
|
|
TokenID token_id;
|
|
UserID grabber;
|
|
CUidList *inhibitor_list;
|
|
UserID recipient;
|
|
PToken token;
|
|
UserID uid;
|
|
|
|
/*
|
|
* Determine what state the token to be merged is in. Then get the
|
|
* required information for each particular state.
|
|
*/
|
|
token_state = merge_token->token_state;
|
|
switch (token_state)
|
|
{
|
|
case TOKEN_GRABBED:
|
|
token_id = merge_token->u.grabbed_token_attributes.token_id;
|
|
grabber = merge_token->u.grabbed_token_attributes.grabber;
|
|
inhibitor_list = NULL;
|
|
recipient = 0;
|
|
break;
|
|
|
|
case TOKEN_INHIBITED:
|
|
token_id = merge_token->u.inhibited_token_attributes.token_id;
|
|
grabber = 0;
|
|
inhibitor_list = merge_token->
|
|
u.inhibited_token_attributes.inhibitors;
|
|
recipient = 0;
|
|
break;
|
|
|
|
case TOKEN_GIVING:
|
|
token_id = merge_token->u.giving_token_attributes.token_id;
|
|
grabber = merge_token->u.giving_token_attributes.grabber;
|
|
inhibitor_list = NULL;
|
|
recipient = merge_token->u.giving_token_attributes.recipient;
|
|
break;
|
|
|
|
case TOKEN_GIVEN:
|
|
token_id = merge_token->u.given_token_attributes.token_id;
|
|
grabber = 0;
|
|
inhibitor_list = NULL;
|
|
recipient = merge_token->u.given_token_attributes.recipient;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Check to see if it is okay to add this token. If we are the top
|
|
* provider, and adding this token would cause us to exceed the arbitrated
|
|
* limit on tokens, then we must fail the request.
|
|
*/
|
|
if ((m_pConnToTopProvider != NULL) ||
|
|
(Number_Of_Tokens < Domain_Parameters.max_token_ids))
|
|
{
|
|
/*
|
|
* Create a new token with all merged values as determined above.
|
|
*/
|
|
DBG_SAVE_FILE_LINE
|
|
token = new Token(token_id, this, m_pConnToTopProvider, &m_ChannelList2,
|
|
&m_AttachmentList, token_state, grabber, inhibitor_list, recipient);
|
|
if (token != NULL)
|
|
{
|
|
/*
|
|
* If the creation was successful, add the token to the list and
|
|
* add the token attributes structure to the merge token list.
|
|
*/
|
|
TRACE_OUT(("Domain::AddToken: add new token ID = %04X", (UINT) token_id));
|
|
if(m_TokenList2.Append(token_id, token))
|
|
{
|
|
Number_Of_Tokens++;
|
|
merge_token_list->Append(merge_token);
|
|
}
|
|
else
|
|
{
|
|
delete token;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The token allocation has failed. It is therefore necessary to
|
|
* perform some cleanup operations.
|
|
*/
|
|
WARNING_OUT (("Domain::AddToken: token allocation failed"));
|
|
|
|
/*
|
|
* Check to see if this is the top provider. If not, then it
|
|
* is necessary to issue appropriate requests upward to free the
|
|
* token from the information bases above.
|
|
*/
|
|
if (m_pConnToTopProvider != NULL)
|
|
{
|
|
/*
|
|
* Determine which state the token is in. This affects how
|
|
* the cleanup needs to work.
|
|
*/
|
|
switch (token_state)
|
|
{
|
|
case TOKEN_GRABBED:
|
|
/*
|
|
* If the token is grabbed, then issue a release to
|
|
* free it above.
|
|
*/
|
|
m_pConnToTopProvider->TokenReleaseRequest(grabber, token_id);
|
|
break;
|
|
|
|
case TOKEN_INHIBITED:
|
|
{
|
|
/*
|
|
* Iterate through the inhibitor list, issuing a
|
|
* release request for each user contained therein.
|
|
* This will result in the token being freed at all
|
|
* upward providers.
|
|
*/
|
|
inhibitor_list->Reset();
|
|
while (NULL != (uid = inhibitor_list->Iterate()))
|
|
{
|
|
m_pConnToTopProvider->TokenReleaseRequest(uid, token_id);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case TOKEN_GIVING:
|
|
/*
|
|
* If the token is being given from one user to
|
|
* another, issue a release on behalf of the current
|
|
* owner, and a rejected give response on behalf
|
|
* of the recipient.
|
|
*
|
|
* WARNING:
|
|
* This will cause the current owner to receive a
|
|
* release confirm with no outstanding request.
|
|
*/
|
|
m_pConnToTopProvider->TokenReleaseRequest(grabber, token_id);
|
|
m_pConnToTopProvider->TokenGiveResponse(RESULT_USER_REJECTED,
|
|
recipient, token_id);
|
|
break;
|
|
|
|
case TOKEN_GIVEN:
|
|
/*
|
|
* Issue a rejected give response on behalf of the
|
|
* user that is being offered the token.
|
|
*/
|
|
m_pConnToTopProvider->TokenGiveResponse(RESULT_USER_REJECTED,
|
|
recipient, token_id);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Add the token ID to the purge token list, which will be passed
|
|
* downward to the former top provider of the lower domain. This
|
|
* will tell that provider that the token was NOT accepted in the
|
|
* upper domain.
|
|
*/
|
|
purge_token_list->Append(token_id);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The upper domain already has the domain limit of tokens. So
|
|
* automatically reject the merge request.
|
|
*/
|
|
ERROR_OUT(("Domain::AddToken: too many tokens - rejecting merge"));
|
|
purge_token_list->Append(token_id);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void CalculateDomainHeight ()
|
|
*
|
|
* Private
|
|
*
|
|
* Functional Description:
|
|
* This function is called whenever an event occurs that could have
|
|
* resulted in a change in the overall height of the domain. This includes
|
|
* making and breaking connections, and the reception of an erect domain
|
|
* request from a lower provider.
|
|
*
|
|
* This routine will adjust the height of the current provider, and if
|
|
* this is the top provider, will take necessary steps to insure that the
|
|
* arbitrated domain parameters are not violated.
|
|
*
|
|
* Formal Parameters:
|
|
* None.
|
|
*
|
|
* Return Value:
|
|
* None.
|
|
*
|
|
* Side Effects:
|
|
* None.
|
|
*/
|
|
Void Domain::CalculateDomainHeight ()
|
|
{
|
|
UINT_PTR domain_height;
|
|
UINT_PTR temp_domain_height;
|
|
CAttachment *pAtt;
|
|
|
|
/*
|
|
* Initialize domain height to zero. This will only be increased if there
|
|
* is at least one remote attachment below this one.
|
|
*/
|
|
domain_height = 0;
|
|
|
|
/*
|
|
* Check to see if there is anyone below this provider that would affect
|
|
* its height in the domain (this would be remote attachments that have
|
|
* issued ErectDomainRequests to tell this provider of their height).
|
|
*/
|
|
if (m_DomainHeightList2.IsEmpty() == FALSE)
|
|
{
|
|
/*
|
|
* Iterate through the domain height list to determine which downward
|
|
* attachment has the greatest height. This is the height that will be
|
|
* used to determine height of this provider.
|
|
*/
|
|
m_DomainHeightList2.Reset();
|
|
while (NULL != (temp_domain_height = m_DomainHeightList2.Iterate()))
|
|
{
|
|
if (domain_height < temp_domain_height)
|
|
domain_height = temp_domain_height;
|
|
}
|
|
|
|
/*
|
|
* The height of this provider is one greater than the height of its
|
|
* highest downward attachment.
|
|
*/
|
|
domain_height++;
|
|
}
|
|
|
|
/*
|
|
* Compare the calculated domain height with the current domain height.
|
|
* If they are the same, then no further action needs to be taken.
|
|
*/
|
|
if (domain_height != m_nDomainHeight)
|
|
{
|
|
TRACE_OUT(("Domain::CalculateDomainHeight: new domain height = %d", (UINT) domain_height));
|
|
m_nDomainHeight = domain_height;
|
|
|
|
/*
|
|
* The domain height has changed. We need to verify that the
|
|
* arbitrated domain height has not been violated.
|
|
*/
|
|
if (m_nDomainHeight > Domain_Parameters.max_height)
|
|
{
|
|
/*
|
|
* The new domain height is invalid. We must issue a plumb
|
|
* domain indication downward to enforce the arbitrated
|
|
* domain height.
|
|
*/
|
|
TRACE_OUT(("Domain::CalculateDomainHeight: issuing plumb domain indication"));
|
|
m_AttachmentList.Reset();
|
|
while (NULL != (pAtt = m_AttachmentList.Iterate()))
|
|
{
|
|
pAtt->PlumbDomainIndication(Domain_Parameters.max_height);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If this is not the Top Provider, then it is necessary to transmit an
|
|
* erect domain request upward to inform the upper domain of the
|
|
* change.
|
|
*/
|
|
if (m_pConnToTopProvider != NULL)
|
|
{
|
|
/*
|
|
* Issue an erect domain request upward to inform the upper
|
|
* domain of the change in height. Without this, the Top Provider
|
|
* would have no way of determining when the domain height is
|
|
* invalid.
|
|
*/
|
|
m_pConnToTopProvider->ErectDomainRequest(m_nDomainHeight, DEFAULT_THROUGHPUT_ENFORCEMENT_INTERVAL);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
PUser CAttachmentList::IterateUser(void)
|
|
{
|
|
CAttachment *pAtt;
|
|
while (NULL != (pAtt = Iterate()))
|
|
{
|
|
if (pAtt->IsUserAttachment())
|
|
{
|
|
return (PUser) pAtt;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
PConnection CAttachmentList::IterateConn(void)
|
|
{
|
|
CAttachment *pAtt;
|
|
while (NULL != (pAtt = Iterate()))
|
|
{
|
|
if (pAtt->IsConnAttachment())
|
|
{
|
|
return (PConnection) pAtt;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
|
|
|