#include <stdlib.h>
#include "sms_smpp.h"
#include "smslib.h"
#include "tcplib.h"

extern int emsg(const char *format, ...);
extern int imsg(const char *format, ...);
extern int dmsg(const char *format, ...);

#define FALSE 0
#define TRUE !FALSE

#include "sms_smpp_defines.h"

#define setint(mem,sz,i) {				\
	mem[sz++] = (i & 0xFF000000) >> 24;	\
	mem[sz++] = (i & 0x00FF0000) >> 16;	\
	mem[sz++] = (i & 0x0000FF00) >> 8;	\
	mem[sz++] = (i & 0x000000FF);		\
}

#define getint(mem,sz,i) {	\
	i = mem[sz++] << 24;	\
	i += mem[sz++] << 16;	\
	i += mem[sz++] << 8;	\
	i += mem[sz++];			\
}

typedef struct pdu {
	SMPP_T_INTEGER length;
	SMPP_T_INTEGER cmd_id;
	SMPP_T_INTEGER cmd_status;
	SMPP_T_INTEGER sequence;

	SMPP_T_BINARY body;
	SMPP_T_BINARY upto;
	int bsz;
} Pdu;

Pdu *pdu_dup(Pdu *p);
void pdu_free(Pdu **p);

typedef struct pdu_list {
	struct pdu_list *next;
	Pdu *p;
} PduList;

void pdulist_add(PduList **l, Pdu *p) {
	PduList *nl;

	if (!p) return ;

	nl = calloc(1,sizeof(PduList));
	nl->p = pdu_dup(p);
	nl->next = *l;
	*l = nl;
}
void pdulist_free(PduList **l) {
	PduList *p,*n;

	for(p = *l; p; p = n) {
		n = p->next;
		pdu_free(&p->p);
		free(p);
	}
	*l = NULL;
}

Pdu *pdu_new(SMPP_T_INTEGER cmd_id, SMPP_T_INTEGER cmd_status) {
	Pdu *p = calloc(1,sizeof(Pdu));
	p->cmd_status = cmd_status;
	p->cmd_id = cmd_id;
	return p;
}
Pdu *pdu_dup(Pdu *p) {
	Pdu *np;

	if (!p) return NULL;
	np = calloc(1,sizeof(Pdu));
	memcpy(np,p,sizeof(Pdu));
	np->body = calloc(np->bsz,sizeof(SMPP_T_CHAR));
	memcpy(np->body,p->body,p->bsz);
	np->upto = NULL;
	return np;
}
void pdu_clear(Pdu *p) {
	free(p->body);
	memset(p,0,sizeof(Pdu));
}
void pdu_free(Pdu **p) {
	if (!p || !*p) return ;
	free((*p)->body);
	free(*p);
	*p = NULL;
}
int pdu_add_int(Pdu *p, SMPP_T_INTEGER i, int sz) {
	SMPP_T_BINARY tmp;

	if (!p) return FALSE;

	tmp = (SMPP_T_BINARY) realloc(p->body,(p->bsz + sz)*sizeof(SMPP_T_CHAR));
	if (!tmp) return FALSE;

	switch (sz) {
		case 4: tmp[p->bsz++] = (i & 0xFF000000) >> 24;
		case 3: tmp[p->bsz++] = (i & 0x00FF0000) >> 16;
		case 2: tmp[p->bsz++] = (i & 0x0000FF00) >> 8;
		case 1: tmp[p->bsz++] = (i & 0x000000FF);
	}

	p->body = tmp;
	return TRUE;
}
SMPP_T_INTEGER pdu_get_int(Pdu *p, int sz) {
	SMPP_T_INTEGER i = 0;

	if (!p) return 0;

	if (!p->upto) {
		if (!p->body) return 0;
		p->upto = p->body;
	}

	if (p->bsz - (p->upto - p->body) < sz) return FALSE;
	switch(sz) {
		case 4: i += *p->upto++ << 24;
		case 3: i += *p->upto++ << 16;
		case 2: i += *p->upto++ << 8;
		case 1: i += *p->upto++;
	}

	return i;
}
int pdu_add_string(Pdu *p, SMPP_T_STRING s, int max) {
	SMPP_T_BINARY tmp;
	int i;

	if (!p) return FALSE;
	
	i = strlen(s) + 1;
	if (i > max) i = max;

	tmp = (SMPP_T_BINARY) realloc(p->body,(p->bsz + i)*sizeof(SMPP_T_CHAR));
	if (!tmp) return FALSE;

	for(i = 1; i < max && *s; i++)
		tmp[p->bsz++] = *s++;
	tmp[p->bsz++] = '\0';

	p->body = tmp;
	return TRUE;
}
int pdu_get_string(Pdu *p, SMPP_T_STRING bf, int bfsz) {
	int i;

	if (!p) return FALSE;

	if (!p->upto) {
		if (!p->body) return FALSE;
		p->upto = p->body;
	}

	if (p->bsz - (p->upto - p->body) < bfsz) return FALSE;
	for(i = 0; i < bfsz; i++, p->upto++)
		bf[i] = *p->upto;
	/* ensure NULL on string - should already be there */
	bf[i-1] = '\0';
	return TRUE;
}
int pdu_add_octet(Pdu *p, SMPP_T_BINARY o, int sz) {
	SMPP_T_BINARY tmp;
	int i;

	if (!p) return FALSE;

	tmp = (SMPP_T_BINARY) realloc(p->body,(p->bsz + sz)*sizeof(SMPP_T_CHAR));
	if (!tmp) return FALSE;

	for(i = 0; i < sz; i++)
		tmp[p->bsz++] = *o++;
	
	p->body = tmp;
	return TRUE;
}
int pdu_get_octet(Pdu *p, SMPP_T_BINARY bf, int bfsz) {
	int i;

	if (!p) return FALSE;

	if (!p->upto) {
		if (!p->body) return FALSE;
		p->upto = p->body;
	}

	if (p->bsz - (p->upto - p->body) < bfsz) return FALSE;
	for(i = 0; i < bfsz; i++, p->upto++)
		bf[i] = *p->upto;

	return TRUE;
}

typedef struct smpp {
	SMPP_T_INTEGER last_error;
	SMPP_T_INTEGER sequence;	
	char server_id[16];
	PduList *plist;
	Chan *c;
} Smpp;

Smpp *smpp_new() {
	Smpp *s = calloc(1,sizeof(Smpp));
	s->last_error = SMPP_ESME_ROK;
	s->sequence = 1;
	tcp_new(&s->c);
	return s;
}
void smpp_free(Smpp **s) {
	if (!s || !*s) return;
	pdulist_free(&(*s)->plist);
	tcp_free(&(*s)->c);
	free(*s);
	*s = NULL;
}
int smpp_send_pdu(Smpp *s, Pdu *p, int timeout) {
	SMPP_T_CHAR header[16];
	char *raw;
	int x;

	if (!s || !p) return FALSE;

	x = 0;
	if (p->sequence == 0) {
		p->sequence = s->sequence++;
		if (s->sequence == 0x80000000)
			s->sequence = 1;
	}

	p->length = p->bsz + 16;
	
	setint(header,x,p->length);
	setint(header,x,p->cmd_id);
	setint(header,x,p->cmd_status);	
	setint(header,x,p->sequence);

	raw = calloc(p->length,sizeof(char));
	memcpy(raw,header,16*sizeof(SMPP_T_CHAR));
	memcpy(&raw[(16*sizeof(SMPP_T_CHAR))],p->body,p->bsz);

	if (timeout) {
		x = tcp_write_timed(s->c,raw,p->length,timeout);
		free(raw);
		if ((unsigned)x != p->length) return FALSE;
	}
	else {
		x = tcp_write(s->c,raw,p->length);
		free(raw);
		if ((unsigned)x != p->length) return FALSE;
	}
	return TRUE;
}
int smpp_recv_pdu(Smpp *s, Pdu *p, int timeout) {
	char bf[16];
	int r,x;
	
	if (!s || !p) return FALSE;

	if (timeout) r = tcp_read_timed(s->c,bf,16,timeout);
	else r = tcp_read(s->c,bf,16);
	if (r != 16) return FALSE;

	x = 0;
	getint(bf,x,p->length);
	getint(bf,x,p->cmd_id);
	getint(bf,x,p->cmd_status);
	getint(bf,x,p->sequence);
	
	if (p->length > 16) {
		p->bsz = p->length - 16;
		p->body = calloc(p->bsz,sizeof(SMPP_T_CHAR));	
	
		if (timeout) r = tcp_read_timed(s->c,p->body,p->bsz,timeout);
		else r = tcp_read(s->c,p->body,p->bsz);
		if (r != p->bsz) return FALSE;
	}

	return TRUE;
}
int smpp_connect(Smpp *s, char *ip, int port) {
	if (!s || !ip) return FALSE;
	if (!tcp_open(s->c,ip,port,NULL)) return FALSE;
	return TRUE;
}
int smpp_reciever(Smpp *s, char *id, char *pass, char *type, int addr_ton, int addr_npi, char *addr_range) {
	Pdu *p;

	if (!s || !id || !pass || !type || !addr_range) return FALSE;

	p = pdu_new(SMPP_CMD_BIND_RECEIVER,SMPP_ESME_ROK);
	pdu_add_string(p,id,16);
	pdu_add_string(p,pass,9);
	pdu_add_string(p,type,13);
	pdu_add_int(p,SMPP_VERSION_NUMBER,1);
	pdu_add_int(p,addr_ton,1);
	pdu_add_int(p,addr_npi,1);
	pdu_add_string(p,addr_range,41);
	if (!smpp_send_pdu(s,p,0)) {
		pdu_free(&p);
		return FALSE;
	}
	pdu_clear(p);

	while (smpp_recv_pdu(s,p,0)) {
		if (p->cmd_id == SMPP_CMD_BIND_RECEIVER_RESP) break;
		pdulist_add(&s->plist,p);
		pdu_clear(p);
	}
	if (p->cmd_status != SMPP_ESME_ROK) {pdu_free(&p); return FALSE;}
	pdu_get_string(p,s->server_id,16);
	pdu_free(&p);
	return TRUE;
}
int smpp_transmitter(Smpp *s, char *id, char *pass, char *type, int addr_ton, int addr_npi, char *addr_range) {
	Pdu *p;

	if (!s || !id || !pass || !type || !addr_range) return FALSE;

	p = pdu_new(SMPP_CMD_BIND_TRANSMITTER,SMPP_ESME_ROK);
	pdu_add_string(p,id,16);
	pdu_add_string(p,pass,9);
	pdu_add_string(p,type,13);
	pdu_add_int(p,SMPP_VERSION_NUMBER,1);
	pdu_add_int(p,addr_ton,1);
	pdu_add_int(p,addr_npi,1);
	pdu_add_string(p,addr_range,41);
	if (!smpp_send_pdu(s,p,0)) {
		pdu_free(&p);
		return FALSE;
	}
	pdu_clear(p);

	while (smpp_recv_pdu(s,p,0)) {
		if (p->cmd_id == SMPP_CMD_BIND_TRANSMITTER_RESP) break;
		pdulist_add(&s->plist,p);
		pdu_clear(p);
	}
	if (p->cmd_status != SMPP_ESME_ROK) {
		s->last_error = p->cmd_status;
		pdu_free(&p);
		return FALSE;
	}
	pdu_get_string(p,s->server_id,16);
	pdu_free(&p);
	return TRUE;
}
int smpp_unbind(Smpp *s) {
	Pdu *p;

	if (!s) return FALSE;
	p = pdu_new(SMPP_CMD_UNBIND,SMPP_ESME_ROK);
	if (!smpp_send_pdu(s,p,0)) {
		pdu_free(&p);
		return FALSE;
	}
	pdu_clear(p);
	while (smpp_recv_pdu(s,p,0)) {
		if (p->cmd_id == SMPP_CMD_UNBIND_RESP) break;
		pdulist_add(&s->plist,p);
		pdu_clear(p);
	}
	if (p->cmd_status != SMPP_ESME_ROK) {
		s->last_error = p->cmd_status;
		pdu_free(&p);
		return FALSE;
	}
	pdu_free(&p);
	return TRUE;
}
int smpp_send_msg(Smpp *s, char *srcPh, char *dstPh, char *msg) {
	Pdu *p = pdu_new(SMPP_CMD_SUBMIT_SM,SMPP_ESME_ROK);
	int sz;

	/* 4.4.1 */
	pdu_add_string(p,"",1);	/* Service Type */

	/* Source Address - Ton, Npi, Source */
	pdu_add_int(p,0,1);
	pdu_add_int(p,0,1);		
	pdu_add_string(p,srcPh,21);
	/* Destination Address - Ton, Npi, Source */
	pdu_add_int(p,0,1);
	pdu_add_int(p,0,1);
	pdu_add_string(p,dstPh,21);
	/* ESM CLASS - (5.2.12) - Default Message mode 
							- Default Message Type
							- No Specific Features slected on GSM Network */
	pdu_add_int(p,0,1);
	/* Protocol ID */
	pdu_add_int(p,0,1);
	/* Priority Flag - Non-Priority (GSM) */
	pdu_add_int(p,0,1);
	/* Schedule Delivery and validity period */
	pdu_add_string(p,"",1);
	pdu_add_string(p,"",1);
	/* Registered Delivery  -  No SMSC Delivery Receipt 
							-  No SME Ack */
	pdu_add_int(p,0,1);
	/* DON'T replace if present Flag */
	pdu_add_int(p,0,1);
	/* Data Coding - SMSC Default Alphabet */
	pdu_add_int(p,0,1);
	/* Default MSG ID - 0  ie send no default message */
	pdu_add_int(p,0,1);

	/* Length And Message */
	sz = strlen(msg);
	if (sz > 254) sz = 254;
	pdu_add_int(p,sz,1);
	pdu_add_octet(p,msg,sz);
	
	/* No Optionals */

	if (!smpp_send_pdu(s,p,0)) {
		pdu_free(&p);
		return FALSE;
	}
	pdu_clear(p);

	while (smpp_recv_pdu(s,p,0)) {
		if (p->cmd_id == SMPP_CMD_SUBMIT_SM_RESP) break;
		pdulist_add(&s->plist,p);
		pdu_clear(p);
	}
	if (p->cmd_status != SMPP_ESME_ROK) {
		s->last_error = p->cmd_status;
		pdu_free(&p);
		return FALSE;
	}
	pdu_free(&p);
	return TRUE;
}

char *smpp_error(int x) {
	if (x == SMPP_ESME_ROK)					return "No Error";
	if (x == SMPP_ESME_RINVMSGLEN)			return "Message Length is Invalid";
	if (x == SMPP_ESME_RINVCMDLEN)			return "Command Length is Invalid";
	if (x == SMPP_ESME_RINVCMDID)			return "Invalid Command ID";
	if (x == SMPP_ESME_RINVBNDSTS)			return "Incorrect BIND Status for given Command";
	if (x == SMPP_ESME_RALYBND)				return "ESME Already in Bound State";
	if (x == SMPP_ESME_RINVPRTFLG)			return "Invalid Priority Flag";
	if (x == SMPP_ESME_RINVREGDLVFLG)		return "Invalid Registered Delivery Flag";
	if (x == SMPP_ESME_RSYSERR)				return "System Error";
	if (x == SMPP_ESME_RINVSRCADR)			return "Invalid Source Address";
	if (x == SMPP_ESME_RINVDSTADR)			return "Invalid Dest Address";
	if (x == SMPP_ESME_RINVMSGID)			return "Message ID is invalid";
	if (x == SMPP_ESME_RBINDFAIL)			return "Bind Failed";
	if (x == SMPP_ESME_RINVPASWD)			return "Invalid Password";
	if (x == SMPP_ESME_RINVSYSID)			return "Invalid System ID";
	if (x == SMPP_ESME_RCANCELFAIL)			return "Cancel SM Failed";
	if (x == SMPP_ESME_RREPLACEFAIL)		return "Replace SM Failed";
	if (x == SMPP_ESME_RMSGQFUL)			return "Message Queue Full";
	if (x == SMPP_ESME_RINVSERTYP)			return "Invalid Service Type";
	if (x == SMPP_ESME_RINVNUMDESTS)		return "Invalid Number of Destinations";
	if (x == SMPP_ESME_RINVDLNAME)			return "Invalid Distribution List name";
	if (x == SMPP_ESME_RINVDESTFLAG)		return "Destination flag is invalid";
	if (x == SMPP_ESME_RINVSUBREP)			return "Inalid 'submit with replace' request";
	if (x == SMPP_ESME_RINVESMCLASS)		return "Invalid esm_class field data";
	if (x == SMPP_ESME_RCNTSUBDL)			return "Cannot Submit to Distribution List";
	if (x == SMPP_ESME_RSUBMITFAIL)			return "Submit Failed.";
	if (x == SMPP_ESME_RINVSRCTON)			return "Invalid Source Address TON";
	if (x == SMPP_ESME_RINVSRCNPI)			return "Invalid Source Address NPI";
	if (x == SMPP_ESME_RINVDSTTON)			return "Invalid Destination Address TON";
	if (x == SMPP_ESME_RINVDSTNPI)			return "Invalid Destination Address NPI";
	if (x == SMPP_ESME_RINVSYSTYP)			return "Invalid system_type field";
	if (x == SMPP_ESME_RINVREPFLAG)			return "Invalid replace_if_present flag";
	if (x == SMPP_ESME_RINVNUMMSGS)			return "Invalid number of messages";
	if (x == SMPP_ESME_RTHROTTLED)			return "Throttling error";
	if (x == SMPP_ESME_RINVSCHED)			return "Invalid Scheduled Delivery Time";
	if (x == SMPP_ESME_RINVEXPIRY)			return "Invalid message validity period";
	if (x == SMPP_ESME_RINVDFTMSGID)		return "Predefined Message Invalid or Not Found";
	if (x == SMPP_ESME_RX_T_APPN)			return "ESME Receiver Temporary App Error Code";
	if (x == SMPP_ESME_RX_P_APPN)			return "ESME Receiver Permanent App Error Code";
	if (x == SMPP_ESME_RX_R_APPN)			return "ESME Receiver Reject Message Error Code";
	if (x == SMPP_ESME_RQUERYFAIL)			return "query_sm request failed";
	if (x == SMPP_ESME_RINVOPTPARSTREAM)	return "Error in the optional part of the PDU Body";
	if (x == SMPP_ESME_ROPTPARNOTALLWD)		return "Optional Parameter not allowed";
	if (x == SMPP_ESME_RINVPARLEN)			return "Invalid Parameter Length";
	if (x == SMPP_ESME_RMISSINGOPTPARAM)	return "Expected Optional Parameter missing";
	if (x == SMPP_ESME_RINVOPTPARAMVAL)		return "Invalid Optional Parameter Value";
	if (x == SMPP_ESME_RDELIVERYFAILURE)	return "Delivery Failure";
	if (x == SMPP_ESME_RUNKNOWNERR)			return "Unknown Error";
	return "Failed to identify error.";
}

struct sms_smpp {
	Smpp *transmitter;
	Smpp *reciever;
};

struct smpp_message {
	char *to,*from,*body;
	Pdu *pdu;
};

/* should NOT return NULL as struct is needed in order to find out what errcode means */
SmsSmpp *sms_smpp_init(Sms *sms, SmppInit *init, int *errcode) {
	SmsSmpp *smpp = calloc(1,sizeof(SmsSmpp));

	smpp->transmitter = smpp_new();
	if (!smpp_connect(smpp->transmitter,init->host,init->port)) {
		*errcode = SMS_SMPP_CONNECT_ERROR;
		return smpp;
	}
	if (!smpp_transmitter(smpp->transmitter,(init->id)?init->id:"",(init->pass)?init->pass:"",init->type,init->addr_ton,init->addr_npi,(init->addr_range)?init->addr_range:"*")) {
		*errcode = SMS_SMPP_BIND_TRANSMITTER_ERROR;
		return smpp;
	}

	smpp->reciever = smpp_new();
	if (!smpp_connect(smpp->reciever,init->host,init->port)) {
		*errcode = SMS_SMPP_CONNECT_ERROR;
		return smpp;
	}
	if (!smpp_reciever(smpp->reciever,(init->id)?init->id:"",(init->pass)?init->pass:"",init->type,init->addr_ton,init->addr_npi,(init->addr_range)?init->addr_range:"*")) {
		*errcode = SMS_SMPP_CONNECT_ERROR;
		return smpp;		
	}
	
	return smpp;
}
void sms_smpp_free(SmsSmpp **smpp) {
	if (smpp && *smpp) {
		/* free struct stuff here */
		smpp_unbind((*smpp)->transmitter);
		smpp_free(&(*smpp)->transmitter);

		smpp_unbind((*smpp)->reciever);
		smpp_free(&(*smpp)->reciever);

		/* */
		free(*smpp);
		*smpp = NULL;
	}
}
int sms_smpp_handler(Smpp *s, Pdu *p, int *errcode) {
	Pdu *rp;

	switch (p->cmd_id) {
	default:
		imsg("Unknown PDU command %d(0x%08x)",p->cmd_id,p->cmd_id);
		break;
	case SMPP_CMD_ALERT_NOTIFICATION:
		break;
	case SMPP_CMD_ENQUIRE_LINK:
		rp =  pdu_new(SMPP_CMD_ENQUIRE_LINK_RESP,SMPP_ESME_ROK);
		if (!smpp_send_pdu(s,rp,0)) {
			emsg("Failed to send ENQUIRE_LINK_RESP");
		}
		pdu_free(&rp);
		break;
	};
	return TRUE;
}
SmppMessage *sms_smpp_wait(SmsSmpp *smpp, long timeout_msec, int *errcode) {
	SmppMessage *msg = NULL;
	PduList *pl,*pprev,*pnext;
	Pdu p;

	/* check transmitter for pdu's */
	
	/* read for one */
	memset(&p,0,sizeof(Pdu));
	if (smpp_recv_pdu(smpp->transmitter,&p,1)) 
		pdulist_add(&smpp->transmitter->plist,&p);
	
	/* check list */
	for(pprev = NULL, pl = smpp->transmitter->plist; pl; pprev = pl, pl = pnext) {
		pnext = pl->next;
		if (!sms_smpp_handler(smpp->reciever,pl->p,errcode)) return NULL;
		if (!pprev) smpp->reciever->plist = pl->next;
		else pprev->next = pl->next;
		free(pl);
	}

	/* check in list for one */
	for(pprev = NULL, pl = smpp->reciever->plist; pl; pprev = pl, pl = pnext) {
		pnext = pl->next;

		/* handle misc pdu's */
		if (pl->p->cmd_id != SMPP_CMD_DELIVER_SM && !sms_smpp_handler(smpp->reciever,pl->p,errcode)) {
			return NULL;
		}
		else {
			msg = calloc(1,sizeof(SmppMessage));
			msg->pdu = pl->p;
		}
		
		if (!pprev) smpp->reciever->plist = pl->next;
		else pprev->next = pl->next;
		free(pl);

		if (msg) return msg;
	}
		
	/* read for one */
	memset(&p,0,sizeof(Pdu));
	if (!smpp_recv_pdu(smpp->reciever,&p,timeout_msec)) return NULL;
	if (p.cmd_id != SMPP_CMD_DELIVER_SM) {
		pdulist_add(&smpp->reciever->plist,&p);
		return NULL;
	}
	msg = calloc(1,sizeof(SmppMessage));
	msg->pdu = pdu_dup(&p);
	return msg;
}
int sms_smpp_send(SmsSmpp *smpp, void *data, char *from, char *to, char *message) {
	if (!smpp_send_msg(smpp->transmitter,from,to,message)) {
		return smpp->transmitter->last_error;
	}
	return SMS_NO_ERROR;
}
int sms_smpp_recv(SmsSmpp *smpp, SmppMessage *msg) {
	/* Nothing to do here */
	return SMS_NO_ERROR;
}
int sms_smpp_accept(SmsSmpp *smpp, SmppMessage *msg, int code, char *reason) {
	Pdu *p = pdu_new(SMPP_CMD_DELIVER_SM_RESP,SMPP_ESME_ROK);
	
	pdu_add_string(p,"",1);
	if (!smpp_send_pdu(smpp->reciever,p,0)) {
		pdu_free(&p);
		return SMS_SMPP_SEND_ERROR;		
	}
	pdu_free(&p);
	return SMS_NO_ERROR;
}
int sms_smpp_reject(SmsSmpp *smpp, SmppMessage *msg, int code, char *reason) {
	Pdu *p = pdu_new(SMPP_CMD_DELIVER_SM_RESP,code);
	
	pdu_add_string(p,"",1);
	if (!smpp_send_pdu(smpp->reciever,p,0)) {
		pdu_free(&p);
		return SMS_SMPP_SEND_ERROR;		
	}
	pdu_free(&p);
	return SMS_NO_ERROR;
}
char *sms_smpp_error(SmsSmpp *smpp, int errcode, char *bf, int bfsz) {
	if (errcode == SMS_SMPP_CONNECT_ERROR) ncpy(bf,"Failed to connect to smsc",bfsz-1);
	else if (errcode == SMS_SMPP_BIND_TRANSMITTER_ERROR) ncpy(bf,"Failed to bind transmitter",bfsz-1);
	else if (errcode == SMS_SMPP_BIND_RECEIVER_ERROR) ncpy(bf,"Failed to bind receiver",bfsz-1);
	else ncpy(bf,smpp_error(errcode),bfsz-1);
	return bf;
}
char *sms_smpp_fromip(SmsSmpp *smpp, SmppMessage *msg) {
	return "";
}
char *sms_smpp_from(SmsSmpp *smpp, SmppMessage *msg) {
	if (!msg->from) {
		char bf[21];

		pdu_get_string(msg->pdu,bf,6);
		pdu_get_int(msg->pdu,1);
		pdu_get_int(msg->pdu,1);
		pdu_get_string(msg->pdu,bf,21);
		msg->from = strdup(bf);
	}
	return msg->from;
}
char *sms_smpp_to(SmsSmpp *smpp, SmppMessage *msg) {
	if (!msg->from) sms_smpp_from(smpp,msg);
	if (!msg->to) {
		char bf[21];

		pdu_get_int(msg->pdu,1);
		pdu_get_int(msg->pdu,1);
		pdu_get_string(msg->pdu,bf,21);
		msg->to = strdup(bf);
	}	
	return msg->to;
}
char *sms_smpp_body(SmsSmpp *smpp, SmppMessage *msg) {
	if (!msg->from) sms_smpp_from(smpp,msg);
	if (!msg->to) sms_smpp_to(smpp,msg);
	if (!msg->body) {
		char bf[254];

		pdu_get_int(msg->pdu,1);
		pdu_get_int(msg->pdu,1);
		pdu_get_int(msg->pdu,1);
		pdu_get_string(msg->pdu,bf,1);
		pdu_get_string(msg->pdu,bf,1);
		pdu_get_int(msg->pdu,1);
		pdu_get_int(msg->pdu,1);
		pdu_get_int(msg->pdu,1);
		pdu_get_int(msg->pdu,1);
		pdu_get_int(msg->pdu,1);
		pdu_get_string(msg->pdu,bf,254);
		msg->body = strdup(bf);
	}
	return msg->body;
}
void sms_smpp_msgfree(SmsSmpp *smpp, SmppMessage **msg) {
	if (smpp && msg && *msg) {
		pdu_free(&(*msg)->pdu);
		free((*msg)->body);
		free((*msg)->from);
		free((*msg)->to);
		free(*msg);
		*msg = NULL;
	}
}
