/********************************************************************/
/*                                                                  */
/*   qsubs.c - Common MQSeries subroutines                          */
/*                                                                  */
/********************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "int64defs.h"
#include "comsubs.h"

/* includes for MQI */
#include <cmqc.h>
#include <cmqxc.h>

/********************************************************************/
/*                                                                  */
/*  Subroutine to check for errors in MQSeries calls.               */
/*                                                                  */
/********************************************************************/

void checkerror(const char *mqcalltype, MQLONG compcode, MQLONG reason, const char *resource)

{
	if ((compcode != MQCC_OK) || (reason != MQRC_NONE))
	{
		printf("MQSeries error with %s on %s - compcode = %d, reason = %d\n",
				mqcalltype, resource, compcode, reason);
	}
}

/********************************************************************/
/*                                                                  */
/*  Subroutine to connect to a queue manager.                       */
/*                                                                  */
/********************************************************************/

void connect2QM(char * qmname, PMQHCONN qm, PMQLONG cc, PMQLONG reason)

{
	/* tell what we are doing */
	printf("connecting to queue manager %s\n",qmname);

	/* connect to the queue manager */
	MQCONN(qmname, qm, cc, reason);
	checkerror("MQCONN", *cc, *reason, qmname);
}

/********************************************************************/
/*                                                                  */
/*  Subroutine to connect to a queue manager.                       */
/*                                                                  */
/********************************************************************/

void clientConnect2QM(char * qmname, PMQHCONN qm, int *maxMsgLen, PMQLONG cc, PMQLONG reason)

{
	char	*ptr;
	char	*connName;
	int		transType=MQXPT_TCP;
	char	mqserver[512];
	MQLONG	opencode;
	MQLONG	rc;
	MQCHAR48	name;
	MQLONG	Select[1];			/* attribute selectors           */
	MQLONG	IAV[1];				/* integer attribute values      */
	MQHOBJ	Hobj;               /* object handle                 */
	MQCHAR	charAttrs[64];		/* character attribute area      */
	MQOD	od={MQOD_DEFAULT};  /* Object Descriptor             */
	static	MQCD	mqcd={MQCD_CLIENT_CONN_DEFAULT};
	static	MQCNO	mqcno={MQCNO_DEFAULT};

	/* make a local copy of the QM name */
	mqserver[0] = 0;

	/* make sure the source string is not too long */
	if (strlen(qmname) < sizeof(mqserver) - 1)
	{
		strcpy(mqserver, qmname);
	}

	ptr = strchr(mqserver, '/');
	if (NULL == ptr)
	{
		/* no forward slash in the name - must be a queue manager name */
		/* connect to the queue manager */
		/* tell what we are doing */
		printf("connecting to %s\n",qmname);

		MQCONN(qmname, qm, cc, reason);
		checkerror("MQCONN", *cc, *reason, qmname);
	}
	else
	{
		ptr[0] = 0;
		ptr++;
		connName = strchr(ptr, '/');
		if (connName != NULL)
		{
			connName[0] = 0;
			connName++;
		}

		/* get the transport type */
		if (strcmp(ptr, "LOCAL") == 0)
		{
			transType = MQXPT_LOCAL;
		}

		if (strcmp(ptr, "LU62") == 0)
		{
			transType = MQXPT_LU62;
		}

		if (strcmp(ptr, "TCP") == 0)
		{
			transType = MQXPT_TCP;
		}

		if (strcmp(ptr, "NETBIOS") == 0)
		{
			transType = MQXPT_NETBIOS;
		}

		if (strcmp(ptr, "SPX") == 0)
		{
			transType = MQXPT_SPX;
		}

		if (strcmp(ptr, "DECNET") == 0)
		{
			transType = MQXPT_DECNET;
		}

		if (strcmp(ptr, "UDP") == 0)
		{
			transType = MQXPT_UDP;
		}

		/* now build a channel entry */
		memset(mqcd.ChannelName, ' ', sizeof(mqcd.ChannelName));
		if (strlen(mqserver) <= sizeof(mqcd.ChannelName))
		{
			memcpy(mqcd.ChannelName, mqserver, strlen(mqserver));
		}

		if ((connName != NULL) && (strlen(connName) <= sizeof(mqcd.ConnectionName)))
		{
			memcpy(mqcd.ConnectionName, connName, strlen(connName));
		}

		/* set the transport type */
		mqcd.TransportType = transType;

		/* clear the queue manager name */
		/*memset(mqcd.QMgrName, 0, sizeof(mqcd.QMgrName));*/

		/* set the channel reference */
		mqcno.ClientConnPtr = &mqcd;

		/* use a lower level for backwards compatibility (version 2 vs version 4) */
		mqcno.Version = MQCNO_VERSION_2;

		/* set version 4 instead of 7 for better backwards compatibility */
		mqcd.Version = MQCD_VERSION_4;

		/* try to connect to the queue manager */
		memset(name, ' ', sizeof(name));
		name[0] = 0;

		/* tell what we are doing */
		printf("connecting using %s channel %s at %s\n",ptr, mqserver, connName);
		MQCONNX(name, &mqcno, qm, cc, reason);
		checkerror("MQCONNX", *cc, *reason, qmname);

		if (MQCC_OK == *cc)
		{
			/* try to get the queue manager name and display it */
			/* Open a qmgr object */
			od.ObjectType = MQOT_Q_MGR;		/* open the queue manager object*/
			MQOPEN((*qm),                      /* connection handle            */
				   &od,                     /* object descriptor for queue  */
				   MQOO_INQUIRE +           /* open it for inquire          */
				   MQOO_FAIL_IF_QUIESCING,  /* but not if MQM stopping      */
				   &Hobj,                   /* object handle                */
				   &opencode,               /* MQOPEN completion code       */
				   &rc);					/* reason code                  */

			if (MQCC_OK == opencode)
			{
				memset(charAttrs, 0, sizeof(charAttrs));
				Select[0] = MQCA_Q_MGR_NAME;
				MQINQ((*qm),
					  Hobj,
					  1L,
					  Select,
					  0L,
					  NULL,
					  sizeof(charAttrs),
					  charAttrs,
					  &opencode,
					  &rc);

				if (MQCC_OK == opencode)
				{
					printf("Connected to %s\n", charAttrs);
				}

				/* try to get the maximum message length allowed */
				/* to avoid 2010 reason codes on MQGETs          */
				Select[0] = MQIA_MAX_MSG_LENGTH;
				IAV[0]=0;
				MQINQ((*qm),
					  Hobj,
					  1L,
					  Select,
					  1L,
					  IAV,
					  0L,
					  NULL,
					  &opencode,
					  &rc);

				if (MQCC_OK == opencode)
				{
					/* check if our maximum length is too large */
					if (((*maxMsgLen) > IAV[0]) && (IAV[0] > 0))
					{
						/* max message length is too large, so reduce it */
						(*maxMsgLen) = IAV[0];
					}
				}

				MQCLOSE((*qm),
						&Hobj,
						MQCO_NONE,
						&opencode,
						&rc);
			}
		}
	}
}

/*****************************************************/
/*                                                   */
/* Routine to recognize an MQMD.                     */
/*                                                   */
/* This routine checks for either an ASCII or        */
/* EBCDIC eye catcher as well as a version number.   */
/* The version number can be in either big-endian or */
/* little-endian binary.  If it is not the normal    */
/* representation for the platform then the MQMD     */
/* will be converted.                                */
/*                                                   */
/* Returns a zero ('0') if no MQMD is found and a    */
/* one ('1') if an MQMD is found.  The MQMD will be  */
/* converted if necessary.                           */
/*                                                   */
/*****************************************************/

int checkForMQMD(char * msgData, int msgLen)

{
	int				result=0;		/* did not find MQMD */
	int				v1r;			/* version 1 in opposite encoding */
	int				v2r;			/* version 2 in opposite encoding */
	MQMD			*mqmd;			/* pointer to MQMD */
	unsigned char	ebcdicID[4];	/* MQMD structure ID field translated to EBCDIC */
	unsigned char	asciiID[4];		/* MQMD structure ID field in ASCII */

	/* check the total length of the message data */
	if (msgLen >= sizeof(MQMD))
	{
		/* point to the message area using an MQMD structure */
		mqmd = (MQMD *)msgData;

		/* get the struture id in both ASCII and EBCDIC */
		memcpy(asciiID, MQMD_STRUC_ID, sizeof(mqmd->StrucId));
		AsciiToEbcdic(asciiID, (const unsigned char *)&ebcdicID, sizeof(ebcdicID));
		if ((memcmp(mqmd->StrucId, asciiID, sizeof(mqmd->StrucId)) != 0) && (memcmp(mqmd->StrucId, ebcdicID, sizeof(mqmd->StrucId)) != 0))
		{
			/* did not find the eyecatcher - return */
			return result;
		}

		/* check if the MQMD is in a different encoding sequence */
		v1r = reverseBytes4(MQMD_VERSION_1);
		v2r = reverseBytes4(MQMD_VERSION_2);

		/* check for a valid version number */
		if ((mqmd->Version != MQMD_VERSION_1) && (mqmd->Version != MQMD_VERSION_1) && (mqmd->Version != v1r) && (mqmd->Version != v2r))
		{
			/* not a valid version - not an MQMD */
			return result;
		}

		/* indicate an MQMD was found */
		result = 1;

		/* does the MQMD need to be converted? */
		if ((v1r == mqmd->Version) || (v2r == mqmd->Version))
		{
			/* it appears the mqmd was captured on a big-endian system */
			/* therefore, we will reverse the byte ordering in the mqmd */
			mqmd->Version = reverseBytes4(mqmd->Version); 
			mqmd->Report = reverseBytes4(mqmd->Report);
			mqmd->MsgType = reverseBytes4(mqmd->MsgType); 
			mqmd->Expiry = reverseBytes4(mqmd->Expiry);
			mqmd->Feedback = reverseBytes4(mqmd->Feedback); 
			mqmd->Encoding = reverseBytes4(mqmd->Encoding);
			mqmd->CodedCharSetId = reverseBytes4(mqmd->CodedCharSetId); 
			mqmd->Priority = reverseBytes4(mqmd->Priority);
			mqmd->Persistence = reverseBytes4(mqmd->Persistence);
			mqmd->BackoutCount = reverseBytes4(mqmd->BackoutCount);
			mqmd->PutApplType = reverseBytes4(mqmd->PutApplType);

			/* deal with the message id, correl id, accounting token */
			reverseBytes24(mqmd->MsgId, mqmd->MsgId);
			reverseBytes24(mqmd->CorrelId, mqmd->CorrelId);
			reverseBytes32(mqmd->AccountingToken, mqmd->AccountingToken);

			/* check if this is a version 1 or version 2 mqmd */
			if (mqmd->Version!= MQMD_VERSION_1)
			{
				/* deal with the group id */
				reverseBytes24(mqmd->GroupId, mqmd->GroupId);
				mqmd->MsgSeqNumber = reverseBytes4(mqmd->MsgSeqNumber);
				mqmd->Offset = reverseBytes4(mqmd->Offset);
				mqmd->MsgFlags = reverseBytes4(mqmd->MsgFlags);
				mqmd->OriginalLength = reverseBytes4(mqmd->OriginalLength);
			}
		}

		/* check if the MQMD is in EBCDIC */
		if (memcpy(mqmd->StrucId, ebcdicID, sizeof(mqmd->StrucId)) == 0)
		{
			/* need to convert the character fields in the MQMD */
			EbcdicToAscii((unsigned char *)&mqmd->Format, sizeof(mqmd->Format), (unsigned char *)&mqmd->Format);
			EbcdicToAscii((unsigned char *)&mqmd->ReplyToQ, sizeof(mqmd->ReplyToQ), (unsigned char *)&mqmd->ReplyToQ);
			EbcdicToAscii((unsigned char *)&mqmd->ReplyToQMgr, sizeof(mqmd->ReplyToQMgr), (unsigned char *)&mqmd->ReplyToQMgr);
			EbcdicToAscii((unsigned char *)&mqmd->UserIdentifier, sizeof(mqmd->UserIdentifier), (unsigned char *)&mqmd->UserIdentifier);
			EbcdicToAscii((unsigned char *)&mqmd->ApplIdentityData, sizeof(mqmd->ApplIdentityData), (unsigned char *)&mqmd->ApplIdentityData);
			EbcdicToAscii((unsigned char *)&mqmd->PutApplName, sizeof(mqmd->PutApplName), (unsigned char *)&mqmd->PutApplName);
			EbcdicToAscii((unsigned char *)&mqmd->PutDate, sizeof(mqmd->PutDate), (unsigned char *)&mqmd->PutDate);
			EbcdicToAscii((unsigned char *)&mqmd->PutTime, sizeof(mqmd->PutTime), (unsigned char *)&mqmd->PutTime);
			EbcdicToAscii((unsigned char *)&mqmd->ApplOriginData, sizeof(mqmd->ApplOriginData), (unsigned char *)&mqmd->ApplOriginData);
		}
	}

	/* return the result */
	return result;
}
