#pragma module ESME_API "SMPP/ESME_API-1-X" #define __MODULE__ "ESME_API" /* **++ ** FACILITY: SMPP/ESME Aplication Programming Interface ** ** MODULE DESCRIPTION: ** ** This module contains set of routines to performs basic SMPP/ESME related operations, ** these operations closed by: ** ESME_API_INIT - The API initialization ** ESME_API_SHUT - The API cleanup operation ** ESME_API_TX - Build a SMPP PDU and send it to the SMSC ** ESME_API_RX - Receive a SMPP PDU from SMSC ** ** This API is uses TCP/IP to communicating with the SMSC. ** ** AUTHORS: ** ** Ruslan R. Laishev ** ** CREATION DATE: 27-SEP-2002 ** ** DESIGN ISSUES: ** ** {@tbs@} ** ** [@optional module tags@]... ** ** MODIFICATION HISTORY: ** ** 31-OCT-2002 RRL Replaced all ILE2 structure with ILE3. ** 19-SEP-2003 RRL Fixed SMPP_CMD$K_DELIVER_SM set of parameters, ** fixed ACCVIO in the getarg_pdu. ** Added sequence and status in the esme_api_tx(). ** 25-AUG-2004 RRL Fixed Octet String processing in the getarg_pdu() ** due signed byte to unsigned byte/word conversion. ** Corrected length of the SMPP_PTAG$K_SMSBODY from 256 to 254 bytes. ** 6-NOV-2004 RRL Some code reorganazing. ** 8-FEB-2008 RRL ACCVIO in the putarg2pdu(): added some sanity check of input arguments. ** 2-APR-2008 RRL Changed netio_read () to use IO$M_LOCKBUF. ** 22-MAY-2009 RRL Set Keep Alive option for outgoing connection. ** 6-SEP-2011 RRL Using 'message_payload' field (SMPP_PTAG$K_MSGPAYLD) instead of ** 'short_message' (SMPP_PTAG$K_SMSBODY). ** 14-SEP-2011 RRL Resore SMPP_PTAG$K_SMSBODY in the DELIVER_SM. ** ** {@tbs@}... **-- */ /* ** ** INCLUDE FILES ** */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define __NEW_STARLET 1 #include #undef __NEW_STARLET #include "macros.h" /* ** ** SMPP AND ESME API INCLUDE FILES ** */ #include "smppdef.h" #include "esme_msg.h" /* ** */ $DESCRIPTOR(dsc_ucxdev, "UCX$DEVICE"); $DESCRIPTOR(dsc_tcpipdev, "TCPIP$DEVICE"); const int iotmo [ 2 ] = { -450000000, -1 }, /* delta time = 45 sec */ one = 1, debug_flag = 0; /* ** ** SMPP PARAMETERS DEFINITIONS TABLE, FIELDS: ** PARAMETER TAG, TYPE, MAXIMAL SIZE, DEFAULT VALUE, LENGTH OF DEFAULT VALUE ** ** MANDATORY PARAMETERS AND DEFAULT VALUES SECTION */ int SMPP_TBL_FLAG = 0,SMPP_TBL_SEM = 0; struct smpp_par smpp_par_tbl [] ={ {SMPP_PTAG$K_SID, SMPP_DTYPE$K_ASCIZ, 16, {"API",3}}, {SMPP_PTAG$K_PWD, SMPP_DTYPE$K_ASCIZ, 9, {"",0}}, {SMPP_PTAG$K_STYPE, SMPP_DTYPE$K_ASCIZ, 13, {"esme",4}}, {SMPP_PTAG$K_VER, SMPP_DTYPE$K_INT, 1, {SMPP$K_VERSION,1}}, {SMPP_PTAG$K_TON, SMPP_DTYPE$K_INT, 1, {SMPP_TON$K_UNKNW,1}}, {SMPP_PTAG$K_NPI, SMPP_DTYPE$K_INT, 1, {SMPP_NPI$K_UNKNW,1}}, {SMPP_PTAG$K_RANGE, SMPP_DTYPE$K_ASCIZ, 41, {"",0}}, {SMPP_PTAG$K_MSGID, SMPP_DTYPE$K_ASCIZ, 65, {"",0}}, {SMPP_PTAG$K_SRCTON, SMPP_DTYPE$K_INT, 1, {SMPP_TON$K_UNKNW,1}}, {SMPP_PTAG$K_SRCNPI, SMPP_DTYPE$K_INT, 1, {SMPP_NPI$K_UNKNW,1}}, {SMPP_PTAG$K_SRC, SMPP_DTYPE$K_ASCIZ, 21, {"",0}}, {SMPP_PTAG$K_DESTTON, SMPP_DTYPE$K_INT, 1, {SMPP_TON$K_UNKNW,1}}, {SMPP_PTAG$K_DESTNPI, SMPP_DTYPE$K_INT, 1, {SMPP_NPI$K_UNKNW,1}}, {SMPP_PTAG$K_DEST, SMPP_DTYPE$K_ASCIZ, 21, {"",0}}, {SMPP_PTAG$K_ESM_CLASS, SMPP_DTYPE$K_INT, 1, {0,1}}, {SMPP_PTAG$K_REG_DLV, SMPP_DTYPE$K_INT, 1, {0,1}}, {SMPP_PTAG$K_DC, SMPP_DTYPE$K_INT, 1, {0,1}}, {SMPP_PTAG$K_REPLIF, SMPP_DTYPE$K_INT, 1, {0,1}}, {SMPP_PTAG$K_FINDATE, SMPP_DTYPE$K_ASCIZ, 17, {"",0}}, {SMPP_PTAG$K_ERRCODE, SMPP_DTYPE$K_INT, 1}, {SMPP_PTAG$K_SCHEDTM, SMPP_DTYPE$K_ASCIZ, 17, {"",0}}, {SMPP_PTAG$K_PERIOD, SMPP_DTYPE$K_ASCIZ, 17, {"",0}}, {SMPP_PTAG$K_DEF_MSGID, SMPP_DTYPE$K_ASCIZ, 65, {"",0}}, {SMPP_PTAG$K_SMSLEN, SMPP_DTYPE$K_INT, 1, {0,1}}, /* ** ** Note: The message data should be inserted in either the 'short_message' (SMPP_PTAG$K_SMSBODY) ** or the 'message_payload' (SMPP_PTAG$K_MSGPAYLD) parameters. ** Both parameters must not be used simultaneously. ** */ {SMPP_PTAG$K_SMSBODY, SMPP_DTYPE$K_OCTETS, 254, {"",0}}, {SMPP_PTAG$K_PROTO, SMPP_DTYPE$K_INT, 1, {0,1}}, {SMPP_PTAG$K_PRIO, SMPP_DTYPE$K_INT, 1, {0,1}}, /* ** OPTIONAL PARAMETERS SECTION */ {SMPP_PTAG$K_DADDR_SU, SMPP_DTYPE$K_INT, 1}, {SMPP_PTAG$K_DADDR_NT, SMPP_DTYPE$K_INT, 1}, {SMPP_PTAG$K_DEST_BT, SMPP_DTYPE$K_INT, 1}, {SMPP_PTAG$K_DEST_TID, SMPP_DTYPE$K_INT, 2}, {SMPP_PTAG$K_SADDR_SU, SMPP_DTYPE$K_INT, 1}, {SMPP_PTAG$K_SADDR_NT, SMPP_DTYPE$K_INT, 1}, {SMPP_PTAG$K_SRC_BT, SMPP_DTYPE$K_INT, 1}, {SMPP_PTAG$K_SRC_TID, SMPP_DTYPE$K_INT, 2}, {SMPP_PTAG$K_QOS_TTL, SMPP_DTYPE$K_INT, 4}, {SMPP_PTAG$K_PAYLD_T, SMPP_DTYPE$K_INT, 1}, {SMPP_PTAG$K_ADDSTS, SMPP_DTYPE$K_ASCIZ, 256}, {SMPP_PTAG$K_RMSGID, SMPP_DTYPE$K_ASCIZ, 65}, {SMPP_PTAG$K_MMSGWFAC, SMPP_DTYPE$K_INT, 1}, {SMPP_PTAG$K_PRIVIND, SMPP_DTYPE$K_INT, 1}, {SMPP_PTAG$K_SSUBADDR, SMPP_DTYPE$K_OCTETS, 23}, {SMPP_PTAG$K_DSUBADDR, SMPP_DTYPE$K_OCTETS, 23}, {SMPP_PTAG$K_UMSGREF, SMPP_DTYPE$K_INT, 2}, {SMPP_PTAG$K_URESPCODE, SMPP_DTYPE$K_INT, 1}, {SMPP_PTAG$K_SRCPORT, SMPP_DTYPE$K_INT, 2}, {SMPP_PTAG$K_DESTPORT, SMPP_DTYPE$K_INT, 2}, {SMPP_PTAG$K_SARMSGREF, SMPP_DTYPE$K_INT, 2}, {SMPP_PTAG$K_LNGIND, SMPP_DTYPE$K_INT, 1}, {SMPP_PTAG$K_SARSEGS, SMPP_DTYPE$K_INT, 1}, {SMPP_PTAG$K_SARSEQ, SMPP_DTYPE$K_INT, 1}, {SMPP_PTAG$K_SCINTFVER, SMPP_DTYPE$K_INT, 1}, {SMPP_PTAG$K_CBNPIND, SMPP_DTYPE$K_INT, 1}, {SMPP_PTAG$K_CBATAG, SMPP_DTYPE$K_OCTETS, 65}, {SMPP_PTAG$K_NUMMSGS, SMPP_DTYPE$K_INT, 1}, {SMPP_PTAG$K_CBNUM, SMPP_DTYPE$K_OCTETS, 19}, {SMPP_PTAG$K_DPFRES, SMPP_DTYPE$K_INT, 1}, {SMPP_PTAG$K_SETDPF, SMPP_DTYPE$K_INT, 1}, {SMPP_PTAG$K_MSAVAIL, SMPP_DTYPE$K_INT, 1}, {SMPP_PTAG$K_NETERR, SMPP_DTYPE$K_OCTETS, 3}, /* ** ** Note: The short message data should be inserted in either ** the 'short_message' or 'message_payload' fields. Both ** fields should not be used simultaneously. The 'sm_length' field should be ** set to zero if using the message_payload parameter. */ {SMPP_PTAG$K_MSGPAYLD, SMPP_DTYPE$K_OCTETS, -1}, {SMPP_PTAG$K_DELYVRES, SMPP_DTYPE$K_INT, 1}, {SMPP_PTAG$K_MOREMSGS, SMPP_DTYPE$K_INT, 1}, {SMPP_PTAG$K_MSGSTATE, SMPP_DTYPE$K_INT, 1}, {SMPP_PTAG$K_USSDSOP, SMPP_DTYPE$K_OCTETS, 1}, {SMPP_PTAG$K_DISPTM, SMPP_DTYPE$K_INT, 1}, {SMPP_PTAG$K_SMSSIG, SMPP_DTYPE$K_INT, 2}, {SMPP_PTAG$K_MSVALID, SMPP_DTYPE$K_INT, 1}, {SMPP_PTAG$K_ALERT, SMPP_DTYPE$K_INT, 0}, {SMPP_PTAG$K_ITSREPLYT, SMPP_DTYPE$K_INT, 1}, {SMPP_PTAG$K_ITSSESSINF,SMPP_DTYPE$K_OCTETS, 2}, {SMPP_PTAG$K_OPTIONAL, SMPP_DTYPE$K_OCTETS, 0} }; const int smpp_par_sz = sizeof(smpp_par_tbl)/sizeof(smpp_par_tbl[0]); /* ** COMMAND & MANDATORY PARAMETERS DESCRIPTON TABLE */ struct smpp_cmd smpp_cmd_tbl [] ={ {SMPP_CMD$K_BIND_TRANSMITTER,{ {SMPP_PTAG$K_SID}, {SMPP_PTAG$K_PWD}, {SMPP_PTAG$K_STYPE}, {SMPP_PTAG$K_VER}, {SMPP_PTAG$K_TON}, {SMPP_PTAG$K_NPI}, {SMPP_PTAG$K_RANGE}}}, {SMPP_CMD$K_BIND_TRANSMITTER_RESP,{ {SMPP_PTAG$K_SID}, {SMPP_PTAG$K_OPTIONAL}}}, {SMPP_CMD$K_BIND_RECEIVER,{ {SMPP_PTAG$K_SID}, {SMPP_PTAG$K_PWD}, {SMPP_PTAG$K_STYPE}, {SMPP_PTAG$K_VER}, {SMPP_PTAG$K_TON}, {SMPP_PTAG$K_NPI}, {SMPP_PTAG$K_RANGE}}}, {SMPP_CMD$K_BIND_RECEIVER_RESP,{ {SMPP_PTAG$K_SID}, {SMPP_PTAG$K_OPTIONAL}}}, {SMPP_CMD$K_BIND_TRANSCEIVER,{ {SMPP_PTAG$K_SID}, {SMPP_PTAG$K_PWD}, {SMPP_PTAG$K_STYPE}, {SMPP_PTAG$K_VER}, {SMPP_PTAG$K_TON}, {SMPP_PTAG$K_NPI}, {SMPP_PTAG$K_RANGE}}}, {SMPP_CMD$K_BIND_TRANSCEIVER_RESP,{ {SMPP_PTAG$K_SID}, {SMPP_PTAG$K_OPTIONAL}}}, {SMPP_CMD$K_OUTBIND,{ {SMPP_PTAG$K_SID}, {SMPP_PTAG$K_PWD}}}, {SMPP_CMD$K_UNBIND}, {SMPP_CMD$K_UNBIND_RESP}, {SMPP_CMD$K_GENERIC_NACK}, {SMPP_CMD$K_SUBMIT_SM,{ {SMPP_PTAG$K_STYPE}, {SMPP_PTAG$K_SRCTON},{SMPP_PTAG$K_SRCNPI},{SMPP_PTAG$K_SRC}, {SMPP_PTAG$K_DESTTON},{SMPP_PTAG$K_DESTNPI},{SMPP_PTAG$K_DEST}, {SMPP_PTAG$K_ESM_CLASS}, {SMPP_PTAG$K_PROTO}, {SMPP_PTAG$K_PRIO}, {SMPP_PTAG$K_SCHEDTM}, {SMPP_PTAG$K_PERIOD}, {SMPP_PTAG$K_REG_DLV}, {SMPP_PTAG$K_REPLIF}, {SMPP_PTAG$K_DC}, {SMPP_PTAG$K_DEF_MSGID}, {SMPP_PTAG$K_SMSLEN}, // {SMPP_PTAG$K_SMSBODY}, {SMPP_PTAG$K_MSGPAYLD}, {SMPP_PTAG$K_OPTIONAL}}}, {SMPP_CMD$K_SUBMIT_SM_RESP,{ {SMPP_PTAG$K_MSGID}}}, {SMPP_CMD$K_SUBMIT_MULTI}, {SMPP_CMD$K_SUBMIT_MULTI_RESP}, {SMPP_CMD$K_DELIVER_SM,{ {SMPP_PTAG$K_STYPE}, {SMPP_PTAG$K_SRCTON},{SMPP_PTAG$K_SRCNPI},{SMPP_PTAG$K_SRC}, {SMPP_PTAG$K_DESTTON},{SMPP_PTAG$K_DESTNPI},{SMPP_PTAG$K_DEST}, {SMPP_PTAG$K_ESM_CLASS}, {SMPP_PTAG$K_PROTO}, {SMPP_PTAG$K_PRIO}, {SMPP_PTAG$K_SCHEDTM}, {SMPP_PTAG$K_PERIOD}, {SMPP_PTAG$K_REG_DLV}, {SMPP_PTAG$K_REPLIF}, {SMPP_PTAG$K_DC}, {SMPP_PTAG$K_DEF_MSGID}, {SMPP_PTAG$K_SMSLEN}, {SMPP_PTAG$K_SMSBODY}, {SMPP_PTAG$K_OPTIONAL}}}, {SMPP_CMD$K_DELIVER_SM_RESP,{ {SMPP_PTAG$K_MSGID}}}, {SMPP_CMD$K_DATA_SM,{ {SMPP_PTAG$K_STYPE}, {SMPP_PTAG$K_SRCTON},{SMPP_PTAG$K_SRCNPI},{SMPP_PTAG$K_SRC}, {SMPP_PTAG$K_DESTTON},{SMPP_PTAG$K_DESTNPI},{SMPP_PTAG$K_DEST}, {SMPP_PTAG$K_ESM_CLASS}, {SMPP_PTAG$K_REG_DLV}, {SMPP_PTAG$K_DC}, {SMPP_PTAG$K_OPTIONAL}}}, {SMPP_CMD$K_DATA_SM_RESP,{ {SMPP_PTAG$K_MSGID}, {SMPP_PTAG$K_OPTIONAL}}}, {SMPP_CMD$K_QUERY_SM,{ {SMPP_PTAG$K_MSGID}, {SMPP_PTAG$K_SRCTON},{SMPP_PTAG$K_SRCNPI},{SMPP_PTAG$K_SRC}}}, {SMPP_CMD$K_QUERY_SM_RESP,{ {SMPP_PTAG$K_MSGID}, {SMPP_PTAG$K_FINDATE}, {SMPP_PTAG$K_MSGSTATE}, {SMPP_PTAG$K_ERRCODE}}}, {SMPP_CMD$K_CANCEL_SM,{ {SMPP_PTAG$K_STYPE}, {SMPP_PTAG$K_MSGID}, {SMPP_PTAG$K_SRCTON},{SMPP_PTAG$K_SRCNPI},{SMPP_PTAG$K_SRC}, {SMPP_PTAG$K_DESTTON},{SMPP_PTAG$K_DESTNPI},{SMPP_PTAG$K_DEST}}}, {SMPP_CMD$K_CANCEL_SM_RESP}, {SMPP_CMD$K_REPLACE_SM,{ {SMPP_PTAG$K_MSGID}, {SMPP_PTAG$K_SRCTON},{SMPP_PTAG$K_SRCNPI},{SMPP_PTAG$K_SRC}, {SMPP_PTAG$K_SCHEDTM}, {SMPP_PTAG$K_REG_DLV}, {SMPP_PTAG$K_DEF_MSGID}, {SMPP_PTAG$K_SMSLEN}, // {SMPP_PTAG$K_SMSBODY}}}, {SMPP_PTAG$K_MSGPAYLD}}}, {SMPP_CMD$K_REPLACE_SM_RESP}, {SMPP_CMD$K_ENQUIRE_LINK}, {SMPP_CMD$K_ENQUIRE_LINK_RESP}, {SMPP_CMD$K_ALERT_NOTIFICATION,{ {SMPP_PTAG$K_SRCTON},{SMPP_PTAG$K_SRCNPI},{SMPP_PTAG$K_SRC}, {SMPP_PTAG$K_DESTTON},{SMPP_PTAG$K_DESTNPI},{SMPP_PTAG$K_DEST}, {SMPP_PTAG$K_OPTIONAL}}}}; const int smpp_cmd_sz = sizeof(smpp_cmd_tbl)/sizeof(smpp_cmd_tbl[0]); struct err2cond_tbl { int error, condition; } err2cond_tbl [] ={ {SMPP_STS$K_ESME_ROK, ESME_ROK}, {SMPP_STS$K_ESME_RINVMSGLEN, ESME_RINVMSGLEN}, {SMPP_STS$K_ESME_RINVCMDLEN, ESME_RINVCMDLEN}, {SMPP_STS$K_ESME_RINVCMDID, ESME_RINVCMDID}, {SMPP_STS$K_ESME_RINVBNDSTS, ESME_RINVBNDSTS}, {SMPP_STS$K_ESME_RALYBND, ESME_RALYBND}, {SMPP_STS$K_ESME_RINVPRTFLG, ESME_RINVPRTFLG}, {SMPP_STS$K_ESME_RINVREGDLVFLG, ESME_RINVREGDLVFLG}, {SMPP_STS$K_ESME_RSYSERR, ESME_RSYSERR}, {SMPP_STS$K_ESME_RINVSRCADR, ESME_RINVSRCADR}, {SMPP_STS$K_ESME_RINVDSTADR, ESME_RINVDSTADR}, {SMPP_STS$K_ESME_RINVMSGID, ESME_RINVMSGID}, {SMPP_STS$K_ESME_RBINDFAIL, ESME_RBINDFAIL}, {SMPP_STS$K_ESME_RINVPASWD, ESME_RINVPASWD}, {SMPP_STS$K_ESME_RINVSYSID, ESME_RINVSYSID}, {SMPP_STS$K_ESME_RCANCELFAIL, ESME_RCANCELFAIL}, {SMPP_STS$K_ESME_RREPLACEFAIL, ESME_RREPLACEFAIL}, {SMPP_STS$K_ESME_RMSGQFUL, ESME_RMSGQFUL}, {SMPP_STS$K_ESME_RINVSERTYP, ESME_RINVSERTYP}, {SMPP_STS$K_ESME_RINVNUMDESTS, ESME_RINVNUMDESTS}, {SMPP_STS$K_ESME_RINVDLNAME, ESME_RINVDLNAME}, {SMPP_STS$K_ESME_RINVDESTFLAG, ESME_RINVDESTFLAG}, {SMPP_STS$K_ESME_RINVSUBREP, ESME_RINVSUBREP}, {SMPP_STS$K_ESME_RINVESMCLASS, ESME_RINVESMCLASS}, {SMPP_STS$K_ESME_RCNTSUBDL, ESME_RCNTSUBDL}, {SMPP_STS$K_ESME_RSUBMITFAIL, ESME_RSUBMITFAIL}, {SMPP_STS$K_ESME_RINVSRCTON, ESME_RINVSRCTON}, {SMPP_STS$K_ESME_RINVSRCNPI, ESME_RINVSRCNPI}, {SMPP_STS$K_ESME_RINVDSTTON, ESME_RINVDSTTON}, {SMPP_STS$K_ESME_RINVDSTNPI, ESME_RINVDSTNPI}, {SMPP_STS$K_ESME_RINVSYSTYP, ESME_RINVSYSTYP}, {SMPP_STS$K_ESME_RINVREPFLAG, ESME_RINVREPFLAG}, {SMPP_STS$K_ESME_RINVNUMMSGS, ESME_RINVNUMMSGS}, {SMPP_STS$K_ESME_RTHROTTLED, ESME_RTHROTTLED}, {SMPP_STS$K_ESME_RINVSCHED, ESME_RINVSCHED}, {SMPP_STS$K_ESME_RINVEXPIRY, ESME_RINVEXPIRY}, {SMPP_STS$K_ESME_RINVSCHED, ESME_RINVSCHED}, {SMPP_STS$K_ESME_RINVEXPIRY, ESME_RINVEXPIRY}, {SMPP_STS$K_ESME_RINVDFTMSGID, ESME_RINVDFTMSGID}, {SMPP_STS$K_ESME_RX_T_APPN, ESME_RX_T_APPN}, {SMPP_STS$K_ESME_RX_P_APPN, ESME_RX_P_APPN}, {SMPP_STS$K_ESME_RX_R_APPN, ESME_RX_R_APPN}, {SMPP_STS$K_ESME_RQUERYFAIL, ESME_RQUERYFAIL}, {SMPP_STS$K_ESME_RINVOPTPARSTREAM, ESME_RINVOPTPARSTREAM}, {SMPP_STS$K_ESME_ROPTPARNOTALLWD, ESME_ROPTPARNOTALLWD}, {SMPP_STS$K_ESME_RINVPARLEN, ESME_RINVPARLEN}, {SMPP_STS$K_ESME_RMISSINGOPTPARAM, ESME_RMISSINGOPTPARAM}, {SMPP_STS$K_ESME_RINVOPTPARAMVAL, ESME_RINVOPTPARAMVAL}, {SMPP_STS$K_ESME_RDELIVERYFAILURE, ESME_RDELIVERYFAILURE}, {SMPP_STS$K_ESME_RUNKNOWNERR, ESME_RUNKNOWNERR} }; static void _trace ( unsigned char * module, unsigned char * func, unsigned line, unsigned char * fao, ... ) { int status, idx = 0; va_list ap; char buf[512] = {"!%T [!AZ\\!AZ\\!UL] "},out[4096]; struct dsc$descriptor buf_dsc,fao_dsc; int argc = 0, argl[32] = {0}; /* ** Form FAO string */ strcat(buf,fao); status = strnlen(buf,sizeof(buf)); INIT_SDESC(fao_dsc,status,buf); /* ** Reorganize parameters list for $FAOL */ va_start(ap,fao); argl[idx++] = 0; argl[idx++] = module; argl[idx++] = func; argl[idx++] = line; for (va_count(argc); idx < argc; idx++) argl[idx] = va_arg(ap,unsigned); va_end((void *) ap); /* ** Format a message, put it to SYS$OUTPUT */ INIT_SDESC(buf_dsc, sizeof(out),out); if ( !(1 & (status = sys$faol(&fao_dsc,&buf_dsc.dsc$w_length,&buf_dsc,argl))) ) lib$signal(status); lib$put_output(&buf_dsc); } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Canceling all queued and processed request due of expiration timer. ** ** FORMAL PARAMETERS: ** ** reqidt: pointer to a network channel ** ** RETURN VALUE: ** ** None ** ** SIDE EFFECTS: ** ** Cancel _all_ request for the given I/O channel. **-- */ static void timer_ast ( short *reqidt ) { sys$cantim(reqidt,0); sys$cancel(*reqidt); } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Close (deassign) a given channel to a network device. ** ** FORMAL PARAMETERS: ** ** chan: channel number, longword, by value ** ** RETURN VALUE: ** ** VMS condition code ** **-- */ static int netio_disc ( short chan ) { sys$cancel (chan); return sys$dassgn (chan); } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Open a TCP-connection to given address and port. ** ** FORMAL PARAMETERS: ** ** chan: channel number, longword, by reference ** host: sockaddr_in structure contained address and port, structure, by reference ** ** RETURN VALUE: ** ** VMS condition code ** **-- */ static int netio_conn ( short *chan, struct sockaddr_in *host ) { int status; static struct { short proto; char type; char domain; } sck_parm = {INET$C_TCP,INET_PROTYP$C_STREAM,INET$C_AF_INET}; iosb netiosb; struct sockaddr_in home = {0,0,0,0,0,0}; ILE2 rhst_adrs = {sizeof(struct sockaddr_in),0,host}, lhst_adrs = {sizeof(struct sockaddr_in),0,&home}, sock_opt [] = {{sizeof(one),TCPIP$C_KEEPALIVE,&one}}, options = {sizeof(sock_opt),TCPIP$C_SOCKOPT,&sock_opt}; *chan = 0; if ( !host->sin_addr.s_addr && !host->sin_port ) return SS$_INSFARG; /* ** Assign an I/O channel to BG device */ if ( !(1 & (status = sys$assign( &dsc_ucxdev,chan,0,0))) ) { if ( status != SS$_NOSUCHDEV ) return status; if ( !(1 & (status = sys$assign( &dsc_tcpipdev,chan,0,0))) ) return status; } /* ** */ home.sin_family = INET$C_AF_INET; /* if ( !(1 & (status = sys$qiow (EFN$C_ENF,*chan,IO$_SETMODE,&netiosb, 0,0,&sck_parm,0,&lhst_adrs,0,&options,0))) ) if ( !(1 & (status = sys$qiow (EFN$C_ENF,*chan,IO$_SETMODE,&netiosb, 0,0,&sck_parm,0,0,0,0,0))) ) */ if ( !(1 & (status = sys$qiow (EFN$C_ENF,*chan,IO$_SETMODE,&netiosb, 0,0,&sck_parm,0,0,0,&options,0))) ) return status; if ( !(netiosb.iosb$w_status & 1) ) return netiosb.iosb$w_status; host->sin_family = INET$C_AF_INET; if ( !(1 & (status = sys$qiow (EFN$C_ENF,*chan,IO$_ACCESS,&netiosb,0,0,0,0,&rhst_adrs,0,0,0))) ) if ( !(netiosb.iosb$w_status & 1) ) return netiosb.iosb$w_status; if ( !(1 & (status = sys$qiow (EFN$C_ENF,*chan,IO$_SETMODE,&netiosb, 0,0,0,0,0,0,&options,0))) ) return status; if ( !(netiosb.iosb$w_status & 1) ) return netiosb.iosb$w_status; return status; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Send a data over TCP-connection. ** ** FORMAL PARAMETERS: ** ** chan: a channel number, given from netio_conn,word, by value ** buf: a buffer to receiving data,byte array, by reference ** buflen: the length of a data in the buffer, word, by value ** ** RETURN VALUE: ** ** VMS condition code ** **-- */ static int netio_write ( short chan, char *buf, short buflen ) { int status; iosb netiosb; /* ** Enqueue a I/O request */ status = sys$qiow (EFN$C_ENF,chan,IO$_WRITEVBLK,&netiosb,0,0, buf,buflen,0,0,0,0); return (1 & status)?netiosb.iosb$w_status:status; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Read a data from the specified network channel, reading for the ** speficied amount of data with a timeout processing. ** ** FORMAL PARAMETERS: ** ** chan: a channel number, given from netio_conn, word, by value ** buf: a buffer to receiving data, a byte array, by reference ** buflen: the size of the buffer, word, by value ** retlen: a length of a received data in the buffer, word, by reference ** minlen: a minimal length of a data must be recived from a network, word, by value ** ** RETURN VALUE: ** ** VMS condition code ** **-- */ static int netio_read ( short chan, char *buf, short buflen, short *retlen, short minlen ) { int status; iosb netiosb; *retlen = 0; minlen = $min(buflen,minlen); /* ** Setup timer request */ if ( !(1 & (status = sys$setimr (EFN$C_ENF,iotmo, timer_ast,&chan,0))) ) return status; /* ** Reading the specified number of bytes */ status = sys$qiow (EFN$C_ENF,chan,IO$_READVBLK | IO$M_LOCKBUF,&netiosb,0,0, buf,buflen,0,0,0,0); *retlen = netiosb.iosb$w_bcnt; /* ** Cancel a timer request */ sys$cantim(&chan,0); return (1 & status)?netiosb.iosb$w_status:status; } static int ccompar ( int *a, int *b ) { if ( *a > *b ) return 1; else if ( *a < *b ) return -1; return 0; } static int pcompar ( short *a, short *b ) { return *a - *b; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** The API Initialization, allocate a context, open a TCP connection ** with remote host (SMSC). Prepare (sorting) elements in the parameters table and ** in the command parameters table for using with the bsearch(). ** ** ESME_API_INIT ctx, consts ** ** FORMAL PARAMETERS: ** ** ctx: A context area returned by this calls, by reference ** consts: A connection string in form "host.name:port", by descriptor ** ** RETURN VALUE: ** ** VMS condition code, ESME API specific condition code ** **-- */ int esme_api_init ( struct esme_ctx * *ctx, struct dsc$descriptor *consts ) { int status,chan = 0; char *cp; struct hostent *hp; char buf[255]; short buflen; struct sockaddr_in smsc; /* ** Check that context area is already created */ if ( *ctx && (*ctx)->esme_ctx$l_mark != ESME_CTX$K_MARK ) return ESME_INVCTX; /* ** Acquire semaphores and sort the tables */ __LOCK_LONG(&SMPP_TBL_SEM); if ( !SMPP_TBL_FLAG ) { unsigned c,p; struct smpp_par *par; /* ** Sort the tables */ qsort(smpp_cmd_tbl,smpp_cmd_sz,sizeof(struct smpp_cmd),ccompar); qsort(smpp_par_tbl,smpp_par_sz,sizeof(struct smpp_par),pcompar); /* ** Runs over the command parameters table and filling pointer field */ for(c = 0;c < smpp_cmd_sz;c++) { /* TRACE "cmd = %x!XL:",smpp_cmd_tbl[c].smpp_cmd$l_cmd); */ for(p = 0;p < SMPP_PAR$K_MAXPAR;p++) { if ( !smpp_cmd_tbl[c].smpp_cmd$r_args[p].par$w_tag ) break; /* printf(" %x ",smpp_cmd_tbl[c].smpp_cmd$r_args[p].par$w_tag); */ /* ** */ if ( !(par = bsearch(&smpp_cmd_tbl[c].smpp_cmd$r_args[p].par$w_tag, smpp_par_tbl, smpp_par_sz,sizeof(struct smpp_par),pcompar)) ) lib$signal(ESME_INVPARTAG,2,smpp_cmd_tbl[c].smpp_cmd$r_args[p].par$w_tag, smpp_cmd_tbl[c].smpp_cmd$l_cmd); /* ** Store pointer to the parameter */ smpp_cmd_tbl[c].smpp_cmd$r_args[p].par$a_ptr = par; } } SMPP_TBL_FLAG = 1; } __UNLOCK_LONG(&SMPP_TBL_SEM); /* ** Split connection string to several fields. */ memset(&smsc,0,sizeof(struct sockaddr_in)); memcpy(buf,consts->dsc$a_pointer,consts->dsc$w_length); buflen = consts->dsc$w_length; if ( !(cp = memchr(buf,':',buflen)) ) return ESME_INVCONSTS; *(cp++) = '\0'; buf[buflen] = '\0'; /* ** Translate/Resolve IP address/name to binary form */ if ( -1 != (inet_addr(buf)) ) smsc.sin_addr.s_addr = inet_addr(buf); else if ( hp = gethostbyname(buf) ) smsc.sin_addr.s_addr = *(int *)hp->h_addr; else return ESME_INVHOST; if ( !(smsc.sin_port = htons(atoi(cp))) ) return ESME_ZEROPORT; /* ** Open a TCP connection to remote host (SMSC) */ if ( !(1 & (status = netio_conn(&chan,&smsc))) ) return status; /* ** Allocate a memory for a new context area */ if ( !(*ctx) && !(1 & (status = lib$get_vm(&sizeof(struct esme_ctx),ctx,0))) ) return status; (*ctx)->esme_ctx$l_mark = ESME_CTX$K_MARK; (*ctx)->esme_ctx$l_seq = 1; (*ctx)->esme_ctx$l_netchan = chan; return status; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Close a TCP-connection with the remote hosts, release a context. ** ** ESME_API_SHUT ctx ** ** FORMAL PARAMETERS: ** ** ctx: A context area returned by this calls, by reference ** ** RETURN VALUE: ** ** VMS condition code, ESME API specific condition code ** **-- */ int esme_api_shut ( struct esme_ctx * *ctx ) { int status,chan = (*ctx)->esme_ctx$l_netchan; if ( *ctx && (*ctx)->esme_ctx$l_mark != ESME_CTX$K_MARK ) return ESME_INVCTX; /* ** Release a memory hasb been allocated for the context */ if ( !(1 & (status = lib$free_vm(&sizeof(struct esme_ctx),ctx,0))) ) return status; /* ** Close the has been opened TCP connection to remote host (SMSC) */ return netio_disc(chan); } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Looking for a specified tag in the array of the ILE3 elements. ** ** FORMAL PARAMETERS: ** ** tag: A tag id for looking for, longword, by value ** items: An items list array, ILE3 "zero" entry terminated array, by reference ** ** RETURN VALUE: ** ** A pointer to found element, otherwise NULL ** **-- */ static ILE3 *look4tag ( unsigned short tag, ILE3 *items ) { if ( !items ) return NULL; for (;items->ile3$w_code && (items->ile3$w_code != tag);items++); return items->ile3$w_code?items:NULL; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Put a specified argument or it default value to the PDU buffer. String value ** copied as is, integer value is copied in the NBO forms. ** ** FORMAL PARAMETERS: ** ** par: A pointer to default aguments value ** arg: A pointer to ILE3 argument structure ** buf: A pointer to buffer ** buflen: A length of the buffer ** retlen: A returned length of the argument's value ** ** RETURN VALUE: ** ** VMS condition code ** **-- */ static int putarg2pdu ( struct smpp_par *par, ILE3 *arg, unsigned char *buf, unsigned buflen, unsigned *retlen ) { unsigned char *val_c = par->defval$a_sts; unsigned int val_i = par->defval$l_int; unsigned short val_len= par->defval$w_len; if ( par->smpp_par$w_tag == SMPP_PTAG$K_OPTIONAL ) { *retlen = 0; return SS$_NORMAL; } /* ** Check that agrument is not NULL, and some sanity check */ if ( arg ) { val_c = arg->ile3$ps_bufaddr?arg->ile3$ps_bufaddr:par->defval$a_sts; val_i = arg->ile3$ps_bufaddr?*((int *)arg->ile3$ps_bufaddr):par->defval$l_int; val_len = $min(arg->ile3$w_length,par->smpp_par$w_maxlen); } /* ** Check for free space in the buffer */ if ( val_len > buflen ) return SS$_INSFMEM; TRACE "tag = !UL/%x!XL,type = !UL", par->smpp_par$w_tag, par->smpp_par$w_tag, par->smpp_par$w_type); /* ** Puts argument's value to the buffer */ switch (par->smpp_par$w_type) { case SMPP_DTYPE$K_INT: switch (val_len) { case 1: *((unsigned char *)buf) = (unsigned char) val_i; TRACE "val_i = !ZB,val_len = !UW",val_i,val_len); break; case sizeof(short): *((unsigned short *) buf) = htons(val_i); TRACE "val_i = !ZW,val_len = !UW", val_i,val_len); break; case sizeof(int): *((unsigned int *) buf) = htonl(val_i); TRACE "val_i = !ZL,val_len = !UW", val_i,val_len); break; } *retlen = val_len; break; case SMPP_DTYPE$K_OCTETS: memcpy(buf,val_c,val_len); *retlen = val_len; TRACE "val_c[0:!UW] = '!AD'",val_len,val_len,buf); break; case SMPP_DTYPE$K_ASCIZ: case SMPP_DTYPE$K_DECASCIZ: case SMPP_DTYPE$K_HEXASCIZ: default: memcpy(buf,val_c,val_len); /* ** Add a NUL character */ *(buf + (val_len++)) = '\0'; *retlen = val_len; TRACE "val_c[0:!UW] = '!AD'",val_len,val_len,buf); break; } return SS$_NORMAL; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Put a specified optional value into the PDU buffer. ** ** FORMAL PARAMETERS: ** ** arg: A pointer to ILE3 argument structure ** buf: A pointer to buffer ** buflen: A length of the buffer ** retlen: A returned length of the argument's value ** ** RETURN VALUE: ** ** VMS condition code ** **-- */ static int putopt2pdu ( ILE3 *arg, unsigned char *buf, unsigned buflen, unsigned *retlen ) { struct tlv *tlvp = (struct tlv *) buf; TRACE "tag = !UL/%x!XL,val[0:!UW] = '!AF'", arg->ile3$w_code,arg->ile3$w_code, arg->ile3$w_length,arg->ile3$w_length,arg->ile3$ps_bufaddr); /* ** Check for free space in the buffer */ if ( (sizeof(struct tlv) + arg->ile3$w_length) > buflen ) return SS$_INSFMEM; /* ** Puts argument's value to the buffer */ tlvp->tlv$w_tag = htons(arg->ile3$w_code); tlvp->tlv$w_len = htons(arg->ile3$w_length); memcpy(&tlvp->tlv$t_val,arg->ile3$ps_bufaddr,arg->ile3$w_length); *retlen = (sizeof(tlvp->tlv$w_tag) + sizeof(tlvp->tlv$w_len) + arg->ile3$w_length); return SS$_NORMAL; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Build a packet with a given list of parameters, sent it to the SMSC ** over has been opened TCP-connection. ** ** ESME_API_TX ctx, smpp_cmd, items, seq , sts ** ** FORMAL PARAMETERS: ** ** ctx: A context area built by netio_conn(), by reference ** smpp_cmd: A SMPP "command" , actualy it's a PDU type (see SMPP_CMD$* codes), by value ** items: An items list array, ILE3 "zero" entry terminated array, by reference ** seq: An optional sequence number ** sts: An optional status ** ** RETURN VALUE: ** ** VMS condition code, ESME API specific condition code ** **-- */ int esme_api_tx ( struct esme_ctx *ctx, unsigned smpp_cmd, ILE3 *items, unsigned *seq, unsigned *sts ) { int status; char buf[2048]; unsigned retlen = 0, buflen = (sizeof (struct smpp_pdu)) - 1; ILE3 *arg; struct smpp_pdu *pdu = (struct smpp_pdu *) buf; struct smpp_cmd *cmd = NULL; struct smpp_par *par = NULL; if ( ctx && (ctx->esme_ctx$l_mark != ESME_CTX$K_MARK) ) return ESME_INVCTX; /* ** Lookup a command in the table */ if ( !(cmd = (struct smpp_cmd *) bsearch(&smpp_cmd,smpp_cmd_tbl,smpp_cmd_sz, sizeof(struct smpp_cmd),ccompar)) ) return ESME_UNSUPCMD; TRACE "cmd = !UL/%x!XL",smpp_cmd,smpp_cmd); /* ** Runs over a parameter list exa and build a request PDU with the given items */ for (int i = 0; i < SMPP_PAR$K_MAXPAR; i++) { /* ** Is there a mandatory parameter? */ if ( !(par = cmd->smpp_cmd$r_args[i].par$a_ptr) ) break; /* ** Look for a value for the parameter in the input items list, ** or get a default value for the parameter. */ arg = look4tag(cmd->smpp_cmd$r_args[i].par$w_tag,items); /* ** Put argument's value into the PDU buffer */ retlen = 0; if ( arg && (arg->ile3$w_code < SMPP_PTAG$K_MANDATORY) ) { if ( !(1 & (status = putopt2pdu(arg,buf+buflen, sizeof(buf) - sizeof (struct smpp_pdu) - buflen,&retlen))) ) return status; } else if ( !(1 & (status = putarg2pdu(par,arg,buf+buflen, sizeof(buf) - sizeof (struct smpp_pdu) - buflen,&retlen))) ) return status; buflen += retlen; } /* ** Filling a header of the PDU */ pdu->smpp_pdu$l_length = htonl(buflen); pdu->smpp_pdu$l_cmd = htonl(smpp_cmd); pdu->smpp_pdu$l_status = (!sts)?0:htonl(*sts); pdu->smpp_pdu$l_seq = (!seq)?htonl(__ATOMIC_INCREMENT_LONG(&ctx->esme_ctx$l_seq)):htonl(*seq); /* ** Send the PDU over TCP-connection eand exit from the routine */ return netio_write(ctx->esme_ctx$l_netchan,buf,buflen); } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Put a specified arguments of it default value to the PDU buffer. String value ** copied as is, integer value putted in the NBO forms. ** ** FORMAL PARAMETERS: ** ** par: A pointer to parameter descriptor ** arg: A pointer to ILE3 argument structure ** buf: A pointer to buffer with the data ** buflen: A length of the buffer ** retlen: A length of processed part of the buffer ** ** RETURN VALUE: ** ** VMS condition code ** **-- */ static int getarg_pdu ( struct smpp_par *par, ILE3 *arg, unsigned char *buf, unsigned short buflen, unsigned short *retlen ) { unsigned char *cp = (unsigned char* ) buf; unsigned short *sp = (unsigned short *) buf; unsigned *ip = (unsigned *) buf; /* ** Extract a specified parameter from the data buffer */ switch (par->smpp_par$w_type) { case SMPP_DTYPE$K_INT: /* ** Some checks ... */ if ( buflen < par->smpp_par$w_maxlen ) return ESME_NODATA; if ( arg && (arg->ile3$w_length < par->smpp_par$w_maxlen) ) return ESME_NOSPACE; switch (par->smpp_par$w_maxlen) { case 1: if ( arg && arg->ile3$ps_bufaddr ) *((unsigned char *) arg->ile3$ps_bufaddr) = *cp; break; case sizeof(short): if ( arg && arg->ile3$ps_bufaddr ) *((unsigned short *) arg->ile3$ps_bufaddr) = ntohs(*sp); break; case sizeof(int): if ( arg && arg->ile3$ps_bufaddr ) *((unsigned int *) arg->ile3$ps_bufaddr) = ntohl(*ip); break; } if ( arg && arg->ile3$ps_bufaddr && arg->ile3$ps_retlen_addr ) *arg->ile3$ps_retlen_addr = par->smpp_par$w_maxlen; *retlen = par->smpp_par$w_maxlen; break; case SMPP_DTYPE$K_OCTETS: *retlen = (unsigned short) ((unsigned char* ) *(buf-1)); if ( arg && arg->ile3$ps_bufaddr && arg->ile3$ps_retlen_addr ) { memcpy(arg->ile3$ps_bufaddr,buf,*retlen); *arg->ile3$ps_retlen_addr = *retlen; } break; case SMPP_DTYPE$K_ASCIZ: case SMPP_DTYPE$K_DECASCIZ: case SMPP_DTYPE$K_HEXASCIZ: default: *retlen = strnlen(buf,$min(buflen,par->smpp_par$w_maxlen)); if ( arg && arg->ile3$ps_bufaddr && arg->ile3$ps_retlen_addr ) { memcpy(arg->ile3$ps_bufaddr,buf,*retlen); *arg->ile3$ps_retlen_addr = *retlen; } /* ** Skip NUL character */ if ( buflen > *retlen) (*retlen)++; break; } return SS$_NORMAL; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Receive a PDU from the SMSC, extract a parameters set specificed by items list, ** optionaly copy a tail of the PDU with optional parameters to the specified ** buffer. ** ** ESME_API_RX ctx, [items], smpp_resp, smpp_sts, [optresp], [optresplen], seq ** ** FORMAL PARAMETERS: ** ** ctx: A context area built by netio_conn(), by reference ** items: An items list array, ILE3 "zero" entry terminated array, by reference ** smpp_resp: A returned code of the SMPP response PDU ** smpp_sts: A status of the response PDU ** optresp: A buffer with optional parameters ** optresplen: A length of the buffer with optional parametyers ** smpp_seq: A sequence number ** ** ** RETURN VALUE: ** ** VMS condition code, ESME API specific condition code ** **-- */ int esme_api_rx ( struct esme_ctx *ctx, ILE3 *items, unsigned *smpp_resp, unsigned *smpp_sts, struct dsc$descriptor *optresp, short *optresplen, unsigned *smpp_seq ) { int status,st; unsigned sz = 0, retlen = 0, buflen = 0; ILE3 *arg; char *buf; struct smpp_pdu *pdu; struct smpp_cmd *cmd = NULL; struct smpp_par *par = NULL; if ( ctx && (ctx->esme_ctx$l_mark != ESME_CTX$K_MARK) ) return ESME_INVCTX; /* ** Get firstly a length of the PDU */ if ( !(1 & (status = netio_read(ctx->esme_ctx$l_netchan,&sz,sizeof(sz),&retlen,sizeof(sz)))) ) return status; sz = ntohl(sz); if ( sz < (sizeof(struct smpp_pdu) - 1) ) return ESME_PDUTOOSHORT; buflen = sz - (sizeof(struct smpp_pdu) - 1); /* ** Allocate memory for the PDU */ if (!(1 & (status = lib$get_vm(&sz,&pdu,0))) ) return status; sz -= 4; /* ** Get a whole PDU */ if ( !(1 & (status = netio_read(ctx->esme_ctx$l_netchan,&pdu->smpp_pdu$l_cmd,sz,&retlen,sz))) ) { /* ** If communication error is occured then release memory end exit */ sz += 4; if (!(1 & (st = lib$free_vm(&sz,&pdu,0))) ) lib$signal(st); return status; } *smpp_resp = ntohl(pdu->smpp_pdu$l_cmd); *smpp_sts = ntohl(pdu->smpp_pdu$l_status); *smpp_seq = ntohl(pdu->smpp_pdu$l_seq); buf = &pdu->smpp_pdu$b_body[0]; /* ** Lookup a command in the table */ if ( !(cmd = bsearch(smpp_resp,smpp_cmd_tbl,smpp_cmd_sz,sizeof(smpp_cmd_tbl[0]),ccompar)) ) { /* ** It's unknown command - stop processing */ sz += 4; if (!(1 & (st = lib$free_vm(&sz,&pdu,0))) ) lib$signal(st); return ESME_UNSUPCMD; } /* ** Runs over given parameter list to extract it from the PDU body */ for (int i = 0; items && ((i < SMPP_PAR$K_MAXPAR)) && buflen;i++) { /* ** Is there an argument for a parameter ? */ if ( !(par = cmd->smpp_cmd$r_args[i].par$a_ptr) ) break; arg = look4tag(cmd->smpp_cmd$r_args[i].par$w_tag,items); /* ** Processing a parameter */ if ( !(1 & (status = getarg_pdu(par,arg,buf,buflen,&retlen))) ) return status; buf += retlen; buflen -= retlen; } /* ** Is there optional parameters */ if ( buflen && optresp ) { status = ots$scopy_r_dx(buflen,buf,optresp); if ( optresplen ) *optresplen = $min(buflen,optresp->dsc$w_length); } /* ** Release memory has been allocated for the PDU */ sz += 4; if (!(1 & (st = lib$free_vm(&sz,&pdu,0))) ) lib$signal(st); return status; } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Translates an SMPP/EMSE error status code (comming in a PDU) to ** corresponding VMS condition code (ESME API specific). ** ** ESME_API_ERR2COND esmests ** ** FORMAL PARAMETERS: ** ** esmests: SMPP/ESME status code ** ** RETURN VALUE: ** ** VMS condition code, ESME API specific condition code ** **-- */ int esme_api_err2cond( int esmests ) { for (int i = 0;i < sizeof(err2cond_tbl)/sizeof(err2cond_tbl[0]);i++) if ( esmests == err2cond_tbl[i].error ) return err2cond_tbl[i].condition; return esmests?ESME_RUNKNOWNERR:SS$_NORMAL; }