/* Sends a basic Login Request on the given streamID, using the RDM package. */
RsslRet sendLoginRequest(ChannelHandler *pChannelHandler, ChannelInfo *pChannelInfo, RsslInt32 streamId, RsslError *error)
{
	RsslRDMLoginRequest loginRequest;
	RsslRet ret;
	RsslBuffer *msgBuf;
	RsslErrorInfo errorInfo;
	RsslEncodeIterator eIter;
	RsslChannelInfo chanInfo;
	RsslChannel *pChannel = pChannelInfo->pChannel;

	if ((ret = rsslGetChannelInfo(pChannel, &chanInfo, error)) != RSSL_RET_SUCCESS)
	{
		printf("rsslGetChannelInfo() failed: %d(%s)\n", ret, error->text);
		return RSSL_RET_FAILURE;
	}

	/* Send Login Request */
	if ((ret = rsslInitDefaultRDMLoginRequest(&loginRequest, streamId)) != RSSL_RET_SUCCESS)
	{
		printf("rsslInitDefaultRDMLoginRequest() failed: %d\n", ret);
		return RSSL_RET_FAILURE;
	}

	if (strlen(niProvPerfConfig.username))
	{
		loginRequest.userName.data = niProvPerfConfig.username;
		loginRequest.userName.length = (RsslUInt32)strlen(niProvPerfConfig.username);
	}

	loginRequest.flags |= RDM_LG_RQF_HAS_ROLE | RDM_LG_RQF_HAS_APPLICATION_NAME;
	loginRequest.role = RDM_LOGIN_ROLE_PROV;
	loginRequest.applicationName = applicationName;

	if (!(msgBuf = rsslGetBuffer(pChannel, chanInfo.maxFragmentSize, RSSL_FALSE, error)))
	{
		printf("rsslGetBuffer() failed: (%d) %s\n", error->rsslErrorId, error->text);
		 return ret;
	}

	rsslClearEncodeIterator(&eIter);
	rsslSetEncodeIteratorRWFVersion(&eIter, pChannel->majorVersion, pChannel->minorVersion);
	if ( (ret = rsslSetEncodeIteratorBuffer(&eIter, msgBuf)) != RSSL_RET_SUCCESS)
	{
		printf("rsslSetEncodeIteratorBuffer() failed: %d(%s)\n", ret, errorInfo.rsslError.text);
		 return ret;
	}

	if ((ret = rsslEncodeRDMLoginMsg(&eIter, (RsslRDMLoginMsg*)&loginRequest, &msgBuf->length, &errorInfo)) != RSSL_RET_SUCCESS)
	{
		printf("rsslEncodeRDMLoginMsg() failed: %d(%s)\n", ret, errorInfo.rsslError.text);
		 return ret;
	}

	return channelHandlerWriteChannel(pChannelHandler, pChannelInfo, msgBuf, 0);

}
RsslRet snapshotSessionProcessChannelActive(SnapshotSession *pSession)
{
	RsslEncodeIterator encodeIter;
	RsslRDMLoginRequest loginRequest;
	RsslBuffer *pBuffer;
	RsslErrorInfo rsslErrorInfo;
	RsslError rsslError;
	RsslRet ret = RSSL_RET_FAILURE;


	/* Get a buffer from the channel for writing. */
	if (!(pBuffer = rsslGetBuffer(pSession->pRsslChannel, 1024, RSSL_FALSE, &rsslError)))
	{
		printf("<%s> rsslGetBuffer() failed while sending login request: %d (%s -- %s).\n\n",
				pSession->name,
				rsslError.rsslErrorId, rsslRetCodeToString(rsslError.rsslErrorId), rsslError.text);
		return rsslError.rsslErrorId;
	}

	/* Populate the login request with some default information. */
	if ((ret = rsslInitDefaultRDMLoginRequest(&loginRequest, LOGIN_STREAM_ID)) != RSSL_RET_SUCCESS)
	{
		printf("<%s> rsslInitDefaultRDMLoginRequest() failed: %d(%s).\n\n",
				pSession->name, ret, rsslRetCodeToString(ret));
		rsslReleaseBuffer(pBuffer, &rsslError);
		return ret;
	}

	/* Encode the login request using the RDM package encoder utility. This will
	 * translate the login request structure to an encoded message and set the proper length
	 * on the buffer. */
	rsslClearEncodeIterator(&encodeIter);
	rsslSetEncodeIteratorRWFVersion(&encodeIter, pSession->pRsslChannel->majorVersion,
			pSession->pRsslChannel->minorVersion);
	rsslSetEncodeIteratorBuffer(&encodeIter, pBuffer);
	if ((ret = rsslEncodeRDMLoginMsg(&encodeIter, (RsslRDMLoginMsg*)&loginRequest, &pBuffer->length,
					&rsslErrorInfo)) != RSSL_RET_SUCCESS)
	{
		printf("<%s> rsslEncodeRDMLoginMsg() failed: %d (%s -- %s).\n\n",
				pSession->name,
				ret, rsslRetCodeToString(ret), rsslErrorInfo.rsslError.text);
		rsslReleaseBuffer(pBuffer, &rsslError);
		return ret;
	}

	/* Write the message. */
	if ((ret = snapshotSessionWrite(pSession, pBuffer)) != RSSL_RET_SUCCESS)
		return ret;

	pSession->state = SNAPSHOT_STATE_LOGIN_REQUESTED;

	return RSSL_RET_SUCCESS;

}
/*
 * Sends the login request reject status message for a channel.
 * pReactorChannel - The channel to send request reject status message to
 * streamId - The stream id of the request
 * reason - The reason for the reject
 */
static RsslRet sendLoginRequestReject(RsslReactor *pReactor, RsslReactorChannel* pReactorChannel, RsslInt32 streamId, RsslLoginRejectReason reason, RsslErrorInfo *pError)
{
	RsslErrorInfo rsslErrorInfo;
	RsslBuffer* msgBuf = 0;

	/* get a buffer for the login request reject status */
	msgBuf = rsslReactorGetBuffer(pReactorChannel, MAX_MSG_SIZE, RSSL_FALSE, &rsslErrorInfo);

	if (msgBuf != NULL)
	{
		RsslRet ret = 0;
		char stateText[MAX_LOGIN_INFO_STRLEN];
		RsslEncodeIterator encodeIter;
		RsslRDMLoginStatus loginStatus;
		RsslErrorInfo rsslErrorInfo;

		rsslClearRDMLoginStatus(&loginStatus);
		loginStatus.flags |= RDM_LG_STF_HAS_STATE;
		loginStatus.rdmMsgBase.streamId = streamId;
		loginStatus.state.streamState = RSSL_STREAM_CLOSED_RECOVER;
		loginStatus.state.dataState = RSSL_DATA_SUSPECT;

		/* set-up message */
		switch(reason)
		{
			case MAX_LOGIN_REQUESTS_REACHED:
				loginStatus.state.code = RSSL_SC_TOO_MANY_ITEMS;
				snprintf(stateText, sizeof(stateText), "Login request rejected for stream id %d - max request count reached", streamId);
				loginStatus.state.text.data = stateText;
				loginStatus.state.text.length = (RsslUInt32)strlen(stateText) + 1;
				break;
			case LOGIN_RDM_DECODER_FAILED:
				/* The typed message decoder failed. Pass along the error text. */
				loginStatus.state.code = RSSL_SC_USAGE_ERROR;
				snprintf(stateText, sizeof(stateText), "Login request rejected for stream id %d - decoding failure: %s", streamId, pError->rsslError.text);
				loginStatus.state.text.data = stateText;
				loginStatus.state.text.length = (RsslUInt32)strlen(stateText) + 1;
				break;
			default:
				break;
		}

		/* encode message */
		rsslClearEncodeIterator(&encodeIter);
		if((ret = rsslSetEncodeIteratorBuffer(&encodeIter, msgBuf)) < RSSL_RET_SUCCESS)
		{
			rsslReactorReleaseBuffer(pReactorChannel, msgBuf, &rsslErrorInfo);
			printf("\nrsslSetEncodeIteratorBuffer() failed with return code: %d\n", ret);
			return RSSL_RET_FAILURE;
		}
		rsslSetEncodeIteratorRWFVersion(&encodeIter, pReactorChannel->majorVersion, pReactorChannel->minorVersion);
		if (ret = rsslEncodeRDMLoginMsg(&encodeIter, (RsslRDMLoginMsg*)&loginStatus, &msgBuf->length, &rsslErrorInfo) != RSSL_RET_SUCCESS)
		{
			rsslReactorReleaseBuffer(pReactorChannel, msgBuf, &rsslErrorInfo);
			printf("\nrsslEncodeRDMLoginMsg() failed\n");
			return RSSL_RET_FAILURE;
		}

		msgBuf->length = rsslGetEncodedBufferLength(&encodeIter);

		/* send request reject status */
		if (sendMessage(pReactor, pReactorChannel, msgBuf) != RSSL_RET_SUCCESS)
			return RSSL_RET_FAILURE;
	}
	else
	{
		printf("rsslReactorGetBuffer(): Failed <%s>\n", rsslErrorInfo.rsslError.text);
		return RSSL_RET_FAILURE;
	}

	return RSSL_RET_SUCCESS;
}
/*
 * Sends a login refresh to a channel.  This consists of getting
 * a message buffer, initializing the RsslRDMLoginRefresh structure, 
 * encoding it, and sending the encoded message.
 * pReactorChannel - The channel to send a login response to
 * pLoginRequest - The login request that solicited this refresh
 */
static RsslRet sendLoginRefresh(RsslReactor *pReactor, RsslReactorChannel* pReactorChannel, RsslRDMLoginRequest* pLoginRequest)
{
	RsslErrorInfo rsslErrorInfo;
	RsslBuffer* msgBuf = 0;
	RsslUInt32 ipAddress = 0;
	RsslRet ret;

	/* get a buffer for the login response */
	msgBuf = rsslReactorGetBuffer(pReactorChannel, MAX_MSG_SIZE, RSSL_FALSE, &rsslErrorInfo);

	if (msgBuf != NULL)
	{
		RsslRDMLoginRefresh loginRefresh;
		RsslEncodeIterator eIter;

		rsslClearRDMLoginRefresh(&loginRefresh);

		/* Set state information */
		loginRefresh.state.streamState = RSSL_STREAM_OPEN;
		loginRefresh.state.dataState = RSSL_DATA_OK;
		loginRefresh.state.code = RSSL_SC_NONE;

		/* Set stream ID */
		loginRefresh.rdmMsgBase.streamId = pLoginRequest->rdmMsgBase.streamId;

		/* Mark refresh as solicited since it is a response to a request. */
		loginRefresh.flags = RDM_LG_RFF_SOLICITED;

		/* Echo the userName, applicationId, applicationName, and position */
		loginRefresh.flags |= RDM_LG_RFF_HAS_USERNAME;
		loginRefresh.userName = pLoginRequest->userName;
		if (pLoginRequest->flags & RDM_LG_RQF_HAS_USERNAME_TYPE)
		{
			loginRefresh.flags |= RDM_LG_RFF_HAS_USERNAME_TYPE;
			loginRefresh.userNameType = pLoginRequest->userNameType;
		}

		loginRefresh.flags |= RDM_LG_RFF_HAS_APPLICATION_ID;
		loginRefresh.applicationId = pLoginRequest->applicationId;

		loginRefresh.flags |= RDM_LG_RFF_HAS_APPLICATION_NAME;
		loginRefresh.applicationName = pLoginRequest->applicationName;

		loginRefresh.flags |= RDM_LG_RFF_HAS_POSITION;
		loginRefresh.position = pLoginRequest->position;

		/* This provider does not support Single-Open behavior. */
		loginRefresh.flags |= RDM_LG_RFF_HAS_SINGLE_OPEN;
		loginRefresh.singleOpen = 0; 

		/* This provider supports posting. */
		loginRefresh.flags |= RDM_LG_RFF_HAS_SUPPORT_POST;
		loginRefresh.supportOMMPost = 1; 

		/* This provider supports batch requests*/
		loginRefresh.flags |= RDM_LG_RFF_HAS_SUPPORT_BATCH;
		loginRefresh.supportBatchRequests = RDM_LOGIN_BATCH_SUPPORT_REQUESTS | RDM_LOGIN_BATCH_SUPPORT_CLOSES;     /* this provider supports batch requests and batch close */

		/* set the clear cache flag */
		loginRefresh.flags |= RDM_LG_RFF_CLEAR_CACHE;

		/* Leave all other parameters as default values. */

		/* Encode the refresh. */
		rsslClearEncodeIterator(&eIter);
		rsslSetEncodeIteratorRWFVersion(&eIter, pReactorChannel->majorVersion, pReactorChannel->minorVersion);
		if((ret = rsslSetEncodeIteratorBuffer(&eIter, msgBuf)) < RSSL_RET_SUCCESS)
		{
			rsslReactorReleaseBuffer(pReactorChannel, msgBuf, &rsslErrorInfo);
			printf("rsslSetEncodeIteratorBuffer() failed with return code: %d\n", ret);
			return RSSL_RET_FAILURE;
		}
		if (rsslEncodeRDMLoginMsg(&eIter, (RsslRDMLoginMsg*)&loginRefresh, &msgBuf->length, &rsslErrorInfo) != RSSL_RET_SUCCESS)
		{
			rsslReactorReleaseBuffer(pReactorChannel, msgBuf, &rsslErrorInfo);
			printf("\nrsslEncodeRDMLoginRefresh() failed:%s(%s)\n", rsslErrorInfo.rsslError.text, rsslErrorInfo.errorLocation);
			return RSSL_RET_FAILURE;
		}

		/* Send the refresh. */
		if (sendMessage(pReactor, pReactorChannel, msgBuf) != RSSL_RET_SUCCESS)
			return RSSL_RET_FAILURE;
	}
	else
	{
		printf("rsslReactorGetBuffer(): Failed <%s>\n", rsslErrorInfo.rsslError.text);
		return RSSL_RET_FAILURE;
	}

	return RSSL_RET_SUCCESS;
}
RsslReactorCallbackRet simpleTunnelMsgHandlerProviderMsgCallback(RsslTunnelStream *pTunnelStream, RsslTunnelStreamMsgEvent *pEvent)
{
	RsslMsg *pRsslMsg = pEvent->pRsslMsg;
	RsslReactorChannel *pReactorChannel = pTunnelStream->pReactorChannel;
	SimpleTunnelMsgHandler *pSimpleTunnelMsgHandler = (SimpleTunnelMsgHandler*)pTunnelStream->userSpecPtr;

	/* Inspect the message and handle it accordingly. This is basically
	 * the same as the consumer's message callback but will respond to the
	 * client's authentication login message if one is received. */

	switch(pEvent->containerType)
	{
		case RSSL_DT_OPAQUE:
		{
			/* Read the text contained. */
			printf("Tunnel Stream %d received OPAQUE data: %.*s\n\n", 
					pTunnelStream->streamId, pEvent->pRsslBuffer->length, pEvent->pRsslBuffer->data);
			break;

		}

		case RSSL_DT_MSG:
		{

			switch(pRsslMsg->msgBase.domainType)
			{
				case RSSL_DMT_LOGIN:
				{
					RsslDecodeIterator dIter;
					RsslRDMLoginMsg loginMsg;
					char tmpMemory[1024];
					RsslBuffer memoryBuffer;
					RsslRet ret;
					RsslErrorInfo errorInfo;

					/* Use the ValueAdd RDM Decoder to decode the login message. */
					rsslClearDecodeIterator(&dIter);
					rsslSetDecodeIteratorRWFVersion(&dIter, pTunnelStream->classOfService.common.protocolMajorVersion,
						pTunnelStream->classOfService.common.protocolMinorVersion);
					rsslSetDecodeIteratorBuffer(&dIter, &pRsslMsg->msgBase.encDataBody);

					rsslClearBuffer(&memoryBuffer);
					memoryBuffer.length = sizeof(tmpMemory);
					memoryBuffer.data = tmpMemory;

					if ((ret = rsslDecodeRDMLoginMsg(&dIter, pRsslMsg, &loginMsg, &memoryBuffer, &errorInfo))
						!= RSSL_RET_SUCCESS)
					{
						printf("rsslDecodeRDMLoginMsg() failed: %s(%s)\n", rsslRetCodeToString(ret), errorInfo.rsslError.text);
						break;
					}

					switch(loginMsg.rdmMsgBase.rdmMsgType)
					{
					case RDM_LG_MT_REQUEST:
						{
							/* This is a login request, likely the client's authentication
							* request. Send a response to establish the tunnel stream. */

							RsslRDMLoginRefresh loginRefresh;
							RsslTunnelStreamSubmitOptions submitOpts;
							RsslTunnelStreamGetBufferOptions bufferOpts;
							RsslBuffer *pBuffer;
							RsslEncodeIterator eIter;
							RsslRet ret, ret2;
							RsslRDMLoginRequest *pLoginRequest = &loginMsg.request;

							printf("Received login request on tunnel stream(ID %d) with stream ID %d.\n", 
								pTunnelStream->streamId, pLoginRequest->rdmMsgBase.streamId);

							if (pLoginRequest->flags & RDM_LG_RQF_NO_REFRESH)
								break;

							rsslClearTunnelStreamGetBufferOptions(&bufferOpts);
							bufferOpts.size = 1024;
							if ((pBuffer = rsslTunnelStreamGetBuffer(pTunnelStream, &bufferOpts, &errorInfo))
								== NULL)
							{
								printf("rsslTunnelStreamGetBuffer failed: %s(%s)\n", rsslRetCodeToString(errorInfo.rsslError.rsslErrorId), &errorInfo.rsslError.text);
								break;
							}

							rsslClearRDMLoginRefresh(&loginRefresh);

							/* Set state information */
							loginRefresh.state.streamState = RSSL_STREAM_OPEN;
							loginRefresh.state.dataState = RSSL_DATA_OK;
							loginRefresh.state.code = RSSL_SC_NONE;
							loginRefresh.state.text.data = (char*)"Tunnel login accepted.";
							loginRefresh.state.text.length = (RsslUInt32)strlen(loginRefresh.state.text.data);

							/* Set stream ID */
							loginRefresh.rdmMsgBase.streamId = pLoginRequest->rdmMsgBase.streamId;

							/* Mark refresh as solicited since it is a response to a request. */
							loginRefresh.flags = RDM_LG_RFF_SOLICITED;

							/* Echo the userName, applicationId, applicationName, and position */
							loginRefresh.flags |= RDM_LG_RFF_HAS_USERNAME;
							loginRefresh.userName = pLoginRequest->userName;
							if (pLoginRequest->flags & RDM_LG_RQF_HAS_USERNAME_TYPE)
							{
								loginRefresh.flags |= RDM_LG_RFF_HAS_USERNAME_TYPE;
								loginRefresh.userNameType = pLoginRequest->userNameType;
							}

							loginRefresh.flags |= RDM_LG_RFF_HAS_APPLICATION_ID;
							loginRefresh.applicationId = pLoginRequest->applicationId;

							loginRefresh.flags |= RDM_LG_RFF_HAS_APPLICATION_NAME;
							loginRefresh.applicationName = pLoginRequest->applicationName;

							loginRefresh.flags |= RDM_LG_RFF_HAS_POSITION;
							loginRefresh.position = pLoginRequest->position;

							/* This provider does not support Single-Open behavior. */
							loginRefresh.flags |= RDM_LG_RFF_HAS_SINGLE_OPEN;
							loginRefresh.singleOpen = 0; 

							/* set the clear cache flag */
							loginRefresh.flags |= RDM_LG_RFF_CLEAR_CACHE;

							/* Leave all other parameters as default values. */

							/* Encode the refresh. */
							rsslClearEncodeIterator(&eIter);
							rsslSetEncodeIteratorRWFVersion(&eIter, pTunnelStream->classOfService.common.protocolMajorVersion,
								pTunnelStream->classOfService.common.protocolMinorVersion);
							if((ret = rsslSetEncodeIteratorBuffer(&eIter, pBuffer)) < RSSL_RET_SUCCESS)
							{
								printf("rsslSetEncodeIteratorBuffer(): Failed <%s>\n", errorInfo.rsslError.text);
								if ((ret2 = rsslTunnelStreamReleaseBuffer(pBuffer, &errorInfo)) != RSSL_RET_SUCCESS)
									printf("rsslTunnelStreamReleaseBuffer(): Failed <%d:%s>\n", ret2, errorInfo.rsslError.text);
								break;
							}

							if (rsslEncodeRDMLoginMsg(&eIter, (RsslRDMLoginMsg*)&loginRefresh, &pBuffer->length, &errorInfo) != RSSL_RET_SUCCESS)
							{
								printf("rsslEncodeRDMLoginMsg(): Failed <%s>\n", errorInfo.rsslError.text);
								if ((ret2 = rsslTunnelStreamReleaseBuffer(pBuffer, &errorInfo)) != RSSL_RET_SUCCESS)
									printf("rsslTunnelStreamReleaseBuffer(): Failed <%d:%s>\n", ret2, errorInfo.rsslError.text);
								break;
							}

							/* Message encoding complete; submit it. */
							rsslClearTunnelStreamSubmitOptions(&submitOpts);
							submitOpts.containerType = RSSL_DT_MSG;
							if ((ret = rsslTunnelStreamSubmit(pTunnelStream, pBuffer, &submitOpts, &errorInfo)) != RSSL_RET_SUCCESS)
							{
								printf("rsslTunnelStreamSubmit(): Failed <%s>\n", errorInfo.rsslError.text);
								if ((ret2 = rsslTunnelStreamReleaseBuffer(pBuffer, &errorInfo)) != RSSL_RET_SUCCESS)
									printf("rsslTunnelStreamReleaseBuffer(): Failed <%d:%s>\n", ret2, errorInfo.rsslError.text);
								break;
							}

							printf("Sent response to tunnel login request.\n\n");

							pSimpleTunnelMsgHandler->waitingForAuthenticationRequest = RSSL_FALSE;

							break;
						}

					case RDM_LG_MT_CLOSE:
						{
							/* Login close message. */
							RsslRDMLoginClose *pLoginClose = &loginMsg.close;

							printf("Received login close on tunnel stream(ID %d) with stream ID %d.\n", 
								pTunnelStream->streamId, pLoginClose->rdmMsgBase.streamId);
							break;
						}
					}


					break;
				}

				default:
				{
					/* Don't recognize this message. */
					printf("Received unhandled message in TunnelStream with stream ID %d, class %u(%s) and domainType %u(%s)\n\n",
						pRsslMsg->msgBase.streamId, 
						pRsslMsg->msgBase.msgClass, rsslMsgClassToString(pRsslMsg->msgBase.msgClass),
						pRsslMsg->msgBase.domainType, rsslDomainTypeToString(pRsslMsg->msgBase.domainType));
					break;
				}
			}
			break;
		}

		default:
		{
			printf("Received unhandled buffer containerType %d(%s) in tunnel stream %d\n\n", 
				pEvent->containerType, rsslDataTypeToString(pEvent->containerType), pTunnelStream->streamId);
			break;
		}
	}


	return RSSL_RC_CRET_SUCCESS; 
}