[PATCH] CIMD2 driver upgraded to smsconn api
"Angel Fradejas" <[email protected]>
| Newsgroups | gmane.comp.mobile.kannel.devel |
|---|---|
| Message-ID | <[email protected]> |
So, finally here it is. A first working version of the Nokia CIMD2 driver upgraded to the more convenient api SMSCConn*. I took Kalle Majorla's work on this issue as a starting point, then further improved it. I have tested this with two providers, with every type of message I could think of (binary, ems, class 0, concatenated and so on) and works fine. Also I've been playing around hard with connections and reconnections, dropping them by iptables, and also with the http interfaces stop-smsc and start-smsc. Apart from the obvious API upgrade, I'd like to highlight some other features I added (some of them compatibility breakers): 1) Support for "our-hort" smsc config directive 2) Support for "our-port" smsc config directive 3) Harmonization of "my-number" config directive, instead of "sender-prefix", to make it consistent with other drivers, such as AT2, EMI2, SMASI and SMPP. (** COMPATIBILITY BREAKER **) 4) Interpretation of "keepalive" config directive in seconds instead of minutes, also by means of consistency with others drivers. It happens that one of my providers has a timeout to cut connections shorter than a minute. (** COMPATIBILITY BREAKER **) 5) Added support for parameter 069, Service Center Address, to avoid cludging log files with warnings and packet dumps. I also have a provider that sends this parameter with every MO. 6) Harmonization of log messages, with the format "CIMD2[smsc_id]: xxxxxxxxxxxxxxx", to ease identification of messages by connection. Maybe some you can try it out and vote. It would be nice to see this going into CVS for the upcoming 1.3.2 release. Cheers. Angel Fradejas Mediafusión España, S.A. [email protected] www.mediafusion.es Tel. +34 91 252 32 00 Fax +34 91 572 27 08
cimd2_smscconn.diff
(application/octet-stream, 64.8 KB)
Index: gw/smscconn.c
===================================================================
RCS file: /home/cvs/gateway/gw/smscconn.c,v
retrieving revision 1.35
diff -u -r1.35 smscconn.c
--- gw/smscconn.c 19 Mar 2003 17:40:36 -0000 1.35
+++ gw/smscconn.c 21 Mar 2003 15:23:46 -0000
@@ -91,6 +91,8 @@
if (octstr_compare(smsc_type, octstr_imm("fake")) == 0)
ret = smsc_fake_create(conn, grp);
+ else if (octstr_compare(smsc_type, octstr_imm("cimd2")) == 0)
+ ret = smsc_cimd2_create(conn, grp);
else if (octstr_compare(smsc_type, octstr_imm("emi2")) == 0)
ret = smsc_emi2_create(conn, grp);
else if (octstr_compare(smsc_type, octstr_imm("http")) == 0)
Index: gw/smscconn_p.h
===================================================================
RCS file: /home/cvs/gateway/gw/smscconn_p.h,v
retrieving revision 1.35
diff -u -r1.35 smscconn_p.h
--- gw/smscconn_p.h 19 Mar 2003 17:40:36 -0000 1.35
+++ gw/smscconn_p.h 21 Mar 2003 15:23:46 -0000
@@ -185,6 +185,9 @@
/* Responsible file: smsc/smsc_fake.c */
int smsc_fake_create(SMSCConn *conn, CfgGroup *cfg);
+/* Responsible file: smsc/smsc_cimd2.c */
+int smsc_cimd2_create(SMSCConn *conn, CfgGroup *cfg);
+
/* Responsible file: smsc/smsc_emi2.c */
int smsc_emi2_create(SMSCConn *conn, CfgGroup *cfg);
Index: gw/smsc/smsc.c
===================================================================
RCS file: /home/cvs/gateway/gw/smsc/smsc.c,v
retrieving revision 1.2
diff -u -r1.2 smsc.c
--- gw/smsc/smsc.c 28 Jan 2003 00:10:04 -0000 1.2
+++ gw/smsc/smsc.c 21 Mar 2003 15:23:46 -0000
@@ -73,18 +73,6 @@
smsc->cimd_username = NULL;
smsc->cimd_password = NULL;
- /* CIMD 2 */
- smsc->cimd2_hostname = NULL;
- smsc->cimd2_port = -1;
- smsc->cimd2_username = NULL;
- smsc->cimd2_password = NULL;
- smsc->cimd2_send_seq = 1;
- smsc->cimd2_receive_seq = 0;
- smsc->cimd2_inbuffer = NULL;
- smsc->cimd2_received = NULL;
- smsc->cimd2_error = 0;
- smsc->cimd2_next_ping = 0;
-
/* EMI */
smsc->emi_phonenum = NULL;
smsc->emi_serialdevice = NULL;
@@ -164,14 +152,6 @@
gw_free(smsc->cimd_username);
gw_free(smsc->cimd_password);
- /* CIMD 2 */
- octstr_destroy(smsc->cimd2_hostname);
- octstr_destroy(smsc->cimd2_username);
- octstr_destroy(smsc->cimd2_password);
- octstr_destroy(smsc->cimd2_inbuffer);
- octstr_destroy(smsc->sender_prefix);
- list_destroy(smsc->cimd2_received, NULL);
-
/* EMI */
gw_free(smsc->emi_phonenum);
gw_free(smsc->emi_serialdevice);
@@ -228,11 +208,6 @@
goto error;
break;
- case SMSC_TYPE_CIMD2:
- if (cimd2_submit_msg(smsc, msg) == -1)
- goto error;
- break;
-
case SMSC_TYPE_EMI:
case SMSC_TYPE_EMI_IP:
if (emi_submit_msg(smsc, msg) == -1)
@@ -289,12 +264,6 @@
goto error;
break;
- case SMSC_TYPE_CIMD2:
- ret = cimd2_receive_msg(smsc, msg);
- if (ret == -1)
- goto error;
- break;
-
case SMSC_TYPE_EMI:
case SMSC_TYPE_EMI_IP:
ret = emi_receive_msg(smsc, msg);
@@ -359,12 +328,6 @@
goto error;
break;
- case SMSC_TYPE_CIMD2:
- ret = cimd2_pending_smsmessage(smsc);
- if (ret == -1)
- goto error;
- break;
-
case SMSC_TYPE_EMI:
case SMSC_TYPE_EMI_IP:
ret = emi_pending_smsmessage(smsc);
@@ -532,8 +495,6 @@
}
if (octstr_compare(type, octstr_imm("cimd")) == 0)
typeno = SMSC_TYPE_CIMD;
- else if (octstr_compare(type, octstr_imm("cimd2")) == 0)
- typeno = SMSC_TYPE_CIMD2;
else if (octstr_compare(type, octstr_imm("emi")) == 0)
typeno = SMSC_TYPE_EMI;
else if (octstr_compare(type, octstr_imm("emi_ip")) == 0)
@@ -610,18 +571,6 @@
octstr_get_cstr(password));
break;
- case SMSC_TYPE_CIMD2:
- if (host == NULL || port == 0 || username == NULL || password == NULL)
- error(0, "Required field missing for CIMD 2 center.");
- else
- smsc = cimd2_open(host,
- port,
- username,
- password,
- keepalive,
- sender_prefix);
- break;
-
case SMSC_TYPE_EMI:
if (phone == NULL || device == NULL || username == NULL ||
password == NULL)
@@ -746,9 +695,6 @@
case SMSC_TYPE_CIMD:
ret = cimd_reopen(smsc);
break;
- case SMSC_TYPE_CIMD2:
- ret = cimd2_reopen(smsc);
- break;
case SMSC_TYPE_EMI_IP:
ret = emi_reopen_ip(smsc);
break;
@@ -795,11 +741,6 @@
switch (smsc->type) {
case SMSC_TYPE_CIMD:
if (cimd_close(smsc) == -1)
- errors = 1;
- break;
-
- case SMSC_TYPE_CIMD2:
- if (cimd2_close(smsc) == -1)
errors = 1;
break;
Index: gw/smsc/smsc_cimd2.c
===================================================================
RCS file: /home/cvs/gateway/gw/smsc/smsc_cimd2.c,v
retrieving revision 1.6
diff -u -r1.6 smsc_cimd2.c
--- gw/smsc/smsc_cimd2.c 6 Feb 2003 13:50:44 -0000 1.6
+++ gw/smsc/smsc_cimd2.c 21 Mar 2003 15:23:46 -0000
@@ -1,6 +1,8 @@
/* Driver for CIMD 2 SMS centres.
* Copyright 2000 WapIT Oy Ltd.
* Author: Richard Braakman
+ *
+ * Upgrade to SMSCConn API: 2002 Kalle Marjola / 2003 Angel Fradejas
*/
/* TODO: Check checksums on incoming packets */
@@ -28,13 +30,42 @@
#include <unistd.h>
#include "gwlib/gwlib.h"
-#include "smsc_p.h"
+#include "smscconn.h"
+#include "smscconn_p.h"
+#include "bb_smscconn_cb.h"
+
+#include "shared.h"
#include "sms.h"
#include "dlr.h"
-#ifndef CIMD2_TRACE
-#define CIMD2_TRACE 0
-#endif
+
+typedef struct privdata {
+ Octstr *username;
+ Octstr *password;
+ Octstr *host;
+ long port;
+ long our_port;
+ long keepalive;
+ Octstr *my_number;
+
+ int socket;
+ int send_seq;
+ int receive_seq;
+
+ Octstr *inbuffer;
+ List *received;
+
+ time_t next_ping;
+
+ List *outgoing_queue;
+ SMSCConn *conn;
+ int receiver_thread;
+ int sender_thread;
+ int quitting;
+ List *stopped; /* list-trick for suspend/isolate */
+
+} PrivData;
+
/* Microseconds before giving up on a request */
@@ -101,6 +132,7 @@
P_MESSAGE_COUNT = 66,
P_PRIORITY = 67,
P_DELIVERY_REQUEST_MODE = 68,
+ P_SERVICE_CENTER_ADDRESS = 69,
P_GET_PARAMETER = 500,
P_MC_TIME = 501,
P_ERROR_CODE = 900,
@@ -159,6 +191,7 @@
{ "message count", P_MESSAGE_COUNT, 3, P_INT, 0, 999 },
{ "priority", P_PRIORITY, 1, P_INT, 1, 9 },
{ "delivery request mode", P_DELIVERY_REQUEST_MODE, 1, P_INT, 0, 2 },
+ { "service center address", P_SERVICE_CENTER_ADDRESS, 20, P_ADDRESS },
{ "get parameter", P_GET_PARAMETER, 3, P_INT, 501, 999 },
{ "MC time", P_MC_TIME, 12, P_TIME },
{ "error code", P_ERROR_CODE, 3, P_INT, 0, 999 },
@@ -356,7 +389,7 @@
int seq; /* Sequence number */
Octstr *data; /* Encoded packet */
/* CIMD 2 packet structure is so simple that packet information is
- * stored as a valid encoded packet, and decoded as necessary.
+ * stored as a valid encoded packet, and decoded as necessary.
* Exceptions: operation code and sequence number are also stored
* as ints for speed, and the checksum is not added until the packet
* is about to be sent. Since checksums are optional, the packet
@@ -381,7 +414,8 @@
/* A reminder that packets are created without a valid sequence number */
#define BOGUS_SEQUENCE 0
-static Msg *cimd2_accept_delivery_report_message(struct packet *request, SMSCenter *smsc);
+static Msg *cimd2_accept_delivery_report_message(struct packet *request,
+ SMSCConn *conn);
/* Look for the STX OO:SSS TAB header defined by CIMD 2, where OO is the
* operation code in two decimals and SSS is the sequence number in three
* decimals. Leave the results in the proper fields of the packet.
@@ -440,7 +474,7 @@
* a struct. Return NULL if "in" contains no packet. Always delete
* leading non-packet data from "in". (The CIMD 2 spec says we should
* ignore any data between the packet markers). */
-static struct packet *packet_extract(Octstr *in)
+static struct packet *packet_extract(Octstr *in, SMSCConn *conn)
{
int stx, etx;
Octstr *packet;
@@ -465,7 +499,8 @@
* will at least allow good logging of the error. */
stx = octstr_search_char(in, STX, 1);
if (stx >= 0 && stx < etx) {
- warning(0, "CIMD2: packet without end marker");
+ warning(0, "CIMD2[%s]: packet without end marker",
+ octstr_get_cstr(conn->id));
packet = octstr_copy(in, 0, stx);
octstr_delete(in, 0, stx);
octstr_append_cstr(packet, ETX_str);
@@ -611,7 +646,7 @@
/* Check if the header is according to CIMD 2 spec, generating log
* entries as necessary. Return -1 if anything was wrong, otherwise 0. */
-static int packet_check_header(struct packet *packet)
+static int packet_check_header(struct packet *packet, SMSCConn *conn)
{
Octstr *data;
@@ -626,14 +661,15 @@
octstr_get_char(data, 3) != ':' ||
!octstr_check_range(data, 4, 3, gw_isdigit) ||
octstr_get_char(data, 7) != TAB) {
- warning(0, "CIMD2 packet header in wrong format");
+ warning(0, "CIMD2[%s]: packet header in wrong format",
+ octstr_get_cstr(conn->id));
return -1;
}
return 0;
}
-static int packet_check_parameter(struct packet *packet, long pos, long len)
+static int packet_check_parameter(struct packet *packet, long pos, long len, SMSCConn *conn)
{
Octstr *data;
long parm;
@@ -653,7 +689,8 @@
if (len < 5 ||
!octstr_check_range(data, pos + 1, 3, gw_isdigit) ||
octstr_get_char(data, pos + 4) != ':') {
- warning(0, "CIMD2 parameter at offset %ld in wrong format",
+ warning(0, "CIMD2[%s]: parameter at offset %ld in wrong format",
+ octstr_get_cstr(conn->id),
pos);
errors++;
}
@@ -673,12 +710,15 @@
i = parm_index(parm);
if (i < 0) {
- warning(0, "CIMD2 packet contains unknown parameter %ld", parm);
+ warning(0, "CIMD2[%s]: packet contains unknown parameter %ld",
+ octstr_get_cstr(conn->id),
+ parm);
return -1;
}
if (dlen > parameters[i].maxlen) {
- warning(0, "CIMD2 packet has '%s' parameter with length %ld, spec says max %d",
+ warning(0, "CIMD2[%s]: packet has '%s' parameter with length %ld, spec says max %d",
+ octstr_get_cstr(conn->id),
parameters[i].name, len, parameters[i].maxlen);
errors++;
}
@@ -689,12 +729,15 @@
negative = (octstr_get_char(data, dpos) == '-');
if (!octstr_check_range(data, dpos + negative,
dlen - negative, gw_isdigit)) {
- warning(0, "CIMD2 packet has '%s' parameter with non-integer contents", parameters[i].name);
+ warning(0, "CIMD2[%s]: packet has '%s' parameter with non-integer contents",
+ octstr_get_cstr(conn->id),
+ parameters[i].name);
errors++;
}
if (octstr_parse_long(&value, data, dpos, 10) >= 0 &&
(value < parameters[i].minval || value > parameters[i].maxval)) {
- warning(0, "CIMD2 packet has '%s' parameter out of range (value %ld, min %d, max %d)",
+ warning(0, "CIMD2[%s]: packet has '%s' parameter out of range (value %ld, min %d, max %d)",
+ octstr_get_cstr(conn->id),
parameters[i].name, value,
parameters[i].minval, parameters[i].maxval);
errors++;
@@ -702,23 +745,31 @@
break;
case P_TIME:
if (!octstr_check_range(data, dpos, dlen, gw_isdigit)) {
- warning(0, "CIMD2 packet has '%s' parameter with non-digit contents", parameters[i].name);
+ warning(0, "CIMD2[%s]: packet has '%s' parameter with non-digit contents",
+ octstr_get_cstr(conn->id),
+ parameters[i].name);
errors++;
}
break;
case P_ADDRESS:
if (!octstr_check_range(data, dpos, dlen, isphonedigit)) {
- warning(0, "CIMD2 packet has '%s' parameter with non phone number contents", parameters[i].name);
+ warning(0, "CIMD2[%s]: packet has '%s' parameter with non phone number contents",
+ octstr_get_cstr(conn->id),
+ parameters[i].name);
errors++;
}
break;
case P_HEX:
if (!octstr_check_range(data, dpos, dlen, gw_isxdigit)) {
- warning(0, "CIMD2 packet has '%s' parameter with non-hex contents", parameters[i].name);
+ warning(0, "CIMD2[%s]: packet has '%s' parameter with non-hex contents",
+ octstr_get_cstr(conn->id),
+ parameters[i].name);
errors++;
}
if (dlen % 2 != 0) {
- warning(0, "CIMD2 packet has odd-length '%s' parameter", parameters[i].name);
+ warning(0, "CIMD2[%s]: packet has odd-length '%s' parameter",
+ octstr_get_cstr(conn->id),
+ parameters[i].name);
errors++;
}
break;
@@ -736,7 +787,7 @@
/* Check the packet against the CIMD 2 spec, generating log entries as
* necessary. Return -1 if anything was wrong, otherwise 0. */
/* TODO: Check if parameters found actually belong in the packet type */
-static int packet_check(struct packet *packet)
+static int packet_check(struct packet *packet, SMSCConn *conn)
{
int errors = 0;
long pos, len, next;
@@ -747,14 +798,15 @@
if (octstr_search_char(data, 0, 0) >= 0) {
/* CIMD2 spec does not allow NUL bytes in a packet */
- warning(0, "CIMD2 packet contains NULs");
+ warning(0, "CIMD2[%s]: packet contains NULs",
+ octstr_get_cstr(conn->id));
errors++;
}
/* Assume the packet starts with STX and ends with ETX,
* because we parsed it that way in the first place. */
- errors += (packet_check_header(packet) < 0);
+ errors += (packet_check_header(packet,conn) < 0);
/* Parameters are separated by tabs. After the last parameter
* there is a tab, an optional two-digit checksum, and the ETX.
@@ -767,7 +819,7 @@
for ( ; pos >= 0; pos = next) {
next = octstr_search_char(data, TAB, pos + 1);
if (next >= 0) {
- errors += (packet_check_parameter(packet, pos, next - pos) < 0);
+ errors += (packet_check_parameter(packet, pos, next - pos, conn) < 0);
} else {
/* Check if the checksum has the right format. Don't
* check the sum itself here, that will be done in a
@@ -778,7 +830,8 @@
if (!(octstr_len(data) - pos == 2 ||
(octstr_len(data) - pos == 4 &&
octstr_check_range(data, pos + 1, 2, gw_isxdigit)))) {
- warning(0, "CIMD2 packet checksum in wrong format");
+ warning(0, "CIMD2[%s]: packet checksum in wrong format",
+ octstr_get_cstr(conn->id));
errors++;
}
}
@@ -793,13 +846,14 @@
return 0;
}
-static void packet_check_can_receive(struct packet *packet)
+static void packet_check_can_receive(struct packet *packet, SMSCConn *conn)
{
gw_assert(packet != NULL);
if (!operation_can_receive(packet->operation)) {
Octstr *name = operation_name(packet->operation);
- warning(0, "CIMD2 SMSC sent us %s request",
+ warning(0, "CIMD2[%s]: SMSC sent us %s request",
+ octstr_get_cstr(conn->id),
octstr_get_cstr(name));
octstr_destroy(name);
}
@@ -867,7 +921,7 @@
{ -1, NULL }
};
-static int packet_display_error(struct packet *packet)
+static int packet_display_error(struct packet *packet, SMSCConn *conn)
{
int code;
Octstr *text = NULL;
@@ -898,7 +952,8 @@
}
opname = operation_name(packet->operation);
- error(0, "CIMD2 %s contained error message:",
+ error(0, "CIMD2[%s]: %s contained error message:",
+ octstr_get_cstr(conn->id),
octstr_get_cstr(opname));
error(0, "code %03d: %s", code, octstr_get_cstr(text));
octstr_destroy(opname);
@@ -968,7 +1023,7 @@
* to the corresponding ISO-8859-1 characters. Since we are heading
* toward that character set in the end, we don't bother converting
* those to GSM. */
-static void convert_cimd2_to_gsm(Octstr *text)
+static void convert_cimd2_to_gsm(Octstr *text, SMSCConn *conn)
{
long pos, len;
int cimd1, cimd2;
@@ -1003,8 +1058,9 @@
break;
}
if (cimd_combinations[i].cimd1 == 0)
- warning(0, "CIMD2: Encountered unknown "
+ warning(0, "CIMD2[%s]: Encountered unknown "
"escape code _%c%c, ignoring.",
+ octstr_get_cstr(conn->id),
cimd1, cimd2);
else {
octstr_delete(text, pos, 2);
@@ -1078,7 +1134,7 @@
/* Add a parameter to the end of packet */
static void packet_add_parm(struct packet *packet, int parmtype,
- int parmno, Octstr *value)
+ int parmno, Octstr *value, SMSCConn *conn)
{
unsigned char parmh[sizeof("tPPP:")];
long position;
@@ -1091,9 +1147,12 @@
gw_assert(parm_type(parmno) == parmtype);
if (len > parm_maxlen(parmno)) {
- warning(0, "CIMD2: %s parameter too long, truncating from "
- "%ld to %ld characters", parm_name(parmno),
- len, (long) parm_maxlen(parmno));
+ warning(0, "CIMD2[%s]: %s parameter too long, truncating from "
+ "%ld to %ld characters",
+ octstr_get_cstr(conn->id),
+ parm_name(parmno),
+ len,
+ (long) parm_maxlen(parmno));
value = octstr_copy(value, 0, parm_maxlen(parmno));
copied = 1;
}
@@ -1110,23 +1169,23 @@
}
/* Add a String parameter to the packet */
-static void packet_add_string_parm(struct packet *packet, int parmno, Octstr *value)
+static void packet_add_string_parm(struct packet *packet, int parmno, Octstr *value, SMSCConn *conn)
{
- packet_add_parm(packet, P_STRING, parmno, value);
+ packet_add_parm(packet, P_STRING, parmno, value, conn);
}
/* Add an Address parameter to the packet */
-static void packet_add_address_parm(struct packet *packet, int parmno, Octstr *value)
+static void packet_add_address_parm(struct packet *packet, int parmno, Octstr *value, SMSCConn *conn)
{
gw_assert(octstr_check_range(value, 0, octstr_len(value), isphonedigit));
- packet_add_parm(packet, P_ADDRESS, parmno, value);
+ packet_add_parm(packet, P_ADDRESS, parmno, value, conn);
}
/* Add an SMS parameter to the packet. The caller is expected to have done
* the translation to the GSM character set already. */
-static void packet_add_sms_parm(struct packet *packet, int parmno, Octstr *value)
+static void packet_add_sms_parm(struct packet *packet, int parmno, Octstr *value, SMSCConn *conn)
{
- packet_add_parm(packet, P_SMS, parmno, value);
+ packet_add_parm(packet, P_SMS, parmno, value, conn);
}
/* There is no function for adding a Time parameter to the packet, because
@@ -1135,16 +1194,16 @@
* and using that for synchronization. And beware of DST changes. */
/* Add a Hexadecimal parameter to the packet */
-static void packet_add_hex_parm(struct packet *packet, int parmno, Octstr *value)
+static void packet_add_hex_parm(struct packet *packet, int parmno, Octstr *value, SMSCConn *conn)
{
value = octstr_duplicate(value);
octstr_binary_to_hex(value, 1); /* 1 for uppercase hex, i.e. A .. F */
- packet_add_parm(packet, P_HEX, parmno, value);
+ packet_add_parm(packet, P_HEX, parmno, value, conn);
octstr_destroy(value);
}
/* Add an Integer parameter to the packet */
-static void packet_add_int_parm(struct packet *packet, int parmno, long value)
+static void packet_add_int_parm(struct packet *packet, int parmno, long value, SMSCConn *conn)
{
unsigned char buf[128];
Octstr *valuestr;
@@ -1153,7 +1212,7 @@
sprintf(buf, "%ld", value);
valuestr = octstr_create(buf);
- packet_add_parm(packet, P_INT, parmno, valuestr);
+ packet_add_parm(packet, P_INT, parmno, valuestr, conn);
octstr_destroy(valuestr);
}
@@ -1202,7 +1261,7 @@
packet->seq = seq;
}
-static struct packet *packet_encode_message(Msg *msg, Octstr *sender_prefix)
+static struct packet *packet_encode_message(Msg *msg, Octstr *sender_prefix, SMSCConn *conn)
{
struct packet *packet;
Octstr *text;
@@ -1220,28 +1279,28 @@
msg->sms.sender = octstr_create("");
if (!parm_valid_address(msg->sms.receiver)) {
- warning(0, "cimd2_submit_msg: non-digits in "
- "destination phone number '%s', discarded",
+ warning(0, "CIMD2[%s]: non-digits in destination phone number '%s', discarded",
+ octstr_get_cstr(conn->id),
octstr_get_cstr(msg->sms.receiver));
return NULL;
}
packet = packet_create(SUBMIT_MESSAGE, BOGUS_SEQUENCE);
- packet_add_address_parm(packet, P_DESTINATION_ADDRESS, msg->sms.receiver);
+ packet_add_address_parm(packet, P_DESTINATION_ADDRESS, msg->sms.receiver, conn);
/* CIMD2 interprets the originating address as a sub-address to
* our connection number (so if the connection is "400" and we
* fill in "600" as the sender number, the user sees "400600").
* Since we have no way to ask what this number is, it has to
* be configured. */
-
+
/* Quick and dirty check to see if we are using alphanumeric sender */
if (parm_valid_address(msg->sms.sender)) {
/* We are not, so send in the usual way */
/* Speed up the default case */
if (octstr_len(sender_prefix) == 0) {
- packet_add_address_parm(packet, P_ORIGINATING_ADDRESS,msg->sms.sender);
+ packet_add_address_parm(packet, P_ORIGINATING_ADDRESS,msg->sms.sender, conn);
}
else if (octstr_compare(sender_prefix, octstr_imm("never")) != 0) {
if (octstr_ncompare(sender_prefix, msg->sms.sender,
@@ -1249,18 +1308,19 @@
Octstr *sender;
sender = octstr_copy(msg->sms.sender,
octstr_len(sender_prefix), octstr_len(msg->sms.sender));
- packet_add_address_parm(packet, P_ORIGINATING_ADDRESS, sender);
+ packet_add_address_parm(packet, P_ORIGINATING_ADDRESS, sender, conn);
octstr_destroy(sender);
} else {
- warning(0, "CIMD2: Sending message with originating address <%s>, "
+ warning(0, "CIMD2[%s]: Sending message with originating address <%s>, "
"which does not start with the sender-prefix.",
+ octstr_get_cstr(conn->id),
octstr_get_cstr(msg->sms.sender));
}
}
}
else {
/* The test above to check if sender was all digits failed, so assume we want alphanumeric sender */
- packet_add_string_parm(packet, P_ALPHANUMERIC_ORIGINATING_ADDRESS,msg->sms.sender);
+ packet_add_string_parm(packet, P_ALPHANUMERIC_ORIGINATING_ADDRESS,msg->sms.sender, conn);
}
/* Add the validity period if necessary. This sets the relative validity
@@ -1273,7 +1333,7 @@
* 168-196 = 2days to 30days in 1 day increments.
* 197-255 = 5weeks to 63weeks in 1 week increments.
*
- * This code was copied from smsc_at2.c.
+ * This code was copied from smsc_at2.c.
*/
if (msg->sms.validity) {
if (msg->sms.validity > 635040)
@@ -1295,7 +1355,7 @@
if (msg->sms.validity < 5)
setvalidity = 0;
- packet_add_int_parm(packet, P_VALIDITY_PERIOD_RELATIVE, setvalidity);
+ packet_add_int_parm(packet, P_VALIDITY_PERIOD_RELATIVE, setvalidity, conn);
}
/* Explicitly ask not to get status reports.
@@ -1306,16 +1366,16 @@
if (msg->sms.dlr_mask & 0x03)
{
- packet_add_int_parm(packet, P_STATUS_REPORT_REQUEST, 14);
+ packet_add_int_parm(packet, P_STATUS_REPORT_REQUEST, 14, conn);
}
else
- packet_add_int_parm(packet, P_STATUS_REPORT_REQUEST, 0);
+ packet_add_int_parm(packet, P_STATUS_REPORT_REQUEST, 0, conn);
/* Turn off reply path as default.
* This avoids phones automatically asking for a reply
* However, it is a nice feature and could be enabled through a parameter
*/
- packet_add_int_parm(packet, P_REPLY_PATH, 0);
+ packet_add_int_parm(packet, P_REPLY_PATH, 0, conn);
truncated = 0;
@@ -1323,8 +1383,7 @@
if (octstr_len(msg->sms.udhdata)) {
/* udhdata will be truncated and warned about if
* it does not fit. */
- packet_add_hex_parm(packet, P_USER_DATA_HEADER, msg->sms.udhdata);
- spaceleft -= octstr_len(msg->sms.udhdata);
+ packet_add_hex_parm(packet, P_USER_DATA_HEADER, msg->sms.udhdata, conn);
}
if (msg->sms.coding == DC_7BIT)
spaceleft = spaceleft * 8 / 7;
@@ -1332,39 +1391,33 @@
spaceleft = 0;
text = octstr_duplicate(msg->sms.msgdata);
+
if (octstr_len(text) > 0 && spaceleft == 0) {
- warning(0, "CIMD2: message filled up with "
- "UDH, no room for message text");
+ warning(0, "CIMD2[%s]: message filled up with UDH, no room for message text",
+ octstr_get_cstr(conn->id));
} else if (msg->sms.coding == DC_8BIT || msg->sms.coding == DC_UCS2) {
if (octstr_len(text) > spaceleft) {
truncated = octstr_len(text) - spaceleft;
octstr_truncate(text, spaceleft);
}
- packet_add_hex_parm(packet, P_USER_DATA_BINARY, text);
+ packet_add_hex_parm(packet, P_USER_DATA_BINARY, text, conn);
} else {
-#if CIMD2_TRACE
- debug("bb.sms.cimd2", 0, "CIMD2 sending message. Text:");
- octstr_dump(text, 0);
-#endif
/* Going from latin1 to GSM to CIMD2 may seem like a
* detour, but it's the only way to get all the escape
* codes right. */
charset_latin1_to_gsm(text);
truncated = charset_gsm_truncate(text, spaceleft);
convert_gsm_to_cimd2(text);
-#if CIMD2_TRACE
- debug("bb.sms.cimd2", 0, "After CIMD2 encoding:");
- octstr_dump(text, 0);
-#endif
- packet_add_sms_parm(packet, P_USER_DATA, text);
+ packet_add_sms_parm(packet, P_USER_DATA, text, conn);
}
if (dcs != 0)
- packet_add_int_parm(packet, P_DATA_CODING_SCHEME, dcs);
+ packet_add_int_parm(packet, P_DATA_CODING_SCHEME, dcs, conn);
if (truncated > 0) {
- warning(0, "CIMD2: truncating message text to fit "
- "in %d characters.", spaceleft);
+ warning(0, "CIMD2[%s]: truncating message text to fit in %d characters.",
+ octstr_get_cstr(conn->id),
+ spaceleft);
}
octstr_destroy(text);
@@ -1376,55 +1429,60 @@
/***************************************************************************/
/* Give this packet a proper sequence number for sending. */
-static void packet_set_send_sequence(struct packet *packet, SMSCenter *smsc)
+static void packet_set_send_sequence(struct packet *packet, PrivData *pdata)
{
- gw_assert(smsc != NULL);
+ gw_assert(pdata != NULL);
/* Send sequence numbers are always odd, receiving are always even */
- gw_assert(smsc->cimd2_send_seq % 2 == 1);
+ gw_assert(pdata->send_seq % 2 == 1);
- packet_set_sequence(packet, smsc->cimd2_send_seq);
- smsc->cimd2_send_seq += 2;
- if (smsc->cimd2_send_seq > 256)
- smsc->cimd2_send_seq = 1;
+ packet_set_sequence(packet, pdata->send_seq);
+ pdata->send_seq += 2;
+ if (pdata->send_seq > 256)
+ pdata->send_seq = 1;
}
-static struct packet *cimd2_get_packet(SMSCenter *smsc, Octstr **ts)
+static struct packet *cimd2_get_packet(PrivData *pdata, Octstr **ts)
{
struct packet *packet = NULL;
- gw_assert(smsc != NULL);
+ gw_assert(pdata != NULL);
/* If packet is already available, don't try to read anything */
- packet = packet_extract(smsc->cimd2_inbuffer);
+ packet = packet_extract(pdata->inbuffer, pdata->conn);
while (packet == NULL) {
- if (read_available(smsc->socket, RESPONSE_TIMEOUT) != 1) {
- warning(0, "CIMD2 SMSCenter is not responding");
+ if (read_available(pdata->socket, RESPONSE_TIMEOUT) != 1) {
+ warning(0, "CIMD2[%s]: SMSC is not responding",
+ octstr_get_cstr(pdata->conn->id));
return NULL;
}
- if (octstr_append_from_socket(smsc->cimd2_inbuffer, smsc->socket) <= 0) {
- error(0, "cimd2_get_packet: read failed");
+ if (octstr_append_from_socket(pdata->inbuffer, pdata->socket) <= 0) {
+ error(0, "CIMD2[%s]: cimd2_get_packet: read failed",
+ octstr_get_cstr(pdata->conn->id));
return NULL;
}
- packet = packet_extract(smsc->cimd2_inbuffer);
+ packet = packet_extract(pdata->inbuffer, pdata->conn);
}
- packet_check(packet);
- packet_check_can_receive(packet);
- if(ts)
- *ts = packet_get_parm(packet,P_MC_TIMESTAMP);
+ packet_check(packet,pdata->conn);
+ packet_check_can_receive(packet,pdata->conn);
+ debug("bb.sms.cimd2", 0, "CIMD2[%s]: received: <%s>",
+ octstr_get_cstr(pdata->conn->id),
+ octstr_get_cstr(packet->data));
+ if (ts)
+ *ts = packet_get_parm(packet,P_MC_TIMESTAMP);
- if (smsc->keepalive > 0)
- smsc->cimd2_next_ping = time(NULL) + 60 * smsc->keepalive;
+ if (pdata->keepalive > 0)
+ pdata->next_ping = time(NULL) + pdata->keepalive;
return packet;
}
/* Acknowledge a request. The CIMD 2 spec only defines positive responses
* to the server, because the server is perfect. */
-static void cimd2_send_response(struct packet *request, SMSCenter *smsc)
+static void cimd2_send_response(struct packet *request, PrivData *pdata)
{
struct packet *response;
@@ -1436,12 +1494,12 @@
/* Don't check errors here because if there is something
* wrong with the socket, the main loop will detect it. */
- octstr_write_to_socket(smsc->socket, response->data);
+ octstr_write_to_socket(pdata->socket, response->data);
packet_destroy(response);
}
-static Msg *cimd2_accept_message(struct packet *request)
+static Msg *cimd2_accept_message(struct packet *request, SMSCConn *conn)
{
Msg *message = NULL;
Octstr *destination = NULL;
@@ -1452,9 +1510,9 @@
/* See GSM 03.38. The bit patterns we can handle are:
* 000xyyxx Uncompressed text, yy indicates alphabet.
- * yy = 00, default alphabet
+ * yy = 00, default alphabet
* yy = 01, 8-bit data
- * yy = 10, UCS2 (can't handle yet)
+ * yy = 10, UCS2
* yy = 11, reserved
* 1111xyxx Data, y indicates alphabet.
* y = 0, default alphabet
@@ -1468,27 +1526,21 @@
/* Text is either in User Data or User Data Binary field. */
text = packet_get_sms_parm(request, P_USER_DATA);
if (text != NULL) {
-#if CIMD2_TRACE
- debug("bb.sms.cimd2", 0, "CIMD2 received message. Text:");
- octstr_dump(text, 0);
-#endif
- convert_cimd2_to_gsm(text);
- charset_gsm_to_latin1(text);
-#if CIMD2_TRACE
- debug("bb.sms.cimd", 0, "Text in latin1:");
- octstr_dump(text, 0);
-#endif
+ convert_cimd2_to_gsm(text,conn);
+ /*charset = charset_gsm; This overrides what we got from the DCS */
} else {
+ /*
+ * FIXME: If DCS indicates GSM charset, and we get it in binary,
+ * then it's probably bit-packed. We'll have to undo it because
+ * our "charset_gsm" means one gsm character per octet. This is
+ * not currently supported. -- RB
+ */
text = packet_get_hex_parm(request, P_USER_DATA_BINARY);
-#if CIMD2_TRACE
- debug("bb.sms.cimd2", 0, "CIMD2 received message. Text:");
- octstr_dump(text, 0);
-#endif
}
/* Code elsewhere in the gateway always expects the sender and
* receiver fields to be filled, so we discard messages that
- * lack them. If they should not be discarded, then the code
+ * lack them. If they should not be discarded, then the code
* handling sms messages should be reviewed. -- RB */
if (!destination || octstr_len(destination) == 0) {
info(0, "CIMD2: Got SMS without receiver, discarding.");
@@ -1528,29 +1580,30 @@
}
/* Deal with a request from the CIMD2 server, and acknowledge it. */
-static void cimd2_handle_request(struct packet *request, SMSCenter *smsc)
+static void cimd2_handle_request(struct packet *request, SMSCConn *conn)
{
+ PrivData *pdata = conn->data;
Msg *message = NULL;
/* TODO: Check if the sequence number of this request is what we
* expected. */
if (request->operation == DELIVER_STATUS_REPORT) {
- message = cimd2_accept_delivery_report_message(request,smsc);
+ message = cimd2_accept_delivery_report_message(request, conn);
if (message)
- list_append(smsc->cimd2_received, message);
+ list_append(pdata->received, message);
}
else if (request->operation == DELIVER_MESSAGE) {
- message = cimd2_accept_message(request);
+ message = cimd2_accept_message(request,conn);
if (message)
- list_append(smsc->cimd2_received, message);
+ list_append(pdata->received, message);
}
- cimd2_send_response(request, smsc);
+ cimd2_send_response(request, pdata);
}
/* Send a request and wait for the ack. If the other side responds with
- * an error code, attempt to correct and retry.
+ * an error code, attempt to correct and retry.
* If other packets arrive while we wait for the ack, handle them.
*
* Return -1 if the SMSC refused the request. Return -2 for other
@@ -1563,67 +1616,78 @@
* TODO: This function has grown large and complex. Break it up
* into smaller pieces.
*/
-static int cimd2_request(struct packet *request, SMSCenter *smsc, Octstr **ts)
+static int cimd2_request(struct packet *request, SMSCConn *conn, Octstr **ts)
{
+ PrivData *pdata = conn->data;
int ret;
struct packet *reply = NULL;
int errorcode;
int tries = 0;
- gw_assert(smsc != NULL);
+ gw_assert(pdata != NULL);
gw_assert(request != NULL);
gw_assert(operation_can_send(request->operation));
- if (smsc->socket < 0) {
- warning(0, "cimd2_request: socket not open.");
+ debug("bb.sms.cimd2", 0, "CIMD2[%s]: sending <%s>",
+ octstr_get_cstr(conn->id),
+ octstr_get_cstr(request->data));
+
+ if (pdata->socket < 0) {
+ warning(0, "CIMD2[%s]: cimd2_request: socket not open.",
+ octstr_get_cstr(conn->id));
goto io_error;
}
retransmit:
- packet_set_send_sequence(request, smsc);
+ packet_set_send_sequence(request, pdata);
packet_set_checksum(request);
- ret = octstr_write_to_socket(smsc->socket, request->data);
+ ret = octstr_write_to_socket(pdata->socket, request->data);
if (ret < 0)
goto io_error;
next_reply:
- reply = cimd2_get_packet(smsc, ts);
+ packet_destroy(reply); // destroy old, if any
+ reply = cimd2_get_packet(pdata, ts);
if (!reply)
goto io_error;
- errorcode = packet_display_error(reply);
+ errorcode = packet_display_error(reply,conn);
if (reply->operation == NACK) {
- warning(0, "CIMD2 received NACK");
+ warning(0, "CIMD2[%s]: received NACK",
+ octstr_get_cstr(conn->id));
octstr_dump(reply->data, 0);
/* Correct sequence number if server says it was wrong,
* but only if server's number is sane. */
if (reply->seq != request->seq && (reply->seq % 1) == 1) {
- warning(0, "correcting sequence number from %ld to %ld.",
- (long) smsc->cimd2_send_seq, (long) reply->seq);
- smsc->cimd2_send_seq = reply->seq;
+ warning(0, "CIMD2[%s]: correcting sequence number from %ld to %ld.",
+ octstr_get_cstr(conn->id),
+ (long) pdata->send_seq,
+ (long) reply->seq);
+ pdata->send_seq = reply->seq;
}
goto retry;
}
if (reply->operation == GENERAL_ERROR_RESPONSE) {
- error(0, "CIMD2 received general error response");
+ error(0, "CIMD2[%s]: received general error response",
+ octstr_get_cstr(conn->id));
goto io_error;
}
/* The server sent us a request. Handle it, then wait for
* a new reply. */
if (reply->operation < RESPONSE) {
- cimd2_handle_request(reply, smsc);
- packet_destroy(reply);
+ cimd2_handle_request(reply, conn);
goto next_reply;
}
if (reply->seq != request->seq) {
/* We got a response to a different request number than
* what we send. Strange. */
- warning(0, "CIMD2: response had unexpected sequence number; ignoring.\n");
+ warning(0, "CIMD2[%s]: response had unexpected sequence number; ignoring.",
+ octstr_get_cstr(conn->id));
goto next_reply;
}
@@ -1631,9 +1695,11 @@
/* We got a response that didn't match our request */
Octstr *request_name = operation_name(request->operation);
Octstr *reply_name = operation_name(reply->operation);
- warning(0, "CIMD2: %s request got a %s",
+ warning(0, "CIMD2[%s]: %s request got a %s",
+ octstr_get_cstr(conn->id),
octstr_get_cstr(request_name),
octstr_get_cstr(reply_name));
+
octstr_destroy(request_name);
octstr_destroy(reply_name);
octstr_dump(reply->data, 0);
@@ -1658,239 +1724,185 @@
retry:
if (++tries < 3) {
- warning(0, "Retransmitting (take %d)", tries);
+ warning(0, "CIMD2[%s]: Retransmitting (take %d)",
+ octstr_get_cstr(conn->id),
+ tries);
goto retransmit;
}
- warning(0, "Giving up.");
+ warning(0, "CIMD2[%s]: Giving up.",
+ octstr_get_cstr(conn->id));
goto io_error;
}
/* Close the SMSC socket without fanfare. */
-static void cimd2_close_socket(SMSCenter *smsc)
+static void cimd2_close_socket(PrivData *pdata)
{
- gw_assert(smsc != NULL);
+ gw_assert(pdata != NULL);
- if (smsc->socket < 0)
+ if (pdata->socket < 0)
return;
- if (close(smsc->socket) < 0)
- warning(errno, "error closing CIMD2 socket");
- smsc->socket = -1;
+ if (close(pdata->socket) < 0)
+ warning(errno, "CIMD2[%s]: error closing socket",
+ octstr_get_cstr(pdata->conn->id));
+ pdata->socket = -1;
}
/* Open a socket to the SMSC, send a login packet, and wait for ack.
* This may block. Return 0 for success, or -1 for failure. */
/* Make sure the socket is closed before calling this function, otherwise
* we will leak fd's. */
-static int cimd2_login(SMSCenter *smsc)
+static int cimd2_login(SMSCConn *conn)
{
+ PrivData *pdata = conn->data;
int ret;
struct packet *packet = NULL;
- gw_assert(smsc != NULL);
+ gw_assert(pdata != NULL);
- if (smsc->socket >= 0) {
- warning(0, "cimd2_login: socket was already open; closing");
- cimd2_close_socket(smsc);
+ if (pdata->socket >= 0) {
+ warning(0, "CIMD2[%s]: login: socket was already open; closing",
+ octstr_get_cstr(conn->id));
+ cimd2_close_socket(pdata);
}
- smsc->socket = tcpip_connect_to_server(octstr_get_cstr(smsc->cimd2_hostname),
- smsc->cimd2_port, NULL);
- /* XXX add interface_name if required */
- if (smsc->socket == -1)
- goto error;
-
- packet = packet_create(LOGIN, BOGUS_SEQUENCE);
- packet_add_string_parm(packet, P_USER_IDENTITY, smsc->cimd2_username);
- packet_add_string_parm(packet, P_PASSWORD, smsc->cimd2_password);
-
- ret = cimd2_request(packet, smsc, NULL);
- if (ret < 0)
- goto error;
-
- packet_destroy(packet);
- info(0, "%s logged in.", smsc_name(smsc));
-
- return 0;
-
-error:
- error(0, "cimd2_login failed");
- cimd2_close_socket(smsc);
+ pdata->socket = tcpip_connect_to_server_with_port(
+ octstr_get_cstr(pdata->host),
+ pdata->port,
+ pdata->our_port,
+ (conn->our_host ? octstr_get_cstr(conn->our_host) : NULL));
+ if (pdata->socket != -1) {
+
+ packet = packet_create(LOGIN, BOGUS_SEQUENCE);
+ packet_add_string_parm(packet, P_USER_IDENTITY, pdata->username, conn);
+ packet_add_string_parm(packet, P_PASSWORD, pdata->password, conn);
+
+ ret = cimd2_request(packet, conn, NULL);
+
+ if (ret >= 0) {
+ packet_destroy(packet);
+ info(0, "CIMD2[%s] logged in.",
+ octstr_get_cstr(conn->id));
+ return 0;
+ }
+ }
+ error(0, "CIMD2[%s] login failed.",
+ octstr_get_cstr(conn->id));
+ cimd2_close_socket(pdata);
packet_destroy(packet);
return -1;
}
-static void cimd2_logout(SMSCenter *smsc)
+static void cimd2_logout(SMSCConn *conn)
{
struct packet *packet = NULL;
-
- gw_assert(smsc != NULL);
+ int ret;
packet = packet_create(LOGOUT, BOGUS_SEQUENCE);
+
/* TODO: Don't wait very long for a response in this case. */
- cimd2_request(packet, smsc,NULL);
+ ret = cimd2_request(packet, conn, NULL);
+
+ if (ret == 0) {
+ info(0, "CIMD2[%s] logged out.",
+ octstr_get_cstr(conn->id));
+ }
packet_destroy(packet);
}
-static int cimd2_send_alive(SMSCenter *smsc)
+static int cimd2_send_alive(SMSCConn *conn)
{
struct packet *packet = NULL;
int ret;
- gw_assert(smsc != NULL);
-
packet = packet_create(ALIVE, BOGUS_SEQUENCE);
- ret = cimd2_request(packet, smsc,NULL);
+ ret = cimd2_request(packet, conn, NULL);
packet_destroy(packet);
if (ret < 0)
- warning(0, "CIMD2: SMSC not alive.\n");
+ warning(0, "CIMD2[%s]: SMSC not alive.",
+ octstr_get_cstr(conn->id));
return ret;
}
-/***************************************************************************/
-/* SMSC Interface, as defined in smsc_interface.def */
-/***************************************************************************/
-
-SMSCenter *cimd2_open(Octstr *hostname, int port, Octstr *username,
-Octstr *password, int keepalive, Octstr *sender_prefix)
-{
- SMSCenter *smsc = NULL;
- int maxlen;
-
- smsc = smscenter_construct();
- gw_assert(smsc != NULL);
-
- smsc->type = SMSC_TYPE_CIMD2;
- smsc->keepalive = keepalive;
- smsc->cimd2_hostname = octstr_duplicate(hostname);
- smsc->cimd2_port = port;
- smsc->cimd2_username = octstr_duplicate(username);
- smsc->cimd2_password = octstr_duplicate(password);
- smsc->sender_prefix = octstr_duplicate(sender_prefix);
- sprintf(smsc->name, "CIMD2:%s:%d:%s", octstr_get_cstr(hostname), port, octstr_get_cstr(username));
- smsc->cimd2_received = list_create();
- smsc->cimd2_inbuffer = octstr_create("");
- smsc->cimd2_error = 0;
- if (keepalive > 0)
- smsc->cimd2_next_ping = time(NULL) + keepalive * 60;
-
- maxlen = parm_maxlen(P_USER_IDENTITY);
- if (octstr_len(smsc->cimd2_username) > maxlen) {
- octstr_truncate(smsc->cimd2_username, maxlen);
- warning(0, "Truncating CIMD2 username to %d chars", maxlen);
- }
-
- maxlen = parm_maxlen(P_PASSWORD);
- if (octstr_len(smsc->cimd2_password) > maxlen) {
- octstr_truncate(smsc->cimd2_password, maxlen);
- warning(0, "Truncating CIMD2 password to %d chars", maxlen);
- }
-
- if (cimd2_login(smsc) < 0)
- goto error;
-
- return smsc;
-
-error:
- error(0, "cimd2_open failed");
- smscenter_destruct(smsc);
- return NULL;
-}
-
-int cimd2_reopen(SMSCenter *smsc)
-{
- gw_assert(smsc != NULL);
-
- warning(0, "Attempting to re-open CIMD2 connection");
-
- cimd2_close_socket(smsc);
-
- /* Restore message counters to their default values */
- smsc->cimd2_send_seq = 1;
- smsc->cimd2_receive_seq = 0;
-
- /* Clear leftover input */
- octstr_destroy(smsc->cimd2_inbuffer);
- smsc->cimd2_inbuffer = octstr_create("");
-
- return cimd2_login(smsc);
-}
-int cimd2_close(SMSCenter *smsc)
+static void cimd2_destroy(PrivData *pdata)
{
- int ret;
int discarded;
- gw_assert(smsc != NULL);
-
- debug("bb.sms.cimd2", 0, "Closing CIMD2 SMSC");
-
- if (smsc->socket < 0) {
- warning(0, "cimd2_close: already closed.\n");
- return 0;
- }
-
- cimd2_logout(smsc);
-
- ret = close(smsc->socket);
- smsc->socket = -1;
-
- smsc->cimd2_send_seq = 0;
- smsc->cimd2_receive_seq = 1;
- octstr_destroy(smsc->cimd2_hostname);
- octstr_destroy(smsc->cimd2_username);
- octstr_destroy(smsc->cimd2_password);
- octstr_destroy(smsc->cimd2_inbuffer);
-
- discarded = list_len(smsc->cimd2_received);
- list_destroy(smsc->cimd2_received, msg_destroy_item);
+ if (pdata == NULL)
+ return;
+
+ octstr_destroy(pdata->host);
+ octstr_destroy(pdata->username);
+ octstr_destroy(pdata->password);
+ octstr_destroy(pdata->inbuffer);
+ octstr_destroy(pdata->my_number);
+ discarded = list_len(pdata->received);
if (discarded > 0)
- warning(0, "CIMD2: discarded %d received messages", discarded);
+ warning(0, "CIMD2[%s]: discarded %d received messages",
+ octstr_get_cstr(pdata->conn->id),
+ discarded);
- return ret;
+ list_destroy(pdata->received, msg_destroy_item);
+ list_destroy(pdata->outgoing_queue, NULL);
+ list_destroy(pdata->stopped, NULL);
+
+ gw_free(pdata);
}
-int cimd2_submit_msg(SMSCenter *smsc, Msg *msg)
+
+static int cimd2_submit_msg(SMSCConn *conn, Msg *msg)
{
+ PrivData *pdata = conn->data;
struct packet *packet;
- int ret = 0;
- int tries;
Octstr *ts;
- ts = NULL;
+ int ret;
- gw_assert(smsc != NULL);
+ gw_assert(pdata != NULL);
+ debug("bb.sms.cimd2", 0, "CIMD2[%s]: sending message",
+ octstr_get_cstr(conn->id));
+
+ packet = packet_encode_message(msg, pdata->my_number,conn);
+ if (!packet) {
+ /* This is a protocol error. Does this help? I doubt..
+ * But nevermind that.
+ */
+ bb_smscconn_send_failed(conn, msg, SMSCCONN_FAILED_MALFORMED);
+ return -1;
+ }
- packet = packet_encode_message(msg, smsc->sender_prefix);
- if (!packet)
- return 0; /* We can't signal protocol errors yet */
-
- for (tries = 0; tries < 3; tries++) {
- ret = cimd2_request(packet, smsc,&ts);
- if((ret == 0) && (ts) && (msg->sms.dlr_mask & 0x03))
- {
- dlr_add(smsc->name,
- octstr_get_cstr(ts),
- octstr_get_cstr(msg->sms.sender),
- octstr_get_cstr(msg->sms.receiver),
- octstr_get_cstr(msg->sms.service),
- octstr_get_cstr(msg->sms.dlr_url),
- msg->sms.dlr_mask,
- octstr_get_cstr(msg->sms.boxc_id));
- }
+ ret = cimd2_request(packet, conn, &ts);
+ if((ret == 0) && (ts) && (msg->sms.dlr_mask & 0x03)) {
+
+ dlr_add(octstr_get_cstr(conn->name),
+ octstr_get_cstr(ts),
+ octstr_get_cstr(msg->sms.sender),
+ octstr_get_cstr(msg->sms.receiver),
+ octstr_get_cstr(msg->sms.service),
+ octstr_get_cstr(msg->sms.dlr_url),
+ msg->sms.dlr_mask,
+ octstr_get_cstr(msg->sms.boxc_id));
+ }
octstr_destroy(ts);
- ts = NULL;
- if (ret == 0 || ret == -1)
- break;
- if (cimd2_reopen(smsc) < 0) {
- ret = -1;
- break;
- }
+ packet_destroy(packet);
+
+ if (ret == -1) {
+ bb_smscconn_send_failed(conn, msg, SMSCCONN_FAILED_REJECTED);
+ }
+ else if (ret == -2) {
+ cimd2_close_socket(pdata);
+ bb_smscconn_send_failed(conn, msg, SMSCCONN_FAILED_TEMPORARILY);
+ mutex_lock(conn->flow_mutex);
+ conn->status = SMSCCONN_DISCONNECTED;
+ mutex_unlock(conn->flow_mutex);
+ }
+ else {
+ bb_smscconn_sent(conn,msg);
}
- packet_destroy(packet);
return ret;
}
@@ -1899,112 +1911,103 @@
* Record the error here, and return it in cimd2_receive_msg. Return
* "message available" if there is an error so that cimd2_receive_msg
* is called. */
-int cimd2_pending_smsmessage(SMSCenter *smsc)
+static int cimd2_receive_msg(SMSCConn *conn, Msg **msg)
{
+ PrivData *pdata = conn->data;
long ret;
struct packet *packet;
- gw_assert(smsc != NULL);
- gw_assert(smsc->type == SMSC_TYPE_CIMD2);
+ gw_assert(pdata != NULL);
- if (list_len(smsc->cimd2_received) > 0)
+ if (list_len(pdata->received) > 0) {
+ *msg = list_consume(pdata->received);
return 1;
+ }
- if (smsc->socket < 0) {
+ if (pdata->socket < 0) {
/* XXX We have to assume that smsc_send_message is
* currently trying to reopen, so we have to make
* this thread wait. It should be done in a nicer
- * way. */
+ * way. */
return 0;
}
- ret = read_available(smsc->socket, 0);
+ ret = read_available(pdata->socket, 0);
if (ret == 0) {
- if (smsc->keepalive > 0 && smsc->cimd2_next_ping < time(NULL)) {
- if (cimd2_send_alive(smsc) < 0) {
- smsc->cimd2_error = 1;
- return 1;
- }
+ if (pdata->keepalive > 0 && pdata->next_ping < time(NULL)) {
+ if (cimd2_send_alive(conn) < 0)
+ return -1;
}
return 0;
}
if (ret < 0) {
- warning(errno, "cimd2_pending_smsmessage: read_available failed");
- smsc->cimd2_error = 1;
- return 1;
+ warning(errno, "CIMD2[%s]: cimd2_receive_msg: read_available failed",
+ octstr_get_cstr(conn->id));
+ return -1;
}
/* We have some data waiting... see if it is an sms delivery. */
- ret = octstr_append_from_socket(smsc->cimd2_inbuffer, smsc->socket);
+ ret = octstr_append_from_socket(pdata->inbuffer, pdata->socket);
if (ret == 0) {
- warning(0, "cimd2_pending_smsmessage: service center closed connection.");
- smsc->cimd2_error = 1;
- return 1;
+ warning(0, "CIMD2[%s]: cimd2_receive_msg: service center closed connection.",
+ octstr_get_cstr(conn->id));
+ return -1;
}
if (ret < 0) {
- warning(0, "cimd2_pending_smsmessage: read failed");
- smsc->cimd2_error = 1;
- return 1;
+ warning(0, "CIMD2[%s]: cimd2_receive_msg: read failed",
+ octstr_get_cstr(conn->id));
+ return -1;
}
for (;;) {
- packet = packet_extract(smsc->cimd2_inbuffer);
+ packet = packet_extract(pdata->inbuffer,conn);
if (!packet)
break;
- packet_check(packet);
- packet_check_can_receive(packet);
+ packet_check(packet,conn);
+ packet_check_can_receive(packet,conn);
+ debug("bb.sms.cimd2", 0, "CIMD2[%s]: received: <%s>",
+ octstr_get_cstr(pdata->conn->id),
+ octstr_get_cstr(packet->data));
if (packet->operation < RESPONSE)
- cimd2_handle_request(packet, smsc);
+ cimd2_handle_request(packet, conn);
else {
- error(0, "cimd2_pending_smsmessage: unexpected response packet");
+ error(0, "CIMD2[%s]: cimd2_receive_msg: unexpected response packet",
+ octstr_get_cstr(conn->id));
octstr_dump(packet->data, 0);
}
packet_destroy(packet);
}
- if (list_len(smsc->cimd2_received) > 0)
+ if (list_len(pdata->received) > 0) {
+ *msg = list_consume(pdata->received);
return 1;
-
- return 0;
-}
-
-int cimd2_receive_msg(SMSCenter *smsc, Msg **msg)
-{
- gw_assert(smsc != NULL);
- gw_assert(msg != NULL);
-
- if (smsc->cimd2_error) {
- smsc->cimd2_error = 0;
- return -1;
}
-
- *msg = list_consume(smsc->cimd2_received);
-
- return 1;
+ return 0;
}
-static Msg *cimd2_accept_delivery_report_message(struct packet *request, SMSCenter *smsc)
+static Msg *cimd2_accept_delivery_report_message(struct packet *request,
+ SMSCConn *conn)
{
Msg *msg = NULL;
Octstr *destination = NULL;
Octstr *timestamp = NULL;
Octstr *statuscode = NULL;
- int st_code;
+ int st_code;
int code;
-
+
destination = packet_get_parm(request, P_DESTINATION_ADDRESS);
timestamp = packet_get_parm(request, P_MC_TIMESTAMP);
statuscode = packet_get_parm(request, P_STATUS_CODE);
st_code = atoi(octstr_get_cstr(statuscode));
-
+
switch(st_code)
{
case 2: /* validity period expired */
@@ -2018,9 +2021,9 @@
code = 0;
}
if(code)
- msg = dlr_find(smsc->name,
- octstr_get_cstr(timestamp),
- octstr_get_cstr(destination),
+ msg = dlr_find(octstr_get_cstr(conn->name),
+ octstr_get_cstr(timestamp),
+ octstr_get_cstr(destination),
code);
else
msg = NULL;
@@ -2035,3 +2038,333 @@
return msg;
}
+
+static Msg *sms_receive(SMSCConn *conn)
+{
+ PrivData *pdata = conn->data;
+ int ret;
+ Msg *newmsg = NULL;
+
+ ret = cimd2_receive_msg(conn, &newmsg);
+ if (ret == 1) {
+ /* if any smsc_id available, use it */
+ newmsg->sms.smsc_id = octstr_duplicate(conn->id);
+ return newmsg;
+ }
+ else if (ret == 0) { /* no message, just retry... */
+ return NULL;
+ }
+ /* error. reconnect. */
+ msg_destroy(newmsg);
+ mutex_lock(conn->flow_mutex);
+ cimd2_close_socket(pdata);
+ conn->status = SMSCCONN_DISCONNECTED;
+ mutex_unlock(conn->flow_mutex);
+ return NULL;
+}
+
+
+static void sms_receiver(void *arg)
+{
+ Msg *msg;
+ SMSCConn *conn = arg;
+ PrivData *pdata = conn->data;
+ double sleep = 0.0001;
+
+ /* Make sure we log into our own log-file if defined */
+ log_thread_to(conn->log_idx);
+
+ /* remove messages from SMSC until we are killed */
+ while (!pdata->quitting) {
+
+ list_consume(pdata->stopped); /* block here if suspended/isolated */
+
+ if (conn->status == SMSCCONN_ACTIVE) {
+ msg = sms_receive(conn);
+ if (msg) {
+ debug("bb.sms.cimd2", 0, "CIMD2[%s]: new message received",
+ octstr_get_cstr(conn->id));
+ sleep = 0.0001;
+ bb_smscconn_receive(conn, msg);
+ continue;
+ }
+ }
+ /* note that this implementations means that we sleep even
+ * when we fail connection.. but time is very short, anyway
+ */
+ gwthread_sleep(sleep);
+ /* gradually sleep longer and longer times until something starts to
+ * happen - this of course reduces response time, but that's better than
+ * extensive CPU usage when it is not used
+ */
+ sleep *= 2;
+ if (sleep >= 2.0)
+ sleep = 1.999999;
+ }
+}
+
+
+static void sms_sender(void *arg)
+{
+ Msg *msg;
+ SMSCConn *conn = arg;
+ PrivData *pdata = conn->data;
+ double sleep = 0.00001;
+
+ mutex_lock(conn->flow_mutex);
+ conn->status = SMSCCONN_CONNECTING;
+ mutex_unlock(conn->flow_mutex);
+
+ /* Make sure we log into our own log-file if defined */
+ log_thread_to(conn->log_idx);
+
+ while (!pdata->quitting) {
+
+ list_consume(pdata->stopped); /* block here if suspended/isolated */
+
+ /* check that connection is active */
+ if (conn->status != SMSCCONN_ACTIVE) {
+ if (cimd2_login(conn) != 0) {
+ error(0, "CIMD2[%s]: Couldn't connect to SMSC (retrying in %ld seconds).",
+ octstr_get_cstr(conn->id),
+ conn->reconnect_delay);
+ gwthread_sleep(conn->reconnect_delay);
+ mutex_lock(conn->flow_mutex);
+ conn->status = SMSCCONN_RECONNECTING;
+ mutex_unlock(conn->flow_mutex);
+ continue;
+ }
+ mutex_lock(conn->flow_mutex);
+ conn->status = SMSCCONN_ACTIVE;
+ conn->connect_time = time(NULL);
+ bb_smscconn_connected(conn);
+ mutex_unlock(conn->flow_mutex);
+ }
+
+ if (pdata->quitting)
+ break;
+
+ /* send messages */
+ do {
+ msg = list_extract_first(pdata->outgoing_queue);
+ if (msg) {
+ if (cimd2_submit_msg(conn,msg) != 0) break;
+ }
+ else {
+ gwthread_sleep(sleep);
+ }
+ } while (msg);
+ }
+}
+
+
+
+static int cimd2_add_msg_cb (SMSCConn *conn, Msg *sms)
+{
+ PrivData *pdata = conn->data;
+ Msg *copy;
+
+ copy = msg_duplicate(sms);
+ list_produce(pdata->outgoing_queue, copy);
+ gwthread_wakeup(pdata->sender_thread);
+
+ return 0;
+}
+
+
+static int cimd2_shutdown_cb (SMSCConn *conn, int finish_sending)
+{
+ PrivData *pdata = conn->data;
+
+ debug("bb.sms", 0, "Shutting down SMSCConn CIMD2 %s (%s)",
+ octstr_get_cstr(conn->id),
+ finish_sending ? "slow" : "instant");
+
+ /* Documentation claims this would have been done by smscconn.c,
+ but isn't when this code is being written. */
+ conn->why_killed = SMSCCONN_KILLED_SHUTDOWN;
+ pdata->quitting = 1; /* Separate from why_killed to avoid locking, as
+ * why_killed may be changed from outside? */
+
+ if (finish_sending == 0) {
+ Msg *msg;
+ while ((msg = list_extract_first(pdata->outgoing_queue)) != NULL) {
+ bb_smscconn_send_failed(conn, msg, SMSCCONN_FAILED_SHUTDOWN);
+ }
+ }
+
+ cimd2_logout(conn);
+ if (conn->is_stopped) {
+ list_remove_producer(pdata->stopped);
+ conn->is_stopped = 0;
+ }
+
+ if (pdata->sender_thread != -1) {
+ gwthread_wakeup(pdata->sender_thread);
+ gwthread_join(pdata->sender_thread);
+ }
+
+ if (pdata->receiver_thread != -1) {
+ gwthread_wakeup(pdata->receiver_thread);
+ gwthread_join(pdata->receiver_thread);
+ }
+
+ cimd2_close_socket(pdata);
+ cimd2_destroy(pdata);
+
+ debug("bb.sms", 0, "SMSCConn CIMD2 %s shut down.",
+ octstr_get_cstr(conn->id));
+ conn->status = SMSCCONN_DEAD;
+ bb_smscconn_killed();
+ return 0;
+}
+
+static void cimd2_start_cb (SMSCConn *conn)
+{
+ PrivData *pdata = conn->data;
+
+ list_remove_producer(pdata->stopped);
+ /* in case there are messages in the buffer already */
+ gwthread_wakeup(pdata->receiver_thread);
+ debug("bb.sms", 0, "SMSCConn CIMD2 %s, start called",
+ octstr_get_cstr(conn->id));
+}
+
+static void cimd2_stop_cb (SMSCConn *conn)
+{
+ PrivData *pdata = conn->data;
+ list_add_producer(pdata->stopped);
+ debug("bb.sms", 0, "SMSCConn CIMD2 %s, stop called",
+ octstr_get_cstr(conn->id));
+}
+
+static long cimd2_queued_cb (SMSCConn *conn)
+{
+ PrivData *pdata = conn->data;
+ conn->load = (pdata ? (conn->status != SMSCCONN_DEAD ?
+ list_len(pdata->outgoing_queue) : 0) : 0);
+ return conn->load;
+}
+
+int smsc_cimd2_create(SMSCConn *conn, CfgGroup *grp)
+{
+ PrivData *pdata;
+ int ok;
+ int maxlen;
+
+ pdata = gw_malloc(sizeof(PrivData));
+ conn->data = pdata;
+ pdata->conn = conn;
+
+ pdata->quitting = 0;
+ pdata->socket = -1;
+ pdata->received = list_create();
+ pdata->inbuffer = octstr_create("");
+ pdata->send_seq = 1;
+ pdata->receive_seq = 0;
+ pdata->outgoing_queue = list_create();
+ pdata->stopped = list_create();
+ list_add_producer(pdata->outgoing_queue);
+
+ if (conn->is_stopped)
+ list_add_producer(pdata->stopped);
+
+ pdata->host = cfg_get(grp, octstr_imm("host"));
+ if (cfg_get_integer(&(pdata->port), grp, octstr_imm("port")) == -1)
+ pdata->port = 0;
+ if (cfg_get_integer(&(pdata->our_port), grp, octstr_imm("our-port")) == -1)
+ pdata->our_port = 0;
+ pdata->username = cfg_get(grp, octstr_imm("smsc-username"));
+ pdata->password = cfg_get(grp, octstr_imm("smsc-password"));
+ pdata->my_number = cfg_get(grp, octstr_imm("my-number"));
+ if (cfg_get_integer(&(pdata->keepalive), grp,octstr_imm("keepalive")) == -1)
+ pdata->keepalive = 0;
+
+ /* Check that config is OK */
+ ok = 1;
+ if (pdata->host == NULL) {
+ error(0,"CIMD2[%s]: Configuration file doesn't specify host",
+ octstr_get_cstr(conn->id));
+ ok = 0;
+ }
+ if (pdata->port == 0) {
+ error(0,"CIMD2[%s]: Configuration file doesn't specify port",
+ octstr_get_cstr(conn->id));
+ ok = 0;
+ }
+ if (pdata->username == NULL) {
+ error(0, "CIMD2[%s]: Configuration file doesn't specify username.",
+ octstr_get_cstr(conn->id));
+ ok = 0;
+ }
+ if (pdata->password == NULL) {
+ error(0, "CIMD2[%s]: Configuration file doesn't specify password.",
+ octstr_get_cstr(conn->id));
+ ok = 0;
+ }
+
+ if (!ok) {
+ cimd2_destroy(pdata);
+ return -1;
+ }
+
+ conn->name = octstr_format("CIMD2:%s:%d:%s",
+ octstr_get_cstr(pdata->host),
+ pdata->port,
+ octstr_get_cstr(pdata->username));
+
+
+ if (pdata->keepalive > 0) {
+ debug("bb.sms.cimd2", 0, "CIMD2[%s]: Keepalive set to %ld seconds",
+ octstr_get_cstr(conn->id),
+ pdata->keepalive);
+ pdata->next_ping = time(NULL) + pdata->keepalive;
+ }
+
+ maxlen = parm_maxlen(P_USER_IDENTITY);
+ if (octstr_len(pdata->username) > maxlen) {
+ octstr_truncate(pdata->username, maxlen);
+ warning(0, "CIMD2[%s]: Truncating username to %d chars",
+ octstr_get_cstr(conn->id),
+ maxlen);
+ }
+
+ maxlen = parm_maxlen(P_PASSWORD);
+ if (octstr_len(pdata->password) > maxlen) {
+ octstr_truncate(pdata->password, maxlen);
+ warning(0, "CIMD2[%s]: Truncating password to %d chars",
+ octstr_get_cstr(conn->id),
+ maxlen);
+ }
+
+ pdata->receiver_thread = gwthread_create(sms_receiver, conn);
+ pdata->sender_thread = gwthread_create(sms_sender, conn);
+
+ if ((pdata->receiver_thread == -1) ||
+ (pdata->sender_thread == -1)) {
+
+ error(0,"CIMD2[%s]: Couldn't start I/O threads.",
+ octstr_get_cstr(conn->id));
+ pdata->quitting = 1;
+ if (pdata->sender_thread != -1) {
+ gwthread_wakeup(pdata->sender_thread);
+ gwthread_join(pdata->sender_thread);
+ }
+ if (pdata->receiver_thread != -1) {
+ gwthread_wakeup(pdata->receiver_thread);
+ gwthread_join(pdata->receiver_thread);
+ }
+ cimd2_destroy(pdata);
+ return -1;
+
+ }
+
+ conn->send_msg = cimd2_add_msg_cb;
+ conn->shutdown = cimd2_shutdown_cb;
+ conn->queued = cimd2_queued_cb;
+ conn->start_conn = cimd2_start_cb;
+ conn->stop_conn = cimd2_stop_cb;
+
+ return 0;
+}
+
Index: gw/smsc/smsc_p.h
===================================================================
RCS file: /home/cvs/gateway/gw/smsc/smsc_p.h,v
retrieving revision 1.2
diff -u -r1.2 smsc_p.h
--- gw/smsc/smsc_p.h 28 Jan 2003 00:10:04 -0000 1.2
+++ gw/smsc/smsc_p.h 21 Mar 2003 15:23:47 -0000
@@ -23,7 +23,6 @@
enum {
SMSC_TYPE_DELETED,
SMSC_TYPE_CIMD,
- SMSC_TYPE_CIMD2,
SMSC_TYPE_EMI,
SMSC_TYPE_EMI_X31,
SMSC_TYPE_EMI_IP,
@@ -90,19 +89,6 @@
time_t cimd_last_spoke;
int cimd_config_bits;
- /* CIMD 2 */
- Octstr *cimd2_hostname;
- int cimd2_port;
- Octstr *cimd2_username;
- Octstr *cimd2_password;
- int cimd2_send_seq;
- int cimd2_receive_seq;
- Octstr *cimd2_inbuffer;
- List *cimd2_received;
- int cimd2_error;
- time_t cimd2_next_ping;
- Octstr *sender_prefix;
-
/* EMI */
int emi_fd;
FILE *emi_fp;
@@ -141,6 +127,7 @@
char *ois_buffer;
size_t ois_bufsize;
size_t ois_buflen;
+ Octstr *sender_prefix;
/* AT Commands (wireless modems...) */
char *at_serialdevice;
@@ -207,16 +194,6 @@
int cimd_pending_smsmessage(SMSCenter *smsc);
int cimd_submit_msg(SMSCenter *smsc, Msg *msg);
int cimd_receive_msg(SMSCenter *smsc, Msg **msg);
-
-/*
- * Interface to Nokia SMS centers using CIMD 2.
- */
-SMSCenter *cimd2_open(Octstr *hostname, int port, Octstr *username, Octstr *password, int keepalive, Octstr *sender_prefix);
-int cimd2_reopen(SMSCenter *smsc);
-int cimd2_close(SMSCenter *smsc);
-int cimd2_pending_smsmessage(SMSCenter *smsc);
-int cimd2_submit_msg(SMSCenter *smsc, Msg *msg);
-int cimd2_receive_msg(SMSCenter *smsc, Msg **msg);
/*
* Interface to CMG SMS centers using EMI.