📄 smsc_cgw.c
字号:
/* * smsc_cgw.c - Implements an interface to the Sonera ContentGateway software * * Anders Lindh, FlyerOne Ltd <alindh@flyerone.com> * * * Changelog: * * 22/02/2002: Preliminary support for the Euro character (req. ProviderServer 2.5.2) * 25/01/2002: Caught a potentially nasty bug * 16/01/2002: Some code cleanup * 10/01/2002: Fixed a bug in trn handling * 16/11/2001: Some minor fixes (Thanks to Tuomas Luttinen) * 12/11/2001: Delivery reports, better acking and numerous other small fixes * 05/11/2001: Initial release. Based heavily on smsc_emi2 and smsc_at2. * * * TO-DO: Do some real life testing * Squash bugs * * Usage: add the following to kannel.conf: * * group = smsc * smsc = cgw * host = x.x.x.x <- CGW server host * port = xxxx <- CGW server otp port (if omitted, defaults to 21772) * receive-port = xxxx <- our port for incoming messages * appname = xxxx <- Name of a "Send only" service. Defaults to "send". * All outgoing messages are routed through this service. * * Configure ContentGateway software to use the above port as text-port * (in provider.cnf). This is documented in their "Guide for Service * development 2.5", page 80. * * Add a new "Receive only" service (with the Remote Control tool), and set * the "Application for Incoming Message" to "text://kannelhost:receive-port". * * * Note: * * Do NOT define the service as a "Query/Reply" service, as it expects response * messages to have a matching session-id tag. Kannel does not store the * relation between a query and a reply, so the response message will not have * the session-id tag. Even though the messages are delivered successfully, * billing of premium priced services will fail. * * */#include <sys/types.h>#include <sys/socket.h>#include <unistd.h>#include <errno.h>#include <time.h>#include <limits.h>#include "gwlib/gwlib.h"#include "smscconn.h"#include "smscconn_p.h"#include "bb_smscconn_cb.h"#include "msg.h"#include "sms.h"#include "dlr.h"#define CGW_DEFPORT 21772#define CGW_EOL 0x0A#define CGW_TRN_MAX 500 /* Size of our internal message buffer. */#define CGWOP_MAXARGS 10 /* max. number of name:value pairs in cgwop *//* Valid CGW operations */#define CGW_OP_NOP 0 /* this doesn't really exist.. */#define CGW_OP_MSG 1#define CGW_OP_OK 2#define CGW_OP_ERR 3#define CGW_OP_DELIVERY 4#define CGW_OP_HELLO 5#define CGW_OP_STATUS 6struct cgwop{ int op; /* one of above */ int num_fields; int trn; /* transaction number, used to ACK messages */ Octstr **name; /* for storing name/value pairs */ Octstr **value;};static char *cgw_ops[6] = {"nop", "msg", "ok", "err", "delivery", "hello"};typedef struct privdata{ List *outgoing_queue; long receiver_thread; long sender_thread; int shutdown; /* Internal signal to shut down */ int listening_socket; /* File descriptor */ int send_socket; int port; /* SMSC port */ int rport; /* port for receiving messages*/ int our_port; /* Optional local port number in which to * bind our end of send connection */ Octstr *host; Octstr *allow_ip, *deny_ip; Octstr *appname; /* Application name as defined in Sonera Remote manager */ Msg *sendmsg[CGW_TRN_MAX]; time_t sendtime[CGW_TRN_MAX]; int dlr[CGW_TRN_MAX]; /* dlr = DLR_SMSC_SUCCESS || DLR_SMSC_FAIL */ int unacked; /* Sent messages not acked */ int waitack; /* Seconds to wait to ack */ int nexttrn; long check_time; /* last checked ack/nack status */}PrivData;static int cgw_add_msg_cb(SMSCConn *conn, Msg *sms);static int cgw_shutdown_cb(SMSCConn *conn, int finish_sending);static void cgw_start_cb(SMSCConn *conn);static long cgw_queued_cb(SMSCConn *conn);static void cgw_sender(void *arg);static Connection *cgw_open_send_connection(SMSCConn *conn);static int cgw_send_loop(SMSCConn *conn, Connection *server);void cgw_check_acks(PrivData *privdata);int cgw_wait_command(PrivData *privdata, SMSCConn *conn, Connection *server, int timeout);static int cgw_open_listening_socket(PrivData *privdata);static void cgw_listener(void *arg);static void cgw_receiver(SMSCConn *conn, Connection *server);static int cgw_handle_op(SMSCConn *conn, Connection *server, struct cgwop *cgwop);struct cgwop *cgw_read_op(PrivData *privdata, SMSCConn *conn, Connection *server, time_t timeout);/****************************************************************************** * Functions for handling cgwop -structures */static void cgwop_add(struct cgwop *cgwop, Octstr *name, Octstr *value){ if (cgwop->num_fields < CGWOP_MAXARGS) { cgwop->name[cgwop->num_fields] = octstr_duplicate(name); cgwop->value[cgwop->num_fields] = octstr_duplicate(value); cgwop->num_fields++; } else { info(0, "cgw: CGWOP_MAXARGS exceeded."); }}static struct cgwop *cgwop_create(int op, int trn){ struct cgwop *ret; Octstr *trnstr; ret = gw_malloc(sizeof(struct cgwop)); ret->op = op; ret->num_fields = 0; ret->trn = trn; ret->name = gw_malloc(CGWOP_MAXARGS * sizeof(Octstr *)); ret->value = gw_malloc(CGWOP_MAXARGS * sizeof(Octstr *)); if (trn != -1) { trnstr = octstr_create(""); octstr_append_decimal(trnstr, trn); cgwop_add(ret, octstr_imm("client-id"), trnstr); octstr_destroy(trnstr); } return ret;}static void cgwop_destroy(struct cgwop *cgwop){ int len; len = cgwop->num_fields; while (--len >= 0) { octstr_destroy(cgwop->name[len]); /* octstr_destroy(NULL) is ok */ octstr_destroy(cgwop->value[len]); } gw_free(cgwop->name); gw_free(cgwop->value); gw_free(cgwop);}static Octstr *cgwop_get(struct cgwop *cgwop, Octstr *name){ int len = cgwop->num_fields; while (--len >= 0) if (octstr_compare(name, cgwop->name[len]) == 0) return cgwop->value[len]; return NULL;}static Octstr *cgwop_tostr(struct cgwop *cgwop){ int len = cgwop->num_fields; Octstr *str; if (cgw_ops[cgwop->op] == NULL) return NULL; // invalid operation str = octstr_create(""); octstr_append(str, octstr_imm("op:")); octstr_append(str, octstr_imm(cgw_ops[cgwop->op])); octstr_append_char(str, CGW_EOL); while (--len >= 0) { octstr_append(str, cgwop->name[len]); octstr_append_char(str, ':'); octstr_append(str, cgwop->value[len]); octstr_append_char(str, CGW_EOL); } octstr_append(str, octstr_imm("end:")); octstr_append(str, octstr_imm(cgw_ops[cgwop->op])); octstr_append_char(str, CGW_EOL); return str;}static int cgwop_send(Connection *conn, struct cgwop *cgwop){ Octstr *dta = cgwop_tostr(cgwop); if (dta == NULL) return -1; /* couldn't convert to string */ if (conn_write(conn, dta) == -1) { octstr_destroy(dta); return -1; } octstr_destroy(dta); return 1;}/****************************************************************************** * cgw_encode_msg - Encode a msg according to specifications */static Octstr *cgw_encode_msg(Octstr* str){ int i; char esc = 27; char e = 'e'; /* Euro char (0x80) -> ESC + e. We do this conversion as long as the message length is under 160 chars (the checking could probably be done better) */ while ((i = octstr_search_char(str, 0x80, 0)) != -1) { octstr_delete(str, i, 1); // delete Euro char if (octstr_len(str) < 160) { octstr_insert_data(str, i, &esc, 1); // replace with ESC + e octstr_insert_data(str, i+1, &e, 1); } else { octstr_insert_data(str, i, &e, 1); // no room for ESC + e, just replace with an e } } /* Escape backslash characters */ while ((i = octstr_search_char(str, '\\', 0)) != -1) { octstr_insert(str, octstr_imm("\\"), i); } /* Remove Line Feed characters */ while ((i = octstr_search_char(str, CGW_EOL, 0)) != -1) { octstr_delete(str, i, 1); // delete EOL char octstr_insert(str, octstr_imm("\\n"), i); } /* Remove Carriage return characters */ while ((i = octstr_search_char(str, 13, 0)) != -1) { octstr_delete(str, i, 1); // delete EOL char octstr_insert(str, octstr_imm("\\r"), i); } return str;}/****************************************************************************** * cgw_decode_msg - Decode an incoming msg */static Octstr *cgw_decode_msg(Octstr* str){ int i; /* make \n -> linefeed */ while ((i = octstr_search(str, octstr_imm("\\n"), 0)) != -1) { octstr_delete(str, i, 2); // delete "\n" str octstr_insert(str, octstr_imm("\n"), i); } /* make \r -> carriage return */ while ((i = octstr_search(str, octstr_imm("\\r"), 0)) != -1) { octstr_delete(str, i, 2); // delete EOL char octstr_insert(str, octstr_imm("\r"), i); } /* remove double backslashes */ while ((i = octstr_search(str, octstr_imm("\\\\"), 0)) != -1) { octstr_delete(str, i, 1); } return str;}/****************************************************************************** * msg_to_cgwop - Create a send cgwop from a message */static struct cgwop *msg_to_cgwop(PrivData *privdata, Msg *msg, int trn){ struct cgwop *cgwop; Octstr *sender, *udh, *dta; cgwop = cgwop_create(CGW_OP_MSG, trn); if (cgwop == NULL) return NULL; if (!octstr_check_range(msg->sms.sender, 0, octstr_len(msg->sms.sender), gw_isdigit)) { /* If alphanumeric, simply prefix sender with '$' char */ sender = octstr_create("$"); octstr_append(sender, msg->sms.sender); } else sender = octstr_duplicate(msg->sms.sender); cgwop_add(cgwop, octstr_imm("app"), privdata->appname); cgwop_add(cgwop, octstr_imm("from"), sender); cgwop_add(cgwop, octstr_imm("to"), msg->sms.receiver); /* If delivery reports are asked, ask for them by adding a nrq:anything field */ if (msg->sms.dlr_mask & 0x07) cgwop_add(cgwop, octstr_imm("nrq"), octstr_imm("true")); octstr_destroy(sender); if (octstr_len(msg->sms.udhdata)) { udh = octstr_duplicate(msg->sms.udhdata); octstr_binary_to_hex(udh, 1); cgwop_add(cgwop, octstr_imm("udh"), udh); octstr_destroy(udh); dta = octstr_duplicate(msg->sms.msgdata); octstr_binary_to_hex(dta, 1); cgwop_add(cgwop, octstr_imm("msg"), dta); cgwop_add(cgwop, octstr_imm("type"), octstr_imm("bin")); octstr_destroy(dta); } else { cgwop_add(cgwop, octstr_imm("msg"), cgw_encode_msg(msg->sms.msgdata)); } return cgwop;}/****************************************************************************** * Called to create the SMSC. This is our entry point. */int smsc_cgw_create(SMSCConn *conn, CfgGroup *cfg){ PrivData *privdata; Octstr *allow_ip, *deny_ip, *host, *appname; long portno, our_port, waitack; int i; privdata = gw_malloc(sizeof(PrivData)); privdata->outgoing_queue = list_create(); privdata->listening_socket = -1; if (cfg_get_integer(&portno, cfg, octstr_imm("port")) == -1) portno = 0; privdata->port = portno; if (cfg_get_integer(&portno, cfg, octstr_imm("receive-port")) < 0) portno = 0; privdata->rport = portno;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -