#include "precomp.h" #include "fsdiag.h" DEBUG_FILEZONE(ZONE_T120_MCSNC); /* * token.cpp * * Copyright (c) 1993 - 1995 by DataBeam Corporation, Lexington, KY * * Abstract: * This is the implementation file for the Token class. It contains all * code necessary to implement tokens as defined in the MCS specification. * * Whenever a user allocates a token (by grabbing or inhibiting it), one * of these objects is created (if domain parameters allow it). This * object then handles all requests related to that token ID. It also * issues confirms back to the originators of those requests. * * This class includes code to maintain a list of user IDs that * correspond to the current "owners" of the token. A user is said to * own a token if it has it grabbed or inhibited. This code implements * the rules concerning who can grab or inhibit tokens at any given * time (which is affected by current state). * * This class also contains the code that allows a current grabber of * the token to give it away to another user in the domain. * * This class also includes code to merge itself upward during a domain * merge operation. * * Private Instance Variables: * Token_ID * This is the token ID for the token that this object represents. * m_pDomain * This is a pointer to the local provider (the domain that owns this * token). This field is used when a command is issued on behalf of * this provider. * m_pConnToTopProvider * This is the top provider of the current domain. * m_pChannelList2 * This is the channel list that is maintained by the domain. It is * used by this class to perform validation of user IDs. * m_pAttachmentList * This is the attachment list that is maintained by the domain. It is * used by this class to determine what users are locally attached, * when it becomes necessary to send certain indications. * Token_State * This contains the current state of the token, which will be one of * the following: available; grabbed; inhibited; giving; or given. * m_uidGrabber * This is the user that current has the token grabbed. This variable * is only valid in the grabbed and giving states. * m_InhibitorList * This is a list of users that have the token inhibited. This * list is only valid when the token is in the inhibited state. * m_uidRecipient * This is the user to whom the token is being given. This variable * is only valid in the giving or given states. * * Private Member Functions: * ValidateUserID * This function is used to verify that a specified user is valid in * the sub-tree of the local provider. * GetUserAttachment * This function is used to determine which attachment leads to a * particular attachment. * IssueTokenReleaseIndication * This function is used to issue a token release indication to a * specified user. It first checks to see if the user is locally * attached, and if so, it sends the indication. * BuildAttachmentList * This function is used to build a list of unique attachments to * send please indications to. * * Caveats: * None. * * Author: * James P. Galvin, Jr. */ /* * External Interfaces */ #include "token.h" /* * Token () * * Public * * Functional Description: * This is the constructor for the token class. It does nothing more than * set the initial states of instance variables. */ Token::Token ( TokenID token_id, PDomain local_provider, PConnection top_provider, CChannelList2 *channel_list, CAttachmentList *attachment_list) : m_InhibitorList(), Token_ID(token_id), m_pDomain(local_provider), m_pConnToTopProvider(top_provider), m_pChannelList2(channel_list), m_pAttachmentList(attachment_list), Token_State(TOKEN_AVAILABLE) { /* * Save all parameters in their associated instance variables for later * use. */ /* * Mark the token as available for use. */ } /* * Token () * * Public * * Functional Description: * This is an alternate constructor for the token class. It is used when * creating a token during a merge operation. It accepts a current state * as well as a list of current owners as parameters. */ Token::Token ( TokenID token_id, PDomain local_provider, PConnection top_provider, CChannelList2 *channel_list, CAttachmentList *attachment_list, TokenState token_state, UserID grabber, CUidList *inhibitor_list, UserID recipient) : m_InhibitorList(), Token_ID(token_id), m_pDomain(local_provider), m_pConnToTopProvider(top_provider), m_pChannelList2(channel_list), m_pAttachmentList(attachment_list), Token_State(token_state) { UserID uid; /* * Save all parameters in their associated instance variables for later * use. */ /* * Indicate the current state of the token (as passed in). */ /* * Depending on token state, copy the pertinent information into local * instance variables. */ switch (Token_State) { case TOKEN_AVAILABLE: break; case TOKEN_GRABBED: m_uidGrabber = grabber; break; case TOKEN_INHIBITED: { /* * Add all user IDs in the inhibitor list to the local * inhibitor list. */ inhibitor_list->Reset(); while (NULL != (uid = inhibitor_list->Iterate())) { m_InhibitorList.Append(uid); } } break; case TOKEN_GIVING: m_uidGrabber = grabber; m_uidRecipient = recipient; break; case TOKEN_GIVEN: m_uidRecipient = recipient; break; } } /* * ~Token () * * Public * * Functional Description: * This is the token destructor. It iterates through its current owner * list, issuing TokenReleaseIndications to any owners that correspond * to locally attached users. */ Token::~Token () { /* * Depending on the current state of the token, release resources and * issue release indications to all owners. */ switch (Token_State) { case TOKEN_AVAILABLE: break; case TOKEN_GRABBED: /* * Send a release indication to the grabber, if it is locally * attached. */ IssueTokenReleaseIndication (m_uidGrabber); break; case TOKEN_INHIBITED: { UserID uid; /* * Iterate through the current inhibitor list, to make sure * that everyone is properly informed of the demise of this * token. */ m_InhibitorList.Reset(); while (NULL != (uid = m_InhibitorList.Iterate())) { IssueTokenReleaseIndication(uid); } } break; case TOKEN_GIVING: /* * Send a release indication to the grabber, if it is locally * attached. */ IssueTokenReleaseIndication (m_uidGrabber); /* * Send a release indication to the recipient, if it is locally * attached. Note that this will not be sent in the case where * the grabber and the recipient are one and the same. This * prevents the sending of two release indications to the same * user for the same token. */ if (m_uidGrabber != m_uidRecipient) IssueTokenReleaseIndication (m_uidRecipient); break; case TOKEN_GIVEN: /* * Send a release indication to the recipient, if it is locally * attached. */ IssueTokenReleaseIndication (m_uidRecipient); break; } } /* * BOOL IsValid () * * Public * * Functional Description: * This function checks the validity of each of its owners. It then * returns TRUE if there are any valid owners left. FALSE otherwise. */ BOOL Token::IsValid () { BOOL valid; /* * We must check for the validity of this token. How this is checked for * is a function of token state. So switch on the state. */ switch (Token_State) { case TOKEN_AVAILABLE: break; case TOKEN_GRABBED: /* * When a token is grabbed, the grabber must be in the sub-tree * of the current provider. If this is not true, then mark the * token as available (which will cause it to be deleted). */ if (ValidateUserID (m_uidGrabber) == FALSE) Token_State = TOKEN_AVAILABLE; break; case TOKEN_INHIBITED: { UserID uid; CUidList deletion_list; /* * Iterate through the current inhibitor list of this token, * checking to make sure that each user is still valid. Each * one that is not will be put into a deletion list (it is * invalid to remove items from a list while using an iterator * on the list). */ m_InhibitorList.Reset(); while (NULL != (uid = m_InhibitorList.Iterate())) { if (ValidateUserID(uid) == FALSE) deletion_list.Append(uid); } /* * Iterate through the deletion list that was built above, * removing each contained user from the token's inhibitor * list. These correspond to users that have detached from the * domain for one reason or another. */ deletion_list.Reset(); while (NULL != (uid = deletion_list.Iterate())) { m_InhibitorList.Remove(uid); } } /* * Check to see if there are any inhibitors left. If not, then * we must change the state of the token to available (which will * cause it to be deleted). */ if (m_InhibitorList.IsEmpty()) Token_State = TOKEN_AVAILABLE; break; case TOKEN_GIVING: /* * When a token is in the giving state, the recipient must be in * the sub-tree of the current provider. If it is not, then the * token MUST change state. The state it changes to depends on * whether or not the grabber is in the sub-tree of the current * provider. */ if (ValidateUserID (m_uidRecipient) == FALSE) { /* * The recipient of the token is gone. Check to see if the * grabber is in the sub-tree of this provider. */ if (ValidateUserID (m_uidGrabber) == FALSE) { /* * The grabber is not in the sub-tree of this provider, * meaning that the token is no longer valid. */ Token_State = TOKEN_AVAILABLE; } else { /* * The grabber is in the sub-tree of this provider, so the * token state will transition back to grabbed. */ Token_State = TOKEN_GRABBED; /* * If this is the top provider, it is necessary to issue a * give confirm to the grabber telling it that the give * failed. */ if (m_pConnToTopProvider == NULL) { /* * Find out what attachment leads to the current * grabber of the token, and issue the appropriate * token give confirm. */ CAttachment *pAtt = GetUserAttachment(m_uidGrabber); if (pAtt) { pAtt->TokenGiveConfirm(RESULT_NO_SUCH_USER, m_uidGrabber, Token_ID, TOKEN_SELF_GRABBED); } } } } break; case TOKEN_GIVEN: /* * When a token is in the given state, the recipient must be in * the sub-tree of the current provider. If it is not, then the * token is no longer valid, and should transition to the * available state. */ if (ValidateUserID (m_uidRecipient) == FALSE) Token_State = TOKEN_AVAILABLE; break; } /* * Check to see if the token is still in use. If it is marked as * available, then it is not, and we will return FALSE. */ if (Token_State != TOKEN_AVAILABLE) valid = TRUE; else valid = FALSE; return (valid); } /* * Void IssueMergeRequest () * * Public * * Functional Description: * This function tells the token object to pack its state into a merge * request and send it to the specified provider. */ Void Token::IssueMergeRequest () { TokenAttributes merge_token; CTokenAttributesList merge_token_list; CTokenIDList purge_token_list; if (m_pConnToTopProvider != NULL) { /* * Check the state to make sure that the token really is in use. If * the state is set to available, then do not issue a merge request. */ if (Token_State != TOKEN_AVAILABLE) { /* * Fill in a token attributes structure to represent the state of * this token. Then put it into the merge token list in * preparation for issuing the merge request. */ merge_token.token_state = Token_State; switch (Token_State) { case TOKEN_GRABBED: merge_token.u.grabbed_token_attributes.token_id = Token_ID; merge_token.u.grabbed_token_attributes.grabber = m_uidGrabber; break; case TOKEN_INHIBITED: merge_token.u.inhibited_token_attributes.token_id = Token_ID; merge_token.u.inhibited_token_attributes.inhibitors = &m_InhibitorList; break; case TOKEN_GIVING: merge_token.u.giving_token_attributes.token_id = Token_ID; merge_token.u.giving_token_attributes.grabber = m_uidGrabber; merge_token.u.giving_token_attributes.recipient = m_uidRecipient; break; case TOKEN_GIVEN: merge_token.u.given_token_attributes.token_id = Token_ID; merge_token.u.given_token_attributes.recipient = m_uidRecipient; break; } merge_token_list.Append(&merge_token); /* * Send the resulting merge request to the indicated provider. */ m_pConnToTopProvider->MergeTokensRequest(&merge_token_list, &purge_token_list); } else { /* * Report that the token is not in use, but do NOT send a merge * request. */ TRACE_OUT(("Token::IssueMergeRequest: token not in use")); } } } /* * Void TokenGrabRequest () * * Public * * Functional Description: * This function is called when a user tries to grab a token. The request * will either succeed or fail depending on the current state of the token. * Either way, a confirm will be sent to the user originating the request. */ Void Token::TokenGrabRequest ( CAttachment *pOrigAtt, UserID uidInitiator, TokenID) { Result result; TokenStatus token_status; /* * Check to see if this provider is the Top Provider. If so, then process * this request here. Otherwise, forward the request upward. */ if (IsTopProvider()) { /* * Determine what state we are, which greatly affects how we process * the request. */ switch (Token_State) { case TOKEN_AVAILABLE: /* * Since the token is available, the request automatically * succeeds. Change the state to grabbed, and mark the * initiator as the grabber. */ Token_State = TOKEN_GRABBED; m_uidGrabber = uidInitiator; result = RESULT_SUCCESSFUL; token_status = TOKEN_SELF_GRABBED; break; case TOKEN_GRABBED: /* * If the token is already grabbed, then we must fail the * request. However, we need to determine if the token is * grabbed by the same user who is currently requesting it, or * another user. */ result = RESULT_TOKEN_NOT_AVAILABLE; if (uidInitiator == m_uidGrabber) token_status = TOKEN_SELF_GRABBED; else token_status = TOKEN_OTHER_GRABBED; break; case TOKEN_INHIBITED: /* * If the token is inhibited, this request can still succeed if * the only inhibitor is the user that is attempting to grab * the token. Check to see if this is the case. */ if (m_InhibitorList.Find(uidInitiator)) { if (m_InhibitorList.GetCount() == 1) { /* * The user attempting to grab the token is the only * inhibitor, so convert the state to grabbed. */ Token_State = TOKEN_GRABBED; m_uidGrabber = uidInitiator; m_InhibitorList.Clear(); result = RESULT_SUCCESSFUL; token_status = TOKEN_SELF_GRABBED; } else { /* * The token is inhibited by at least one other user, * so the grab request must fail. */ result = RESULT_TOKEN_NOT_AVAILABLE; token_status = TOKEN_SELF_INHIBITED; } } else { /* * The token is not inhibited by the requestor, so it must * be inhibited by someone else. */ result = RESULT_TOKEN_NOT_AVAILABLE; token_status = TOKEN_OTHER_INHIBITED; } break; case TOKEN_GIVING: /* * If the token is in the process of being given from one to * another, then a grab request must fail. All we need to * figure out is the proper token status to report. */ result = RESULT_TOKEN_NOT_AVAILABLE; if (uidInitiator == m_uidRecipient) token_status = TOKEN_SELF_RECIPIENT; else if (uidInitiator == m_uidGrabber) token_status = TOKEN_SELF_GIVING; else token_status = TOKEN_OTHER_GIVING; break; case TOKEN_GIVEN: /* * If the token is in the process of being given from one to * another, then a grab request must fail. All we need to * figure out is the proper token status to report. */ result = RESULT_TOKEN_NOT_AVAILABLE; if (uidInitiator == m_uidRecipient) token_status = TOKEN_SELF_RECIPIENT; else token_status = TOKEN_OTHER_GIVING; break; } /* * Issue the token grab confirm to the initiating user. */ pOrigAtt->TokenGrabConfirm(result, uidInitiator, Token_ID, token_status); } else { /* * Forward this request upward towards the Top Provider. */ TRACE_OUT(("Token::TokenGrabRequest: forwarding request to Top Provider")); m_pConnToTopProvider->TokenGrabRequest(uidInitiator, Token_ID); } } /* * Void TokenGrabConfirm () * * Public * * Functional Description: * This function is called as a part of sending a response to a user for * a previous request. It tells the user the result of the request. */ Void Token::TokenGrabConfirm ( Result result, UserID uidInitiator, TokenID, TokenStatus token_status) { /* * Make sure that the initiator ID is valid, since we must forward this * confirm in the direction of that user. If it is not valid, ignore * this confirm. */ if (ValidateUserID(uidInitiator)) { /* * Check to see if this request was successful. */ if (result == RESULT_SUCCESSFUL) { /* * Force this token to conform to the results of this confirm. */ Token_State = TOKEN_GRABBED; m_uidGrabber = uidInitiator; m_InhibitorList.Clear(); } /* * Determine what attachment leads to the initiator, and forward the * confirm in that direction. */ CAttachment *pAtt = GetUserAttachment(uidInitiator); if (pAtt) { pAtt->TokenGrabConfirm(result, uidInitiator, Token_ID, token_status); } } else { /* * The initiator is not in the sub-tree of this provider. So ignore * this confirm. */ ERROR_OUT(("Token::TokenGrabConfirm: invalid initiator ID")); } } /* * Void TokenInhibitRequest () * * Public * * Functional Description: * This function is called when a user tries to inhibit a token. The * request will either succeed or fail depending on the current state of * the token. Either way, a confirm will be sent to the user originating * the request. */ Void Token::TokenInhibitRequest ( CAttachment *pOrigAtt, UserID uidInitiator, TokenID) { Result result; TokenStatus token_status; /* * Check to see if this is the Top Provider. */ if (IsTopProvider()) { /* * Determine what state we are, which greatly affects how we process * the request. */ switch (Token_State) { case TOKEN_AVAILABLE: /* * Since the token is available, the request automatically * succeeds. Set the token state to inhibited, and add the * initiator to the list of inhibitors. */ Token_State = TOKEN_INHIBITED; m_InhibitorList.Append(uidInitiator); result = RESULT_SUCCESSFUL; token_status = TOKEN_SELF_INHIBITED; break; case TOKEN_GRABBED: /* * If the token is grabbed, this request can still succeed if * the grabber is the user that is attempting to inhibit the * token. Check to see if this is the case. */ if (uidInitiator == m_uidGrabber) { /* * The current grabber is attempting to convert the state * of the token to inhibited. This is valid, so set the * state appropriately. */ Token_State = TOKEN_INHIBITED; m_InhibitorList.Append(uidInitiator); result = RESULT_SUCCESSFUL; token_status = TOKEN_SELF_INHIBITED; } else { /* * The token is grabbed by someone else, so the inhibit * request must fail. */ result = RESULT_TOKEN_NOT_AVAILABLE; token_status = TOKEN_OTHER_GRABBED; } break; case TOKEN_INHIBITED: /* * The token is already inhibited, but this is okay. Add this * user to the list of inhibitors (if it is not already there). */ if (m_InhibitorList.Find(uidInitiator) == FALSE) m_InhibitorList.Append(uidInitiator); result = RESULT_SUCCESSFUL; token_status = TOKEN_SELF_INHIBITED; break; case TOKEN_GIVING: /* * If the token is in the process of being given from one to * another, then an inhibit request must fail. All we need to * figure out is the proper token status to report. */ result = RESULT_TOKEN_NOT_AVAILABLE; if (uidInitiator == m_uidRecipient) token_status = TOKEN_SELF_RECIPIENT; else if (uidInitiator == m_uidGrabber) token_status = TOKEN_SELF_GIVING; else token_status = TOKEN_OTHER_GIVING; break; case TOKEN_GIVEN: /* * If the token is in the process of being given from one to * another, then an inhibit request must fail. All we need to * figure out is the proper token status to report. */ result = RESULT_TOKEN_NOT_AVAILABLE; if (uidInitiator == m_uidRecipient) token_status = TOKEN_SELF_RECIPIENT; else token_status = TOKEN_OTHER_GIVING; break; } /* * If the originator is NULL, then this inhibit request is happening as * part of a merge operation, in which case we do NOT want to send a * token inhibit confirm. Otherwise we do send one. */ if (pOrigAtt != NULL) { pOrigAtt->TokenInhibitConfirm(result, uidInitiator, Token_ID, token_status); } } else { /* * Forward the request toward the top provider. */ TRACE_OUT(("Token::TokenInhibitRequest: forwarding request to Top Provider")); m_pConnToTopProvider->TokenInhibitRequest(uidInitiator, Token_ID); } } /* * Void TokenInhibitConfirm () * * Public * * Functional Description: * This function is called as a part of sending a response to a user for * a previous request. It tells the user the result of the request. */ Void Token::TokenInhibitConfirm ( Result result, UserID uidInitiator, TokenID, TokenStatus token_status) { /* * Make sure that the initiator ID is valid, since we must forward this * confirm in the direction of that user. If it is not valid, ignore * this confirm. */ if (ValidateUserID (uidInitiator) ) { /* * Check to see if this request was successful. */ if (result == RESULT_SUCCESSFUL) { /* * Force this token to conform to the results of this confirm. */ Token_State = TOKEN_INHIBITED; if (m_InhibitorList.Find(uidInitiator) == FALSE) m_InhibitorList.Append(uidInitiator); } /* * Determine what attachment leads to the initiator, and issue the * token confirm in that direction. */ CAttachment *pAtt = GetUserAttachment(uidInitiator); if (pAtt) { pAtt->TokenInhibitConfirm(result, uidInitiator, Token_ID, token_status); } } else { /* * The initiator is not in the sub-tree of this provider. So ignore * this confirm. */ ERROR_OUT(("Token::TokenInhibitConfirm: invalid initiator ID")); } } /* * Void TokenGiveRequest () * * Public * * Functional Description: * This function is called when one user asks to give a token to another * user. */ Void Token::TokenGiveRequest ( CAttachment *pOrigAtt, PTokenGiveRecord pTokenGiveRec) { Result result; TokenStatus token_status; /* * Check to see if this provider is the Top Provider. If so, then process * this request here. Otherwise, forward the request upward. */ if (m_pConnToTopProvider == NULL) { UserID uidInitiator = pTokenGiveRec->uidInitiator; UserID receiver_id = pTokenGiveRec->receiver_id; /* * Determine what state we are, which greatly affects how we process * the request. */ switch (Token_State) { case TOKEN_AVAILABLE: /* * The token is not in use, and therefore cannot be given by * anyone to anyone. So fail this request. */ result = RESULT_TOKEN_NOT_POSSESSED; token_status = TOKEN_NOT_IN_USE; break; case TOKEN_GRABBED: /* * Check to see if the requestor really is the grabber of this * token. */ if (uidInitiator == m_uidGrabber) { /* * Check to see if the intended recipient is a valid user * in the domain. */ if (ValidateUserID (receiver_id) ) { /* * Everything checks out. Set the result to success * to disable transmission of the give confirm below. * Change the state of the token to giving, and * save the ID of the intended recipient. Then issue * the give indication toward the recipient. */ result = RESULT_SUCCESSFUL; Token_State = TOKEN_GIVING; m_uidRecipient = receiver_id; CAttachment *pAtt = GetUserAttachment(receiver_id); ASSERT (Token_ID == pTokenGiveRec->token_id); if (pAtt) { pAtt->TokenGiveIndication(pTokenGiveRec); } } else { /* * The recipient does not exist in the domain, so * fail the request. */ result = RESULT_NO_SUCH_USER; token_status = TOKEN_SELF_GRABBED; } } else { /* * The requestor does not own the token, so the request * must fail. */ result = RESULT_TOKEN_NOT_POSSESSED; token_status = TOKEN_OTHER_GRABBED; } break; case TOKEN_INHIBITED: /* * Inhibited tokens cannot be given by anyone to anyone. So * fail this request with the proper status. */ result = RESULT_TOKEN_NOT_POSSESSED; if (m_InhibitorList.Find(uidInitiator) ) token_status = TOKEN_SELF_INHIBITED; else token_status = TOKEN_OTHER_INHIBITED; break; case TOKEN_GIVING: /* * This token is already in the process of being given. So * this request must fail. */ result = RESULT_TOKEN_NOT_POSSESSED; if (uidInitiator == m_uidRecipient) token_status = TOKEN_SELF_RECIPIENT; else if (uidInitiator == m_uidGrabber) token_status = TOKEN_SELF_GIVING; else token_status = TOKEN_OTHER_GIVING; break; case TOKEN_GIVEN: /* * This token is already in the process of being given. So * this request must fail. */ result = RESULT_TOKEN_NOT_POSSESSED; if (uidInitiator == m_uidRecipient) token_status = TOKEN_SELF_RECIPIENT; else token_status = TOKEN_OTHER_GIVING; break; } /* * If necessary, issue a token give confirm to the initiating user. */ if (result != RESULT_SUCCESSFUL) { pOrigAtt->TokenGiveConfirm(result, uidInitiator, Token_ID, token_status); } } else { /* * Forward this request upward towards the Top Provider. */ TRACE_OUT(("Token::TokenGiveRequest: forwarding request to Top Provider")); ASSERT (Token_ID == pTokenGiveRec->token_id); m_pConnToTopProvider->TokenGiveRequest(pTokenGiveRec); } } /* * Void TokenGiveIndication () * * Public * * Functional Description: * This function is called in order to deliver a message to a user that * another user is trying to give them a token. */ Void Token::TokenGiveIndication ( PTokenGiveRecord pTokenGiveRec) { UserID receiver_id; receiver_id = pTokenGiveRec->receiver_id; /* * Make sure that the receiver ID is valid, since we must forward this * indication in the direction of that user. If it is not valid, ignore * this indication. */ if (ValidateUserID (receiver_id) ) { /* * Force this token to conform to the state implied by this indication. */ Token_State = TOKEN_GIVING; m_uidGrabber = pTokenGiveRec->uidInitiator; m_InhibitorList.Clear(); m_uidRecipient = receiver_id; /* * Determine what attachment leads to the recipient, and forward the * indication in that direction. */ CAttachment *pAtt = GetUserAttachment(receiver_id); ASSERT (Token_ID == pTokenGiveRec->token_id); if (pAtt) { pAtt->TokenGiveIndication(pTokenGiveRec); } } else { /* * The recipient is not in the sub-tree of this provider. So ignore * this indication. */ ERROR_OUT(("Token::TokenGiveIndication: invalid receiver ID")); } } /* * Void TokenGiveResponse () * * Public * * Functional Description: * This function is called when a potential recipient decides whether or * not to accept an offered token. */ Void Token::TokenGiveResponse ( Result result, UserID receiver_id, TokenID) { UserID uidInitiator; TokenStatus token_status; /* * Process the response according to the current state of this token. */ switch (Token_State) { case TOKEN_AVAILABLE: case TOKEN_GRABBED: case TOKEN_INHIBITED: /* * The token is not in the process of being given to anyone, so * this response must be ignored. */ break; case TOKEN_GIVING: /* * The token is being given to someone. Check to see if this is * the proper recipient. If not, don't do anything. */ if (receiver_id == m_uidRecipient) { /* * Save the ID of the initiator, for use in issuing a give * confirm (if necessary). */ uidInitiator = m_uidGrabber; /* * Check to see if the token was accepted. A result of * anything but successful would indicate that it was not. */ if (result == RESULT_SUCCESSFUL) { /* * The token was accepted by the intended recipient. * Change the state of the token to being grabbed by the * receiver. */ Token_State = TOKEN_GRABBED; m_uidGrabber = receiver_id; } else { /* * The token was not accepted. It must either revert to * being grabbed by the donor, or deleted, depending on * whether or not the donor is in the sub-tree of this * provider. */ if (ValidateUserID(uidInitiator)) { /* * The donor is in the sub-tree of this provider, so * change the state of the token back to grabbed. */ Token_State = TOKEN_GRABBED; } else { /* * The donor is not in the sub-tree of this provider, * so the token will be marked as available (which * will cause it to be deleted). */ Token_State = TOKEN_AVAILABLE; } } /* * Check to see if this is the Top Provider. */ if (m_pConnToTopProvider == NULL) { /* * If the donor is still a valid user in the domain, a * token give confirm must be issued in its direction. */ if (ValidateUserID(uidInitiator)) { /* * Determine which attachment leads to the donor, and * issue the token give confirm. */ if (uidInitiator == m_uidGrabber) token_status = TOKEN_SELF_GRABBED; else token_status = TOKEN_OTHER_GRABBED; CAttachment *pAtt = GetUserAttachment(uidInitiator); if (pAtt) { pAtt->TokenGiveConfirm(result, uidInitiator, Token_ID, token_status); } } } else { /* * If this is not the Top Provider, then the valid give * response must be forwarded to the Top Provider. */ m_pConnToTopProvider->TokenGiveResponse(result, receiver_id, Token_ID); } } break; case TOKEN_GIVEN: /* * The token is being given to someone. Check to see if this is * the proper recipient. If not, don't do anything. */ if (receiver_id == m_uidRecipient) { /* * Check to see if the token was accepted. A result of * anything but successful would indicate that it was not. */ if (result == RESULT_SUCCESSFUL) { /* * The token was accepted by the intended recipient. * Change the state of the token to being grabbed by the * receiver. */ Token_State = TOKEN_GRABBED; m_uidGrabber = receiver_id; } else { /* * The token was not accepted. Since the donor has * already relinquished control of the token, the token * will marked as available (which will cause it to be * deleted). */ Token_State = TOKEN_AVAILABLE; } /* * Check to see if this is the Top Provider. */ if (m_pConnToTopProvider != NULL) { /* * If this is not the Top Provider, then the valid give * response must be forwarded to the Top Provider. */ m_pConnToTopProvider->TokenGiveResponse(result, receiver_id, Token_ID); } } break; } } /* * Void TokenGiveConfirm () * * Public * * Functional Description: * This function is called as a potential giver of a token is told whether * or not the token was successfully given to the intended recipient. */ Void Token::TokenGiveConfirm ( Result result, UserID uidInitiator, TokenID, TokenStatus token_status) { /* * Make sure that the initiator ID is valid, since we must forward this * confirm in the direction of that user. If it is not valid, ignore * this confirm. */ if (ValidateUserID(uidInitiator)) { /* * The token should be in the grabbed state, or else this confirm * was generated in error. */ if (Token_State == TOKEN_GRABBED) { /* * Check to see if this request was successful. */ if (result == RESULT_SUCCESSFUL) { /* * If this token is marked as being owned by the initiator of * the give, but the status indicates that the token is now * owned by someone else (as a result of the successful give), * then release the token.* */ if ((uidInitiator == m_uidGrabber) && (token_status == TOKEN_OTHER_GRABBED)) Token_State = TOKEN_AVAILABLE; } } else { /* * The token is in an invalid state. Report the error, but do * not change the state of the token. */ ERROR_OUT(("Token::TokenGiveConfirm: invalid token state")); } /* * Determine what attachment leads to the initiator, and forward the * confirm in that direction. */ CAttachment *pAtt = GetUserAttachment(uidInitiator); if (pAtt) { pAtt->TokenGiveConfirm(result, uidInitiator, Token_ID, token_status); } } else { /* * The initiator is not in the sub-tree of this provider. So ignore * this confirm. */ ERROR_OUT(("Token::TokenGiveConfirm: invalid initiator ID")); } } /* * Void TokenPleaseRequest () * * Public * * Functional Description: * This function is called when a user wishes to ask all current owners * of a token to relinquish their ownership. */ Void Token::TokenPleaseRequest ( UserID uidInitiator, TokenID) { CUidList please_indication_list; /* * Check to see if this is the Top Provider. */ if (IsTopProvider()) { CAttachmentList attachment_list; CAttachment *pAtt; /* * Determine the state of the token, to determine who to send the * please indication to. Each state will place the appropriate user * IDs in the please indication list. */ switch (Token_State) { case TOKEN_AVAILABLE: break; case TOKEN_GRABBED: /* * Put the grabber into the list. */ please_indication_list.Append(m_uidGrabber); break; case TOKEN_INHIBITED: { UserID uid; /* * Put all current inhibitors into the list. */ m_InhibitorList.Reset(); while (NULL != (uid = m_InhibitorList.Iterate())) { please_indication_list.Append(uid); } } break; case TOKEN_GIVING: /* * Put the grabber into the list. And if the recipient is * different from the grabber, put it in as well. Remember * that it is valid for someone to give a token to themselves. */ please_indication_list.Append(m_uidGrabber); if (m_uidGrabber != m_uidRecipient) please_indication_list.Append(m_uidRecipient); break; case TOKEN_GIVEN: /* * Put the recipient into the list. */ please_indication_list.Append(m_uidRecipient); break; } /* * Build lists of unique attachments that lead to the users in the * please indication list (built above). */ BuildAttachmentList (&please_indication_list, &attachment_list); /* * Iterate through the newly created attachment list, issuing token * please indications to all attachments contained therein. */ attachment_list.Reset(); while (NULL != (pAtt = attachment_list.Iterate())) { pAtt->TokenPleaseIndication(uidInitiator, Token_ID); } } else { /* * Forward the request toward the top provider. */ TRACE_OUT(("Token::TokenPleaseRequest: forwarding request to Top Provider")); m_pConnToTopProvider->TokenPleaseRequest(uidInitiator, Token_ID); } } /* * Void TokenPleaseIndication () * * Public * * Functional Description: * This function is called in order to deliver a message to all current * owners of a token that someone else wishes to own the token. */ Void Token::TokenPleaseIndication ( UserID uidInitiator, TokenID) { CUidList please_indication_list; CAttachmentList attachment_list; CAttachment *pAtt; /* * Determine the state of the token, to determine who to forward the * please indication to. Each state will place the appropriate user * IDs in the please indication list. */ switch (Token_State) { case TOKEN_AVAILABLE: break; case TOKEN_GRABBED: /* * Put the grabber into the list. */ please_indication_list.Append(m_uidGrabber); break; case TOKEN_INHIBITED: { UserID uid; /* * Put all current inhibitors into the list. */ m_InhibitorList.Reset(); while (NULL != (uid = m_InhibitorList.Iterate())) { please_indication_list.Append(uid); } } break; case TOKEN_GIVING: /* * Put the grabber into the list. And if the recipient is * different from the grabber, put it in as well. Remember * that it is valid for someone to give a token to themselves. */ please_indication_list.Append(m_uidGrabber); if (m_uidGrabber != m_uidRecipient) please_indication_list.Append(m_uidRecipient); break; case TOKEN_GIVEN: /* * Put the recipient into the list. */ please_indication_list.Append(m_uidRecipient); break; } /* * Build lists of unique attachments that lead to the users in the * please indication list (built above). */ BuildAttachmentList (&please_indication_list, &attachment_list); /* * Iterate through the newly created attachment list, issuing token * please indications to all attachments contained therein. */ attachment_list.Reset(); while (NULL != (pAtt = attachment_list.Iterate())) { pAtt->TokenPleaseIndication(uidInitiator, Token_ID); } } /* * Void TokenReleaseRequest () * * Public * * Functional Description: * This function is called when a user wished to release a token. If the * requesting user really is an owner of the token, the request will * succeed. Otherwise it will fail. Either way, an appropriate token * release confirm will be issued. */ Void Token::TokenReleaseRequest ( CAttachment *pAtt, UserID uidInitiator, TokenID) { Result result; TokenStatus token_status; /* * Check to see if this is the Top Provider. */ if (IsTopProvider()) { /* * Determine the current state of the token before proceeding. */ switch (Token_State) { case TOKEN_AVAILABLE: /* * If the token is available, then the requestor cannot be an * owner. This means that the request must fail. */ result = RESULT_TOKEN_NOT_POSSESSED; token_status = TOKEN_NOT_IN_USE; break; case TOKEN_GRABBED: /* * The token is in the grabbed state. See if the requesting * user is the one who has it grabbed. */ if (uidInitiator == m_uidGrabber) { /* * The current grabber of the token wishes to release it. * Set the state back to available, and send the * appropriate token release confirm. */ Token_State = TOKEN_AVAILABLE; result = RESULT_SUCCESSFUL; token_status = TOKEN_NOT_IN_USE; } else { /* * Someone is trying to release someone elses token. This * request must fail. Send the appropriate token release * confirm. */ result = RESULT_TOKEN_NOT_POSSESSED; token_status = TOKEN_OTHER_GRABBED; } break; case TOKEN_INHIBITED: /* * The token is in the inhibited state. See if the requesting * user is one of the inhibitors. */ if (m_InhibitorList.Remove(uidInitiator)) { /* * The user is an inhibitor. Remove the user from the * list. Then check to see if this has resulted in an * "ownerless" token. */ if (m_InhibitorList.IsEmpty()) { /* * The token has no other inhibitors. Return the token * to the available state, and issue the appropriate * token release confirm. */ Token_State = TOKEN_AVAILABLE; result = RESULT_SUCCESSFUL; token_status = TOKEN_NOT_IN_USE; } else { /* * There are still other inhibitors of the token. * Simply issue the appropriate token release confirm. */ result = RESULT_SUCCESSFUL; token_status = TOKEN_OTHER_INHIBITED; } } else { /* * The user attempting to release the token is not one of * the inhibitors. Therefore the request must fail. Issue * the appropriate token release indication. */ result = RESULT_TOKEN_NOT_POSSESSED; token_status = TOKEN_OTHER_INHIBITED; } break; case TOKEN_GIVING: /* * See if the requestor is the current owner of the token. */ if (uidInitiator == m_uidGrabber) { /* * The token must transition to the given state. This * state indicates that if the recipient rejects the offer * or detaches, the token will be freed instead of * returning to the grabbed state. Issue the appropriate * release confirm. */ Token_State = TOKEN_GIVEN; result = RESULT_SUCCESSFUL; token_status = TOKEN_OTHER_GIVING; } else { /* * If the requestor is not the current owner, then this * request must fail. We first need to determine the * proper token status, and then issue the confirm. */ result = RESULT_TOKEN_NOT_POSSESSED; if (uidInitiator == m_uidRecipient) token_status = TOKEN_SELF_RECIPIENT; else token_status = TOKEN_OTHER_GIVING; } break; case TOKEN_GIVEN: /* * When the token is in the given state, there is no true * owner (only a pending owner). This request must therefore * fail. We first need to determine the proper token status, * and then issue the confirm. */ result = RESULT_TOKEN_NOT_POSSESSED; if (uidInitiator == m_uidRecipient) token_status = TOKEN_SELF_RECIPIENT; else token_status = TOKEN_OTHER_GIVING; break; } /* * Issue the token release confirm to the initiator. */ pAtt->TokenReleaseConfirm(result, uidInitiator, Token_ID, token_status); } else { /* * Forward the request toward the top provider. */ TRACE_OUT(("Token::TokenReleaseRequest: forwarding request to Top Provider")); m_pConnToTopProvider->TokenReleaseRequest(uidInitiator, Token_ID); } } /* * Void TokenReleaseConfirm () * * Public * * Functional Description: * This function is called as a part of sending a response to a user for * a previous request. It tells the user the result of the request. */ Void Token::TokenReleaseConfirm ( Result result, UserID uidInitiator, TokenID, TokenStatus token_status) { /* * Make sure that the initiator ID is valid, since we must forward this * confirm in the direction of that user. If it is not valid, ignore * this confirm. */ if (ValidateUserID (uidInitiator) ) { /* * Check to see if this request was successful. */ if (result == RESULT_SUCCESSFUL) { /* * Process the confirm according to current state. */ switch (Token_State) { case TOKEN_AVAILABLE: break; case TOKEN_GRABBED: /* * If the grabber has released the token, then is becomes * available. */ if (uidInitiator == m_uidGrabber) Token_State = TOKEN_AVAILABLE; break; case TOKEN_INHIBITED: /* * If an inhibitor releases the token, then remove it from * the list. If there are no more entries in the list, * then the token becomes available. */ if (m_InhibitorList.Remove(uidInitiator)) { if (m_InhibitorList.IsEmpty()) Token_State = TOKEN_AVAILABLE; } break; case TOKEN_GIVING: /* * If the grabber releases the token, then it transitions * to an intermediate state. This state indicates that * if the recipient rejects the token, it will be freed * instead of returning to the grabbed state. */ if (uidInitiator == m_uidGrabber) Token_State = TOKEN_GIVEN; break; case TOKEN_GIVEN: break; } } /* * Determine what attachment leads to the initiator, and forward the * confirm in that direction. */ CAttachment *pAtt = GetUserAttachment(uidInitiator); if (pAtt) { pAtt->TokenReleaseConfirm(result, uidInitiator, Token_ID, token_status); } } else { /* * The initiator is not in the sub-tree of this provider. So ignore * this confirm. */ ERROR_OUT(("Token::TokenReleaseConfirm: invalid initiator ID")); } } /* * Void TokenTestRequest () * * Public * * Functional Description: * This function is called when a user wishes to test the current state * of a token. The only action is to issue a token test confirm containing * the state information. */ Void Token::TokenTestRequest ( CAttachment *pAtt, UserID uidInitiator, TokenID) { TokenStatus token_status; /* * Check to see if this is the Top Provider. */ if (m_pConnToTopProvider == NULL) { /* * Determine the state of the token before proceeding. */ switch (Token_State) { case TOKEN_AVAILABLE: /* * The token is not in use. */ token_status = TOKEN_NOT_IN_USE; break; case TOKEN_GRABBED: /* * The token is grabbed. See if the originating user is the * grabber. If so, return the state as self grabbed. If not, * return the state as other grabbed. */ if (uidInitiator == m_uidGrabber) token_status = TOKEN_SELF_GRABBED; else token_status = TOKEN_OTHER_GRABBED; break; case TOKEN_INHIBITED: /* * The token is inhibited. See if the originating user is one * of the inhibitors. If so, return the state as self * inhibited. If not, return the state as other inhibited. */ if (m_InhibitorList.Find(uidInitiator)) token_status = TOKEN_SELF_INHIBITED; else token_status = TOKEN_OTHER_INHIBITED; break; case TOKEN_GIVING: /* * The token is being given from one user to another. See if * the requestor is one of the users involved. */ if (uidInitiator == m_uidRecipient) token_status = TOKEN_SELF_RECIPIENT; else if (uidInitiator == m_uidGrabber) token_status = TOKEN_SELF_GIVING; else token_status = TOKEN_OTHER_GIVING; break; case TOKEN_GIVEN: /* * The token has been given from one user to another. See if * the requestor is the receiver. */ if (uidInitiator == m_uidRecipient) token_status = TOKEN_SELF_RECIPIENT; else token_status = TOKEN_OTHER_GIVING; break; } /* * Issue the test confirm with the appropriate status information. */ pAtt->TokenTestConfirm(uidInitiator, Token_ID, token_status); } else { /* * Forward the request toward the top provider. */ TRACE_OUT(("Token::TokenTestRequest: forwarding request to Top Provider")); m_pConnToTopProvider->TokenTestRequest(uidInitiator, Token_ID); } } /* * Void TokenTestConfirm () * * Public * * Functional Description: * This function is called as a part of sending a response to a user for * a previous request. It tells the user the result of the request. */ Void Token::TokenTestConfirm ( UserID uidInitiator, TokenID, TokenStatus token_status) { /* * Make sure that the initiator ID is valid, since we must forward this * confirm in the direction of that user. If it is not valid, ignore * this confirm. */ if (ValidateUserID(uidInitiator)) { /* * Determine what attachment leads to the initiator, and forward the * confirm in that direction. */ CAttachment *pAtt = GetUserAttachment(uidInitiator); if (pAtt) { pAtt->TokenTestConfirm(uidInitiator, Token_ID, token_status); } } else { /* * The initiator is not in the sub-tree of this provider. So ignore * this confirm. */ ERROR_OUT(("Token::TokenReleaseConfirm: invalid initiator ID")); } } /* * BOOL ValidateUserID () * * Private * * Functional Description: * This function is used to verify the existence of the specified user * in the sub-tree of this provider. * * Formal Parameters: * user_id (i) * This is the ID of the user the caller wishes to validate. * * Return Value: * TRUE if the user is valid. FALSE otherwise. * * Side Effects: * None. */ BOOL Token::ValidateUserID ( UserID user_id) { /* * Initialize the return value to FALSE, indicating that if any of the * following checks fail, the ID does NOT refer to a valid user ID. */ BOOL valid=FALSE; PChannel channel; /* * First check to see if the user ID is in the channel list at all. This * prevents an attempt to read an invalid entry from the dictionary. */ if (NULL != (channel = m_pChannelList2->Find(user_id))) { /* * We know that the ID is in the dictionary, but we don't know for sure * whether or not it is a user ID channel. So check this. If it is a * user channel, then set the valid flag to TRUE. */ if (channel->GetChannelType () == USER_CHANNEL) valid = TRUE; } return (valid); } /* * PCommandTarget GetUserAttachment () * * Private * * Functional Description: * This function returns the attachment which leads to the specified * user. * * Formal Parameters: * user_id (i) * This is the ID of the user the caller wishes to find the attachment * for. * * Return Value: * A pointer to the attachment that leads to the user. * * Side Effects: * None. */ CAttachment *Token::GetUserAttachment ( UserID user_id) { PChannel lpChannel; /* * Read and return a pointer to the attachment that leads to the * specified user. Note that this routine does NOT check to see if the * user is in the channel list. It assumes that the user is known to * be valid BEFORE this routine is called. */ return ((NULL != (lpChannel = m_pChannelList2->Find(user_id))) ? lpChannel->GetAttachment() : NULL); } /* * Void IssueTokenReleaseIndication () * * Private * * Functional Description: * This function is used to issue a token release indication to a * particular user. It first check to make sure that the user id valid, * and that it is a local user. * * Formal Parameters: * user_id (i) * This is the ID of the user the caller wishes to send a token * release indication to. * * Return Value: * None. * * Side Effects: * None. */ Void Token::IssueTokenReleaseIndication ( UserID user_id) { /* * Make sure that the specified user exists in the sub-tree of this * provider. */ if (ValidateUserID (user_id) ) { /* * Determine which attachment leads to the grabber. */ CAttachment *pAtt = GetUserAttachment(user_id); /* * Is this attachment a local one? If so, then issue a token * release indication to let the user know that the token has * been taken away. */ if ( ( pAtt != NULL ) && m_pAttachmentList->Find(pAtt) && pAtt->IsUserAttachment()) { PUser pUser = (PUser) pAtt; pUser->TokenReleaseIndication(REASON_TOKEN_PURGED, Token_ID); } } } /* * Void BuildAttachmentList () * * Private * * Functional Description: * This function builds a list of unique attachments out of the list of * user IDs that is poassed in. This is done to insure that no given * attachment receives more than one indication, even when there are more * than one user in the same direction. * * Formal Parameters: * user_id_list (i) * This is a list of user IDs that the caller wishes to send a token * please indication to. * attachment_list (i) * This is the list that all unique attachments will be added to. * * Return Value: * None. * * Side Effects: * None. */ Void Token::BuildAttachmentList ( CUidList *user_id_list, CAttachmentList *attachment_list) { UserID uid; /* * Loop through the passed in user ID list building a list of unique * attachments. This will be used to send indications downward without * sending one twice over the same attachment. */ user_id_list->Reset(); while (NULL != (uid = user_id_list->Iterate())) { /* * Check to see if the user ID refers to a valid user in the sub-tree * of this provider. */ if (ValidateUserID(uid)) { /* * Determine which attachment leads to the user in question. Then * check to see if it is already in the attachment list. If not, * then put it there. */ CAttachment *pAtt = GetUserAttachment(uid); if (attachment_list->Find(pAtt) == FALSE) attachment_list->Append(pAtt); } else { /* * This user ID does not correspond to a valid user in the sub-tree * of this provider. Therefore, discard the ID. */ ERROR_OUT(("Token::BuildAttachmentList: user ID not valid")); } } }