/* 
** CDMA 2000 project, SMPP module
**
** File name: smpp_func.c
** Written by Li Long at 2004-11-25
** CVS $Id: smpp_func.c,v0.1 2004/11/25 17:03:14 lilong Exp $
**
*/

#include "./include/smpp.h"
#include "./include/smpp_def.h"

extern int register_ssn(BYTE ssn, BYTE usertype);

/* by simon at 23/9/26 */
SocketsMsg SockMsg;

int smpp_init_new(u8 sys_id)
{
	smpp_system_no = sys_id & 0x01;
	#ifdef SMPP_DEBUG
	smpp_debugInit();
	#endif //end ifdef SMPP_DEBUG
	smpp_mibInit();
	smpp_loadParam();
	smpp_writeParam();

	heartbeat_init(0xffff);

	smpp_initTCPClient();
	smpp_initTCPServer();
	smpp_stateInit();
	smpp_msgInit();

//	if (FALSE == register_ssn(SMPP_PPS_SSN, SMPP_PPS_USERTYPE))
	if (FALSE == register_ssn(SMPP_PPS_SSN, SMPP_SMSC_USERTYPE))
		printf("SMPP register PPS SSN %d fail!\n", SMPP_PPS_SSN);
	if (FALSE == register_ssn(SMPP_SMSC_SSN, SMPP_SMSC_USERTYPE))
		printf("SMPP register SMSC SSN %d fail!\n", SMPP_SMSC_SSN);
	printf("SMPP init........ Complete.\n");

	return TRUE;
}

void smpp_reset()
{
	int i;
	for (i=0; i<MAX_SMPP_LINK; i++) {
		if (smpp_param[i].link_type == SMPP_TCP)
			smpp_closeTCPConnection(i);
	}
	close(Mysock.DaemonSock);
	smpp_init_new(smpp_system_no);

	for (i=0; i<MAX_SMPP_LINK; i++) {
		if (callBackFunc[i].link_proc != NULL)
			callBackFunc[i].link_proc(i, FALSE);
	}
}

/*	
	return:	bit7:	0-not fill, 1-fill
			bit6-0: link number (<=128)
	server_type: 0-SMPP_CLIENT; 1-SMPP_SERVER
	session_type: 0-BIND_TX; 1-BIND_RX; 2-BIND_TRX
	service_number_ptr: poiter to remote service number.
	message_mode: 0-STORE_AND_FORWARD; 1-DATAGRAM; 2-TRANSACTION
*/					
BYTE smpp_registerLink(BYTE server_type, BYTE session_type, char* service_number_ptr, BYTE message_mode)
{
	char debugBuf[1024];
	int i, len_get, len_in_para;
	
	for (i=0; i<MAX_SMPP_LINK; i++) {
		if ((smpp_param[i].link_enable & 0x01) == 0) continue;
		if (smpp_param[i].session_type != session_type) continue;
		if (smpp_param[i].server_type != server_type) continue;
		if(smpp_param[i].local_ip>0x00 && smpp_param[i].local_ip != GetLocalIP()) continue;
		
		len_get	= strlen(service_number_ptr);
		if (len_get > 8) {
			service_number_ptr[8] = 0;
			len_get = 8;
		}
		len_in_para = strlen(smpp_param[i].service_number);
		if (len_in_para < len_get) {
			if (strncmp(service_number_ptr, smpp_param[i].service_number, len_in_para) == 0) {
				smpp_param[i].message_mode = message_mode;
				return (BYTE)((i&0x7F)|0x80);
			}
		}
		else {
			if (strcmp(service_number_ptr, smpp_param[i].service_number) == 0) {
				smpp_param[i].message_mode = message_mode;
				return (BYTE)((i&0x7F)|0x80);
			}
		}
	}
	
	sprintf(debugBuf,"smpp_registerLink(server_type:%d,session_type:%d,service_number:%s,message_mode:%d) fail\r\n\r\n",server_type,session_type,service_number_ptr,message_mode);
	smpp_send_error(debugBuf);
	return 0;
}

void smpp_attach_link(BYTE linkNo, smpp_callBack_dataProc onDataRecv, 
	smpp_callBack_linkProc onLinkStateChange)
{
	BYTE	temp_status;

	if(linkNo>=MAX_SMPP_LINK) return;

	// removed by simon, 2023/11/04
	// added by simon at 2024/06/12
	smeg_debug_log("[smpp]link %d attached.\n", linkNo);
	if(smpp_state[linkNo] == ESTABLISHED ||
	   smpp_state[linkNo] == SUSPEND)
	{   
		if(callBackFunc[linkNo].link_proc!= NULL)
			callBackFunc[linkNo].link_proc(linkNo,TRUE);
		return;	
	}
	else if(smpp_state[linkNo] == WAITING)
	{
		if(callBackFunc[linkNo].link_proc!= NULL)
			callBackFunc[linkNo].link_proc(linkNo,FALSE);
		return;
	}

	linkAttachFlag[linkNo]			= TRUE;
	smpp_state[linkNo]				= IDLE;
	temp_status = IDLE;
	temp_status = temp_status<<((linkNo%4)*2);
	smpp_hearbeat_status[linkNo/4]	|= temp_status;
	callBackFunc[linkNo].data_proc	= onDataRecv;
	callBackFunc[linkNo].link_proc	= onLinkStateChange;
	exterEvent[linkNo][exter_head[linkNo]]	= Setup;
	exter_head[linkNo]	= (exter_head[linkNo]+1)&0x7;
	
	if(SMPP_TCP == smpp_param[linkNo].link_type)
	{	
		switch (smpp_param[linkNo].session_type) {
		case BIND_TX:
		case BIND_TRX:
			if (smpp_param[linkNo].server_type == SMPP_CLIENT)
				smpp_TCPClientFlag[linkNo]	= 1;
			break;
		case BIND_RX:
			if (smpp_param[linkNo].server_type == SMPP_SERVER)
				smpp_TCPClientFlag[linkNo]	= 1;
			break;
		}
	}
}

WORD smpp_send(BYTE linkNo, SMPP_MSG* pMsg, WORD dataLen)
{
	PDU_SUBMIT_SM	*pSubmitSM;
	PDU_SUBMIT_MULTI	*pSubmitMulti;
	PDU_DELIVER_SM	*pDeliverSM;
	PDU_DATA_SM		*pDataSM;
	PDU_QUERY_SM	*pQuerySM;
	PDU_CANCEL_SM	*pCancelSM;
	PDU_REPLACE_SM	*pReplaceSM;
	DWORD			seqNum;

	smeg_debug_log("\33[32mlink %d call smpp_send, message_type=%d\33[0m\n", linkNo, pMsg->message_type);
	switch (pMsg->message_type)
	{
	case 0x0B:	/* SUBMIT SM */
		pSubmitSM	= &pMsg->pdu.submit_sm;
		pSubmitSM->head.command_id		= htonl(SUBMIT_SM);
		pSubmitSM->head.command_status	= 0x0;
		//seqNum	= smpp_getSequenceNumber(linkNo);
		//pSubmitSM->head.sequence_number	= htonl(seqNum);
		pSubmitSM->head.sequence_number	= htonl(pSubmitSM->head.sequence_number);

		smpp_encode_msg(linkNo, pMsg, SubmitSM);
		break;
	case 0x0C:	/* SUBMIT SM RESP */
		pMsg->pdu.submit_sm_resp.head.command_id	= htonl(SUBMIT_SM_RESP);
		seqNum	= ntohl(pMsg->pdu.submit_sm_resp.head.sequence_number);
		smpp_encode_msg(linkNo, pMsg, SubmitSMResp);
		break;
	case 0x0D:	/* SUBMIT MULTI */
		pSubmitMulti = &pMsg->pdu.submit_multi;
		pMsg->pdu.submit_multi.head.command_id		= htonl(SUBMIT_MULTI);
		pMsg->pdu.submit_multi.head.command_status	= 0x0;
		//seqNum	= smpp_getSequenceNumber(linkNo);
		//pMsg->pdu.submit_multi.head.sequence_number	= htonl(seqNum);
		pSubmitMulti->head.sequence_number	= htonl(pSubmitMulti->head.sequence_number);

		smpp_encode_msg(linkNo, pMsg, SubmitMulti);
		break;
	case 0x0E:	/* SUBMIT MULTI RESP */
		pMsg->pdu.submit_multi_resp.head.command_id	= htonl(SUBMIT_MULTI_RESP);
		seqNum	= ntohl(pMsg->pdu.submit_multi_resp.head.sequence_number);
		smpp_encode_msg(linkNo, pMsg, SubmitMultiResp);
		break;
	case 0x0F:	/* DELIVER SM */
		pDeliverSM	= &pMsg->pdu.deliver_sm;
		pDeliverSM->head.command_id			= htonl(DELIVER_SM);
		pDeliverSM->head.command_status		= 0x0;
		//seqNum	= smpp_getSequenceNumber(linkNo);
		//pDeliverSM->head.sequence_number	= htonl(seqNum);
		pDeliverSM->head.sequence_number	= htonl(pDeliverSM->head.sequence_number);
		
		pDeliverSM->schedule_delivery_time	= 0;
		pDeliverSM->validity_period			= 0;
		pDeliverSM->replace_if_present_flag	= 0;
		pDeliverSM->sm_default_msg_id		= 0;

		smpp_encode_msg(linkNo, pMsg, DeliverSM);
		break;
	case 0x10:	/* DELIVER SM RESP */
		pMsg->pdu.deliver_sm.head.command_id		= htonl(DELIVER_SM_RESP);
		seqNum	= ntohl(pMsg->pdu.deliver_sm.head.sequence_number);
		smpp_encode_msg(linkNo, pMsg, DeliverSMResp);
		break;
	case 0x11:	/* DATA SM */
		pDataSM	= &pMsg->pdu.data_sm;
		pDataSM->head.command_id		= htonl(DATA_SM);
		pDataSM->head.command_status	= 0x0;
		//seqNum	= smpp_getSequenceNumber(linkNo);
		//pDataSM->head.sequence_number	= htonl(seqNum);
		pDataSM->head.sequence_number	= htonl(pDataSM->head.sequence_number);

		smpp_encode_msg(linkNo, pMsg, DataSM);
		break;
	case 0x12:	/* DATA SM RESP */
		pMsg->pdu.data_sm_resp.head.command_id			= htonl(DATA_SM_RESP);
		seqNum	= ntohl(pMsg->pdu.data_sm_resp.head.sequence_number);
		smpp_encode_msg(linkNo, pMsg, DataSMResp);
		break;
	case 0x13:	/* QUERY SM */
		pQuerySM	= &pMsg->pdu.query_sm;
		pMsg->pdu.query_sm.head.command_id			= htonl(QUERY_SM);
		pMsg->pdu.query_sm.head.command_status		= 0x0;
		//seqNum	= smpp_getSequenceNumber(linkNo);
		//pMsg->pdu.query_sm.head.sequence_number		= htonl(seqNum);
		pQuerySM->head.sequence_number	= htonl(pQuerySM->head.sequence_number);

		smpp_encode_msg(linkNo, pMsg, QuerySM);
		break;
	case 0x14:	/* QUERY SM RESP */
		pMsg->pdu.query_sm_resp.head.command_id			= htonl(QUERY_SM_RESP);
		seqNum	= ntohl(pMsg->pdu.query_sm_resp.head.sequence_number);
		smpp_encode_msg(linkNo, pMsg, QuerySMResp);
		break;
	case 0x15:	/* CANCEL SM */
		pCancelSM	= &pMsg->pdu.cancel_sm;
		pMsg->pdu.cancel_sm.head.command_id			= htonl(CANCEL_SM);
		pMsg->pdu.cancel_sm.head.command_status		= 0x0;
		//seqNum	= smpp_getSequenceNumber(linkNo);
		//pMsg->pdu.cancel_sm.head.sequence_number	= htonl(seqNum);
		pCancelSM->head.sequence_number	= htonl(pCancelSM->head.sequence_number);

		smpp_encode_msg(linkNo, pMsg, CancelSM);
		break;
	case 0x16:	/* CANCEL SM RESP */
		pMsg->pdu.cancel_sm_resp.head.command_id		= htonl(CANCEL_SM_RESP);
		seqNum	= ntohl(pMsg->pdu.cancel_sm_resp.head.sequence_number);
		smpp_encode_msg(linkNo, pMsg, CancelSMResp);
		break;
	case 0x17:	/* REPLACE SM */
		pReplaceSM	= &pMsg->pdu.replace_sm;
		pMsg->pdu.replace_sm.head.command_id		= htonl(REPLACE_SM);
		pMsg->pdu.replace_sm.head.command_status	= 0x0;
		//seqNum	= smpp_getSequenceNumber(linkNo);
		//pMsg->pdu.replace_sm.head.sequence_number	= htonl(seqNum);
		pReplaceSM->head.sequence_number	= htonl(pReplaceSM->head.sequence_number);
		smpp_encode_msg(linkNo, pMsg, ReplaceSM);
		break;
	case 0x18:	/* REPLACE SM RESP */
		pMsg->pdu.replace_sm_resp.head.command_id	= htonl(REPLACE_SM_RESP);
		seqNum	= ntohl(pMsg->pdu.replace_sm_resp.head.sequence_number);
		smpp_encode_msg(linkNo, pMsg, ReplaceSMResp);
		break;
	default:
		smeg_debug_log("[ERROR]link %d no such message type!(=%02x)\n", linkNo, pMsg->message_type);
		return 0;
	}
	
	return seqNum;
}

void smpp_close(BYTE linkNo)
{
	exterEvent[linkNo][exter_head[linkNo]]	= Close;
	exter_head[linkNo]	= (exter_head[linkNo]+1)&0x7;
}

void smpp_PrimitiveSend(int linkNo, int event_id)
{
	switch (event_id)
	{
	case BindTransmitter:
		smpp_sendBind(linkNo, BindTransmitter);
		break;
	case BindTransmitterResp:
		smpp_sendBindResp(linkNo, BindTransmitterResp, 0);
		break;
	case BindReceiver:
		smpp_sendBind(linkNo, BindReceiver);
		break;
	case BindReceiverResp:
		smpp_sendBindResp(linkNo, BindReceiverResp, 0);
		break;
	case BindTransceiver:
		smpp_sendBind(linkNo, BindTransceiver);
		break;
	case BindTransceiverResp:
		smpp_sendBindResp(linkNo, BindTransceiverResp, 0);
		break;
	case Unbind:
		smpp_sendUnbind(linkNo);
		break;
	case UnbindResp:
		smpp_sendUnbindResp(linkNo);
		break;
	case EnquireLink:
		smpp_sendEnquireLink(linkNo);
		break;
	case EnquireLinkResp:
		smpp_sendEnquireLinkResp(linkNo);
		break;
	case Outbind:
		smpp_sendOutbind(linkNo);
		break;
	default:
		break;
	}
}

void smpp_initTCPClintByLink(unsigned int linkNo)
{
    int on=1, flag=1;
    struct sockaddr_in    local_addr;

    if(linkNo>=MAX_SMPP_LINK) return;    
    
    if (((smpp_param[linkNo].link_enable & 0x01) == 0) || (smpp_param[linkNo].link_type == SMPP_UDP))
    {
        return;
    }
    if ((Mysock.client_sock[linkNo] = socket(AF_INET, SOCK_STREAM, 0))<0)
    {
        return;
    }
    if (setsockopt(Mysock.client_sock[linkNo], SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)
    {
        close(Mysock.client_sock[linkNo]);
        return;
    }
    if (setsockopt(Mysock.client_sock[linkNo], SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)) == -1)
    {
        close(Mysock.client_sock[linkNo]);
        return;
    }
    if (fcntl(Mysock.client_sock[linkNo], F_SETFL, O_NONBLOCK) == -1)
    {
        close(Mysock.client_sock[linkNo]);
        return;
    }
    /* Set this sockets as a Non-blocking sockets. */
    if (ioctl(Mysock.client_sock[linkNo], FIONBIO, &flag) == -1)
    {
        close(Mysock.client_sock[linkNo]);
        return;
    }

    bzero(&local_addr, sizeof(local_addr));
    local_addr.sin_family      = AF_INET;
    local_addr.sin_addr.s_addr = GetLocalIP();//htonl(INADDR_ANY);
    local_addr.sin_port        = 0;
    memset(local_addr.sin_zero, 0, sizeof(local_addr.sin_zero));
    if (bind(Mysock.client_sock[linkNo], (struct sockaddr *)&local_addr, sizeof(local_addr)) < 0)
    {
      perror("init TCP Client By Link: bind error");
      exit(1);
    }
}

void smpp_initTCPClient()
{
	int i;

	for (i=0; i<MAX_SMPP_LINK; i++)
	{
		smpp_initTCPClintByLink(i);
	}
}

void smpp_tcp_connect(int link)
{
	struct sockaddr_in	server;
	int on = 1, flag = 1;

	server.sin_family	= AF_INET;
	server.sin_port		= htons(SMPP_PORT);
	server.sin_addr.s_addr	= smpp_param[link].remote_ip;
	/* Connect to the server. */
	if (connect(Mysock.client_sock[link], (struct sockaddr *)&server, sizeof(server))<0)
	{
		if( EINPROGRESS !=errno && EALREADY != errno)//ch:Bad file descriptor
		{
			close(Mysock.client_sock[link]);
			Mysock.client_sock[link]=0;
			smpp_initTCPClintByLink(link);
			/*
			if ((Mysock.client_sock[link] = socket(AF_INET, SOCK_STREAM, 0))<0)
				return;
			if (setsockopt(Mysock.client_sock[link], SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)
			{
				close(Mysock.client_sock[link]);
				return;
			}
			if (setsockopt(Mysock.client_sock[link], SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)) == -1)
			{
				close(Mysock.client_sock[link]);
				return;
			}
			if (fcntl(Mysock.client_sock[link], F_SETFL, O_NONBLOCK) == -1)
			{
				close(Mysock.client_sock[link]);
				return;
			}
			if (ioctl(Mysock.client_sock[link], FIONBIO, &flag) == -1)
			{
				close(Mysock.client_sock[link]);
				return;
			}
			*/
		}
		//perror("connect");
		return;	
	}
	/*
	FD_SET(clientSockFd, &Mysock.readfds);
	FD_SET(clientSockFd, &Mysock.writefds);
	FD_SET(clientSockFd, &Mysock.exceptfds);
	*/
	Mysock.Sockets[link]	= Mysock.client_sock[link];
	Mysock.client_sock[link] =-1;

	smeg_debug_log("[init TCP client]link %d socket number = %d\n", link, Mysock.Sockets[link]);
}

extern DWORD GetLocalIP();

void smpp_initTCPServer()
{
	int serverSockFd;
	int on=1;
	struct sockaddr_in	local_addr;

	serverSockFd	= socket(AF_INET, SOCK_STREAM, 0);
	if (serverSockFd < 0)
	{
		perror("server socket error!");
		exit(1);
	}
	//if ((serverSockFd = socket(AF_INET, SOCK_STREAM, 0))<0)	{ return (-1); }
	if (setsockopt(serverSockFd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)	
	{
		close(serverSockFd);
		return;
	}
	if (setsockopt(serverSockFd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)) == -1)
	{
		close(serverSockFd);
		return;
	}
	if (fcntl(serverSockFd, F_SETFL, O_NONBLOCK) == -1)
	{
		close(serverSockFd);
		return;
	}
	bzero(&local_addr, sizeof(local_addr));
	local_addr.sin_family		= AF_INET;
//	local_addr.sin_addr.s_addr	= inet_addr(smpp_getLocalIP());
	local_addr.sin_addr.s_addr = GetLocalIP();//htonl(INADDR_ANY);
	local_addr.sin_port			= htons(SMPP_PORT);
	memset(local_addr.sin_zero, 0, sizeof(local_addr.sin_zero));
//	printf("Init TCP server: ip = %s, port = %d\n", 
//		inet_ntoa(local_addr.sin_addr), 
//		ntohs(local_addr.sin_port));
	if (bind(serverSockFd, (struct sockaddr *)&local_addr, sizeof(local_addr)) < 0)
	{
		perror("bind error");
		exit(1);
	}
	if (listen(serverSockFd, MAX_SMPP_LINK*2) < 0)
	{
		perror("listen error");
		exit(1);
	}

	/*set this socket as a non-blocking socket. 
	if (ioctl(serverSockFd, FIONBIO, &flag) == -1)
	{
		perror("ioctl error");
		close(serverSockFd);
	}
	*/
	Mysock.DaemonSock	= serverSockFd;
	FD_SET(serverSockFd, &Mysock.readfds);
	FD_SET(serverSockFd, &Mysock.exceptfds);

	smeg_debug_log("init TCP server complete. socket number = %d.\n",serverSockFd);
}

WORD smpp_getServerPort()
{
	static WORD	offset	= 0;

	offset	= offset % 1000;
	return (offset + offset++);
}

DWORD smpp_getSequenceNumber(int linkNo)
{
	DWORD sequence_number;

	sequence_number	= smpp_link[linkNo].seq_num;
	sequence_number++;
	smpp_link[linkNo].seq_num	= sequence_number;
	#ifdef SMPP_PRINT_SCREEN
//		printf("link %d sequence number = %d.\n", linkNo, sequence_number);
	#endif //end ifdef SMPP_PRINT_SCREEN
	return sequence_number;
}

char *smpp_getLocalIP()
{
	char	hostname[128];

	char	*host_ip;
	struct hostent	*host;
	if (gethostname(hostname,128) == 0)
	{
		if ((host = gethostbyname(hostname)) != NULL)
		{
			((struct in_addr *)(*host->h_addr_list))->s_addr = GetLocalIP();
			host_ip = inet_ntoa(*(struct in_addr *)(*host->h_addr_list));
		}
		else
		{
			perror("[smpp] gethostbyname");
			exit(2);
		}
	}

	return host_ip;
}

int smpp_closeTCPConnection(int linkNo)
{
	int retcode;

	if ( (linkNo>=MAX_SMPP_LINK) || (linkNo<0) || (Mysock.Sockets[linkNo]<=0) )
		return (0);
	retcode	= close(Mysock.Sockets[linkNo]);

	FD_CLR(Mysock.Sockets[linkNo], &Mysock.readfds);
	FD_CLR(Mysock.Sockets[linkNo], &Mysock.writefds);
	FD_CLR(Mysock.Sockets[linkNo], &Mysock.exceptfds);
	Mysock.Sockets[linkNo]	= 0;
	
	#ifdef SMPP_PRINT_SCREEN
//		printf("link %d close TCP connection.\n", linkNo);
	#endif
	return (retcode);
}

int QuerySocketsMsg()
{
	fd_set rfds,wfds,efds;
	int retcode,i;
	struct timeval TimeOut;

	rfds	= Mysock.readfds;
	wfds	= Mysock.writefds;
	efds	= Mysock.exceptfds;
	TimeOut.tv_sec	= 0;
	TimeOut.tv_usec	= 0;
	bzero((char *)&SockMsg, sizeof(SockMsg));
	if ((retcode = select(MAX_SMPP_LINK, &rfds, &wfds, &efds, &TimeOut)) == 0)
	{
		return (0);
	}
	if (FD_ISSET(Mysock.DaemonSock, &rfds))
	{
		SockMsg.AcceptNum	= 1;
	}
	for (i=0; i<MAX_SMPP_LINK; i++)
	{
		if ((Mysock.Sockets[i]>0) && (FD_ISSET(Mysock.Sockets[i],&rfds)))
		{
			SockMsg.ReadQueue[SockMsg.ReadNum++] = i;
		}
	}
	for (i=0; i<MAX_SMPP_LINK; i++)
	{
		if ((Mysock.Sockets[i]>0) && (FD_ISSET(Mysock.Sockets[i],&wfds)))
		{
			SockMsg.WriteQueue[SockMsg.WriteNum++] = i;
		}
	}
	if (FD_ISSET(Mysock.DaemonSock, &efds))
	{
		SockMsg.AcceptNum = -1;
	}
	for (i=0; i<MAX_SMPP_LINK; i++)
	{
		if ((Mysock.Sockets[i]>0) && (FD_ISSET(Mysock.Sockets[i],&efds)))
		{
			SockMsg.WriteQueue[SockMsg.ExceptNum++] = i;
		}
	}
	
	return (retcode);
}

int smpp_check_sysid_route(char* sysid_rv, BYTE sysidlen_rv, BYTE server_type, BYTE session_type) 
{
	int i;
	char debugBuf[256];
	
	for (i=0; i<MAX_SMPP_LINK; i++) {
		if (linkAttachFlag[i] == FALSE) continue;		
		if ((smpp_param[i].link_enable & 0x01) == 0) continue;			
		if (smpp_param[i].server_type == server_type) continue;
		if (smpp_param[i].session_type != session_type) continue;
			
		#ifdef SMPP_DEBUG
		if ((smppDebug.debug_switch == 1) || (smppDebug.link_switch[i] == 1)) {
			sprintf(debugBuf,
				"link: %d, link type: %d\n \
				[RECEIVE] system id: %s, sysidlen: %d\n \
				[PARAM]   system id: %s\n",
				i, smpp_param[i].link_type, sysid_rv, sysidlen_rv, smpp_param[i].sys_id);
			smpp_send_ascout(debugBuf);
		}
		#endif
		if (memcmp(sysid_rv, smpp_param[i].sys_id, sysidlen_rv) == 0)
			return i;
	}
	return (-1);
}

void smpp_setSccpAddr(int linkNo, SCCP_ADDR *pSrcAddr, SCCP_ADDR *pDstAddr)
{
	/* set SCCP source addr. */
	pSrcAddr->RI	= 0;
	pSrcAddr->GTI	= 4;
	pSrcAddr->TT	= 0;
	pSrcAddr->NP	= 1; /* MSISDN */
	pSrcAddr->NAI	= 4;
	pDstAddr->SSN	= smpp_param[linkNo].localSSN;
	pSrcAddr->len	= smpp_ascii2GTAI(pSrcAddr->GTAI, smpp_param[linkNo].localGTT);

	/* set SCCP destination address. */
	pDstAddr->RI	= 0;
	pDstAddr->GTI	= 4;
	pDstAddr->TT	= 0;
	pDstAddr->NP	= 1; /* MSISDN */
	pDstAddr->NAI	= 4;
	pDstAddr->SSN	= smpp_param[linkNo].remoteSSN;
	pDstAddr->len	= smpp_ascii2GTAI(pDstAddr->GTAI, smpp_param[linkNo].remoteGTT);
}

BYTE smpp_ascii2GTAI(BYTE *pGTAI, char *pGTT)
{
	BYTE len, bcd;
	int i;
	char debugBuf[1024];

	len	= strlen(pGTT);
	if (len < 1)	return 0;
	if (len==16) len = 15;//last BYTE is ssn		
	for (i=0; i<len; i++)
	{
		bcd	= pGTT[i]-'0';
		if (i%2 == 0)
			pGTAI[i/2]	= 0|bcd;
		else
			pGTAI[i/2]	= pGTAI[i/2]|(bcd<<4);
	}
	if (len%2 == 1)
		pGTAI[(len-1)/2]	|= 0xf0;
	
	#ifdef SMPP_DEBUG
	if (smppDebug.debug_switch == 1) {
		sprintf(debugBuf, "GTAI: ");
		for (i=0; i<(len+1)/2; i++)
		{
			sprintf(debugBuf+6+i*2, "%02X", pGTAI[i]);
		}
		debugBuf[6+i*2] = 0x0a;
		debugBuf[7+i*2] = 0;
		smpp_send_ascout(debugBuf);
	}
	#endif
	/*
	#ifdef SMPP_PRINT_SCREEN
	printf("GTAI[]: ");
	for (i=0; i<(len+1)/2; i++)
	{
		printf("0x%02x ", pGTAI[i]);
	}
	printf("\n");
	#endif
	*/
	return len;
}

void smpp_GTAI2ascii(char *pGTT, BYTE *pGTAI, BYTE len)
{
	BYTE i;

	for (i=0; i<(len+1)/2; i++)
	{
		pGTT[i*2]	= (pGTAI[i]&0xf) + '0';
		if ((pGTAI[i]>>4) == 0xf)
		{
			pGTT[i*2+1]	= '\0';
		}
		else
		{
			pGTT[i*2+1]	= ((pGTAI[i]>>4)&0xf) + '0';
		}
	}
	pGTT[len]	= '\0';
}

void smpp_addTLV(BYTE *smpp_msg, WORD optionTag, BYTE *pMsg, WORD len)
{
	WORD	ns_optionTag;
	WORD	ns_len;

	ns_optionTag	= htons(optionTag);
	switch (optionTag)
	{
	/* value 0 byte */
	case ALERT_ON_MESSAGE_DELIVERY:
		memcpy(smpp_msg, &ns_optionTag, 2);
		len = 0;
		memcpy(smpp_msg+2, &len, 2);
		break;
	/* value 1 byte for integer. */
	case DEST_ADDR_SUBUNIT:
	case SOURCE_ADDR_SUBUNIT:
	case DEST_NETWORK_TYPE:
	case SOURCE_NETWORK_TYPE:
	case DEST_BEARER_TYPE:
	case SOURCE_BEARER_TYPE:
	case PRIVACY_INDICATOR:
	case PAYLOAD_TYPE:
	case LANGUAGE_INDICATOR:
	case USER_RESPONSE_CODE:
	case MS_MSG_WAIT_FACILITIES:
	case SAR_TOTAL_SEGMENTS:
	case SAR_SEGMENT_SEQNUM:
	case SC_INTERFACE_VERSION:
	case DISPLAY_TIME:
	case MS_VALIDITY:
	case DPF_RESULT:
	case SET_DPF:
	case MS_AVAILABILITY_STATUS:
	case DELIVERY_FAILURE_REASON:
	case MORE_MESSAGES_TO_SEND:
	case MESSAGE_STATE:
	case CALLBACK_NUM_PRES_IND:
	case NUMBER_OF_MESSAGES:
	case ITS_REPLY_TYPE:
	case USSD_SERVICE_OP:
		memcpy(smpp_msg, &ns_optionTag, 2);
		len	= 1;
		ns_len	= htons(len);
		memcpy(smpp_msg+2, &ns_len, 2);
		*(smpp_msg+4)	= *pMsg;
		break;
	/* value 2 bytes for integer */
	case DEST_TELEMATICS_ID:
	case SOURCE_TELEMATICS_ID:
	case USER_MESSAGE_REFERENCE:
	case SOURCE_PORT:
	case DESTINATION_PORT:
	case SAR_MSG_REF_NUM:
	case SMS_SIGNAL:
	case ITS_SESSION_INFO:
		memcpy(smpp_msg, &ns_optionTag, 2);
		len = 2;
		ns_len	= htons(len);
		memcpy(smpp_msg+2, &ns_len, 2);
		memcpy(smpp_msg+4, pMsg, 2);
		break;
	/* value 3 bytes for octet string */
	case NETWORK_ERROR_CODE:
		memcpy(smpp_msg, &ns_optionTag, 2);
		len	= 3;
		ns_len	= htons(len);
		memcpy(smpp_msg+2, &ns_len, 2);
		memcpy(smpp_msg+4, pMsg, 3);
		break;
	/* value 4 bytes for integer */
	case QOS_TIME_TO_LIVE:
		memcpy(smpp_msg, &ns_optionTag, 2);
		len	= 4;
		ns_len	= htons(len);
		memcpy(smpp_msg+2, &ns_len, 2);
		memcpy(smpp_msg+4, pMsg, 4);
		break;
	/* variable length */
	case ADDITIONAL_STATUS_INFO_TEXT:
	case RECEIPTED_MESSAGE_ID:
	case SOURCE_SUBADDRESS:
	case DEST_SUBADDRESS:
	case MESSAGE_PAYLOAD:
	case CALLBACK_NUM:
	case CALLBACK_NUM_ATAG:
		memcpy(smpp_msg, &ns_optionTag, 2);
		ns_len	= htons(len);
		memcpy(smpp_msg+2, &ns_len, 2);
		memcpy(smpp_msg+4, pMsg, len+1);
		break;
	}
}

void smpp_display_hex_msg(BYTE* disp_ptr, int len)
{
	int i;
	char debugBuf[4096];

	for (i=0; i<len; i++)
	{
		sprintf(debugBuf+i*3, "%02X ", disp_ptr[i]);
	}
	debugBuf[len*3] = 0x0a;	//line feed
	debugBuf[len*3+1] = 0;	
	smpp_send_ascout(debugBuf);
}

void smpp_command_id_to_string(char *comm_str, DWORD command_id)
{
	switch (command_id)
	{
	case BIND_TRANSMITTER:
		strcpy(comm_str, "BIND_TRANSMITTER");
		break;
	case BIND_TRANSMITTER_RESP:
		strcpy(comm_str, "BIND_TRANSMITTER_RESP");
		break;
	case BIND_RECEIVER:
		strcpy(comm_str, "BIND_RECEIVER");
		break;
	case BIND_RECEIVER_RESP:
		strcpy(comm_str, "BIND_RECEIVER_RESP");
		break;
	case BIND_TRANSCEIVER:
		strcpy(comm_str, "BIND_TRANSCEIVER");
		break;
	case BIND_TRANSCEIVER_RESP:
		strcpy(comm_str, "BIND_TRANSCEIVER_RESP");
		break;
	case ENQUIRE_LINK:
		strcpy(comm_str, "ENQUIRE_LINK");
		break;
	case ENQUIRE_LINK_RESP:
		strcpy(comm_str, "ENQUIRE_LINK_RESP");
		break;
	case SUBMIT_SM:
		strcpy(comm_str, "SUBMIT_SM");
		break;
	case SUBMIT_SM_RESP:
		strcpy(comm_str, "SUBMIT_SM_RESP");
		break;
	case SUBMIT_MULTI:
		strcpy(comm_str, "SUBMIT_MULTI");
		break;
	case SUBMIT_MULTI_RESP:
		strcpy(comm_str, "SUBMIT_MULTI_RESP");
		break;
	case DELIVER_SM:
		strcpy(comm_str, "DELIVER_SM");
		break;
	case DELIVER_SM_RESP:
		strcpy(comm_str, "DELIVER_SM_RESP");
		break;
	case DATA_SM:
		strcpy(comm_str, "DATA_SM");
		break;
	case DATA_SM_RESP:
		strcpy(comm_str, "DATA_SM_RESP");
		break;
	case UNBIND:
		strcpy(comm_str, "UNBIND");
		break;
	case UNBIND_RESP:
		strcpy(comm_str, "UNBIND_RESP");
		break;
	case GENERIC_NAK:
		strcpy(comm_str, "GENERIC_NAK");
		break;
	case OUTBIND:
		strcpy(comm_str, "OUTBIND");
		break;
	default:
		strcpy(comm_str, "\x1b[31mUnknown Command Id\x1b[0m");
		break;
	}
}

void smpp_command_status_to_string(char* status_str, DWORD command_status)
{
	switch (command_status)
	{
	case ESME_ROK:
		strcpy(status_str, "No Error");
		break;
	case ESME_RINVMSGLEN:
		strcpy(status_str, "Message Length is invalid");
		break;
	case ESME_RINVCMDLEN:
		strcpy(status_str, "Command Length is invalid");
		break;
	case ESME_RINVCMDID:
		strcpy(status_str, "Invalid Command ID");
		break;
	case ESME_RINVBNDSTS:
		strcpy(status_str, "Incorrect BIND status for given command");
		break;
	case ESME_RALYBND:
		strcpy(status_str, "ESME Already in Bound State");
		break;
	case ESME_RINVPRTFLG:
		strcpy(status_str, "Invalid Priority Flag");
		break;
	case ESME_RINVREGDLVFLG:
		strcpy(status_str, "Invalid Registered Delivery Flag");
		break;
	case ESME_RSYSERR:
		strcpy(status_str, "System Error");
		break;
	case ESME_RINVSRCADR:
		strcpy(status_str, "Invalid Source Address");
		break;
	case ESME_RINVDSTADR:
		strcpy(status_str, "Invalid Dest Addr");
		break;
	case ESME_RINVMSGID:
		strcpy(status_str, "Message ID is invalid");
		break;
	case ESME_RBINDFAIL:
		strcpy(status_str, "Bind Failed");
		break;
	case ESME_RINVPASWD:
		strcpy(status_str, "Invalid Password");
		break;
	case ESME_RINVSYSID:
		strcpy(status_str, "Invalid System ID");
		break;
	case ESME_RCANCELFAIL:
		strcpy(status_str, "Cancel SM Failed");
		break;
	case ESME_RREPLACEFAIL:
		strcpy(status_str, "Replace SM Failed");
		break;
	case ESME_RMSGQFUL:
		strcpy(status_str, "Message Queue Full");
		break;
	case ESME_RINVSERTYP:
		strcpy(status_str, "Invalid Service Type");
		break;
	case ESME_RINVNUMDESTS:
		strcpy(status_str, "Invalid Number of Destinations");
		break;
	case ESME_RINVDLNAME:
		strcpy(status_str, "Invalid Distribution List Name");
		break;
	case ESME_RINVDESTFLAG:
		strcpy(status_str, "Destination flag is invalid");
		break;
	case ESME_RINVSUBREP:
		strcpy(status_str, "Invalid 'submit with replace' reques( i.e. submit_sm with replace_if_present_flag set");
		break;
	case ESME_RINVESMCLASS:
		strcpy(status_str, "Invalid esm_class filed data");
		break;
	case ESME_RCNTSUBDL:
		strcpy(status_str, "Cannot Submit to Distribution List");
		break;
	case ESME_RSUBMITFAIL:
		strcpy(status_str, "submit_sm or submit_multi failed");
		break;
	case ESME_RINVSRCTON:
		strcpy(status_str, "Invalid Source address TON");
		break;
	case ESME_RINVSRCNPI:
		strcpy(status_str, "Invalid Source address NPI");
		break;
	case ESME_RINVDSTTON:
		strcpy(status_str, "Invalid Destination address TON");
		break;
	case ESME_RINVDSTNPI:
		strcpy(status_str, "Invalid Destination address NPI");
		break;
	case ESME_RINVSYSTYP:
		strcpy(status_str, "Invalid system_tpye field");
		break;
	case ESME_RINVREPFLAG:
		strcpy(status_str, "Invalid replace_if_present flag");
		break;
	case ESME_RINVNUMMSGS:
		strcpy(status_str, "Invalid number of messages");
		break;
	case ESME_RTHROTTLED:
		strcpy(status_str, "Throttling error(ESME has exceeded allowed message limits)");
		break;
	case ESME_RINVSCHED:
		strcpy(status_str, "Invalid Scheduled Delivery Time");
		break;
	case ESME_RINVEXPIRY:
		strcpy(status_str, "Invalid message validity period(Expiry time)");
		break;
	case ESME_RINVDFTMSGID:
		strcpy(status_str, "Predefined Message Invalid or Not Found");
		break;
	case ESME_RX_T_APPN:
		strcpy(status_str, "ESME Receiver Temporary App Error Code");
		break;
	case ESME_RX_P_APPN:
		strcpy(status_str, "ESME Receiver Permanent App Error Code");
		break;
	case ESME_RX_R_APPN:
		strcpy(status_str, "ESME Receiver Reject Message Error Code");
		break;
	case ESME_RQUERYFAIL:
		strcpy(status_str, "query_sm request failed");
		break;
	case ESME_RINVOPTPARSTREAM:
		strcpy(status_str, "Error in the optional part of the PDU Body");
		break;
	case ESME_ROPTPARNOTALLWD:
		strcpy(status_str, "Optional Parameter Length");
		break;
	case ESME_RINVPARSLEN:
		strcpy(status_str, "Invalid Parameter Length");
		break;
	case ESME_RMISSINGOPTPARAM:
		strcpy(status_str, "Expected Optional Parameter missing");
		break;
	case ESME_RINVOPTPARAMVAL:
		strcpy(status_str, "Invalid Optional Parameter Value");
		break;
	case ESME_RDELIVERYFAILURE:
		strcpy(status_str, "Delivery Failure (used for data_sm_resp)");
		break;
	case ESME_RUNKNOWNERR:
		strcpy(status_str, "Unknown Error");
		break;
	default:
		strcpy(status_str, "Reserved");
		break;
	}
}

void smpp_ton_to_string(char* ton_str, BYTE ton)
{
	switch (ton)
	{
	case TON_UNKNOWN:
		strcpy(ton_str, "Unknown");
		break;
	case TON_INTERNATIONAL:
		strcpy(ton_str, "International");
		break;
	case TON_NATIONAL:
		strcpy(ton_str, "National");
		break;
	case TON_NETWORK_SPECIFIC:
		strcpy(ton_str, "Network Specific");
		break;
	case TON_SUBSCRIBER_NUMBER:
		strcpy(ton_str, "Subscriber Number");
		break;
	case TON_ALPHANUMERIC:
		strcpy(ton_str, "Alphanumeric");
		break;
	case TON_ABBREVIATE:
		strcpy(ton_str, "Abbreviated");
		break;
	default:
		strcpy(ton_str, "Reserved");
		break;
	}
}

void smpp_npi_to_string(char* npi_str, BYTE npi)
{
	switch (npi)
	{
	case NPI_UNKNOWN:
		strcpy(npi_str, "Unknown");
		break;
	case NPI_ISDN:
		strcpy(npi_str, "ISDN(E163/E164)");
		break;
	case NPI_DATA:
		strcpy(npi_str, "Data(X.121)");
		break;
	case NPI_TELEX:
		strcpy(npi_str, "Telex(F.69)");
		break;
	case NPI_LAND_MOBILE:
		strcpy(npi_str, "Land Mobile(E.212)");
		break;
	case NPI_NATIONAL:
		strcpy(npi_str, "National");
		break;
	case NPI_PRIVATE:
		strcpy(npi_str, "Private");
		break;
	case NPI_ERMES:
		strcpy(npi_str, "ERMES");
		break;
	case NPI_INTERNET:
		strcpy(npi_str, "Internet(IP)");
		break;
	default:
		strcpy(npi_str, "Reserved");
		break;
	}
}

/* msg_ptr start with optional parameters. len=optional parameter total length */
int smpp_decode_optional_parameter(SMPP_OPTIONAL_PARAMETER* opt_ptr, BYTE* msg_ptr, int t_len)
{
	int		param_counter = 0, pointer = 0;
	WORD	tag = 0, len = 0;
	char	debugBuf[1024];
	
	while (pointer < t_len)
	{
		tag = msg_ptr[pointer++];
		tag	= (tag<<8) + msg_ptr[pointer++];
		opt_ptr[param_counter].tag = tag;

		len	= msg_ptr[pointer++];
		len = (len<<8) + msg_ptr[pointer++];
		opt_ptr[param_counter].len = len;

		memcpy(&opt_ptr[param_counter].value, msg_ptr+pointer, len);
		pointer = pointer + len;

		#ifdef SMPP_DEBUG
		sprintf(debugBuf, "optional parameter %d: tag = 0x%04X, len = %d, value[0] = 0x%02X",
			param_counter, tag, len, *(msg_ptr+pointer));
		//smpp_send_ascout(debugBuf);
		//printf("%s\n", debugBuf);
		#endif

		param_counter++;
	}

	return param_counter;
}
