128 #define RESPONSE_TIMEOUT (60 * 1000000) 264 for (i = 0;
parameters[i].name != NULL; i++) {
326 return isdigit(c) || c ==
'+' || c ==
'-';
351 {
"Login",
LOGIN, 1, 0 },
352 {
"Logout",
LOGOUT, 1, 0 },
357 {
"Set parameter",
SET_REQ, 1, 0 },
358 {
"Get parameter",
GET_REQ, 1, 0 },
363 {
"Alive",
ALIVE, 1, 1 },
365 {
"NACK",
NACK, 1, 1 },
375 for (i = 0;
operations[i].name != NULL; i++) {
466 #define STX_str "\02" 467 #define ETX_str "\03" 468 #define TAB_str "\011" 471 #define BOGUS_SEQUENCE 0 557 if (stx >= 0 && stx < etx) {
558 warning(0,
"CIMD2[%s]: packet without end marker",
720 warning(0,
"CIMD2[%s]: packet header in wrong format",
748 warning(0,
"CIMD2[%s]: parameter at offset %ld in wrong format",
761 dlen = len - (dpos - pos);
769 warning(0,
"CIMD2[%s]: packet contains unknown parameter %ld",
776 warning(0,
"CIMD2[%s]: packet has '%s' parameter with length %ld, spec says max %d",
788 warning(0,
"CIMD2[%s]: packet has '%s' parameter with non-integer contents",
795 warning(0,
"CIMD2[%s]: packet has '%s' parameter out of range (value %ld, min %d, max %d)",
804 warning(0,
"CIMD2[%s]: packet has '%s' parameter with non-digit contents",
812 warning(0,
"CIMD2[%s]: packet has '%s' parameter with non phone number contents",
820 warning(0,
"CIMD2[%s]: packet has '%s' parameter with non-hex contents",
826 warning(0,
"CIMD2[%s]: packet has odd-length '%s' parameter",
857 warning(0,
"CIMD2[%s]: packet contains NULs",
874 for ( ; pos >= 0; pos = next) {
888 warning(0,
"CIMD2[%s]: packet checksum in wrong format",
910 warning(0,
"CIMD2[%s]: SMSC sent us %s request",
925 { 1,
"Unexpected operation" },
926 { 2,
"Syntax error" },
927 { 3,
"Unsupported parameter error" },
928 { 4,
"Connection to message center lost" },
929 { 5,
"No response from message center" },
930 { 6,
"General system error" },
931 { 7,
"Cannot find information" },
932 { 8,
"Parameter formatting error" },
933 { 9,
"Requested operation failed" },
935 { 100,
"Invalid login" },
936 { 101,
"Incorrect access type" },
937 { 102,
"Too many users with this login id" },
938 { 103,
"Login refused by message center" },
940 { 300,
"Incorrect destination address" },
941 { 301,
"Incorrect number of destination addresses" },
942 { 302,
"Syntax error in user data parameter" },
943 { 303,
"Incorrect bin/head/normal user data parameter combination" },
944 { 304,
"Incorrect data coding scheme parameter usage" },
945 { 305,
"Incorrect validity period parameters usage" },
946 { 306,
"Incorrect originator address usage" },
947 { 307,
"Incorrect pid paramterer usage" },
948 { 308,
"Incorrect first delivery parameter usage" },
949 { 309,
"Incorrect reply path usage" },
950 { 310,
"Incorrect status report request parameter usage" },
951 { 311,
"Incorrect cancel enabled parameter usage" },
952 { 312,
"Incorrect priority parameter usage" },
953 { 313,
"Incorrect tariff class parameter usage" },
954 { 314,
"Incorrect service description parameter usage" },
955 { 315,
"Incorrect transport type parameter usage" },
956 { 316,
"Incorrect message type parameter usage" },
957 { 318,
"Incorrect mms parameter usage" },
958 { 319,
"Incorrect operation timer parameter usage" },
960 { 400,
"Incorrect address parameter usage" },
961 { 401,
"Incorrect scts parameter usage" },
963 { 500,
"Incorrect scts parameter usage" },
964 { 501,
"Incorrect mode parameter usage" },
965 { 502,
"Incorrect parameter combination" },
967 { 600,
"Incorrect scts parameter usage" },
968 { 601,
"Incorrect address parameter usage" },
969 { 602,
"Incorrect mode parameter usage" },
970 { 603,
"Incorrect parameter combination" },
972 { 800,
"Changing password failed" },
973 { 801,
"Changing password not allowed" },
975 { 900,
"Unsupported item requested" },
1010 error(0,
"CIMD2[%s]: %s contained error message:",
1097 for (pos = 0; pos < len; pos++) {
1107 else if (c ==
'_' && pos + 2 < len) {
1116 warning(0,
"CIMD2[%s]: Encountered unknown " 1117 "escape code _%c%c, ignoring.",
1139 for (pos = 0; pos < len; pos++) {
1160 }
else if (c == 2) {
1179 char minpacket[
sizeof(
"sOO:SSSte")];
1194 char parmh[
sizeof(
"tPPP:")];
1205 warning(0,
"CIMD2[%s]: %s parameter too long, truncating from " 1206 "%ld to %ld characters",
1219 sprintf(parmh,
TAB_str "%03d:", parmno);
1268 sprintf(buf,
"%ld", value);
1298 sprintf(buf,
"%02X", checksum);
1310 sprintf(buf,
"%03d",
seq);
1327 int setvalidity = 0;
1335 if (
msg->sms.sender == NULL)
1339 warning(0,
"CIMD2[%s]: non-digits in destination phone number '%s', discarded",
1371 warning(0,
"CIMD2[%s]: Sending message with originating address <%s>, " 1372 "which does not start with the sender-prefix.",
1396 long val = (
msg->sms.validity - time(NULL)) / 60;
1399 if (val >= 50400 && val <= 635040)
1400 setvalidity = (val - 1) / 7 / 24 / 60 + 192 + 1;
1401 if (val > 43200 && val < 50400)
1403 if (val >= 2880 && val <= 43200)
1404 setvalidity = (val - 1) / 24 / 60 + 166 + 1;
1405 if (val > 1440 && val < 2880)
1407 if (val >= 750 && val <= 1440)
1408 setvalidity = (val - 720 - 1) / 30 + 143 + 1;
1409 if (val > 720 && val < 750)
1411 if (val >= 5 && val <= 720)
1412 setvalidity = (val - 1) / 5 - 1 + 1;
1431 warning(0,
"CIMD2[%s]: dlr request make no sense while no-dlr set to true",
1437 if (
msg->sms.rpi > 0)
1447 if (
msg->sms.pid > 0)
1454 if (
msg->sms.msg_left > 0)
1468 spaceleft = spaceleft * 8 / 7;
1475 warning(0,
"CIMD2[%s]: message filled up with UDH, no room for message text",
1496 if (truncated > 0) {
1497 warning(0,
"CIMD2[%s]: truncating message text to fit in %d characters.",
1537 warning(0,
"CIMD2[%s]: SMSC is not responding",
1543 error(0,
"CIMD2[%s]: cimd2_get_packet: read failed",
1553 debug(
"bb.sms.cimd2", 0,
"CIMD2[%s]: received: <%s>",
1577 debug(
"bb.sms.cimd2", 0,
"CIMD2[%s]: sending <%s>",
1590 Msg *message = NULL;
1591 Octstr *destination = NULL;
1631 if (!destination ||
octstr_len(destination) == 0) {
1632 info(0,
"CIMD2[%s]: Got SMS without receiver, discarding.",
1637 info(0,
"CIMD2[%s]: Got SMS without sender, discarding.",
1643 info(0,
"CIMD2[%s]: Got empty SMS, ignoring.",
1651 debug(
"bb.sms.cimd2", 0,
"CIMD2[%s]: Invalid DCS",
1655 time(&message->sms.time);
1656 message->sms.sender = origin;
1657 message->sms.receiver = destination;
1659 message->sms.udhdata = UDH;
1661 message->sms.msgdata =
text;
1677 Msg *message = NULL;
1681 warning(0,
"CIMD2[%s]: request had same sequence number as previous.",
1731 warning(0,
"CIMD2[%s]: cimd2_request: socket not open.",
1740 debug(
"bb.sms.cimd2", 0,
"CIMD2[%s]: sending <%s>",
1757 warning(0,
"CIMD2[%s]: received NACK",
1763 warning(0,
"CIMD2[%s]: correcting sequence number from %ld to %ld.",
1773 error(0,
"CIMD2[%s]: received general error response",
1788 warning(0,
"CIMD2[%s]: response had unexpected sequence number; ignoring.",
1797 warning(0,
"CIMD2[%s]: %s request got a %s",
1826 warning(0,
"CIMD2[%s]: Retransmitting (take %d)",
1831 warning(0,
"CIMD2[%s]: Giving up.",
1844 if (close(pdata->
socket) < 0)
1845 warning(errno,
"CIMD2[%s]: error closing socket",
1862 if (pdata->
socket >= 0) {
1863 warning(0,
"CIMD2[%s]: login: socket was already open; closing",
1873 if (pdata->
socket != -1) {
1883 info(0,
"CIMD2[%s] logged in.",
1888 error(0,
"CIMD2[%s] login failed.",
1906 info(0,
"CIMD2[%s] logged out.",
1922 warning(0,
"CIMD2[%s]: SMSC not alive.",
1944 warning(0,
"CIMD2[%s]: discarded %d received messages",
1964 debug(
"bb.sms.cimd2", 0,
"CIMD2[%s]: sending message",
1988 else if (ret == -2) {
2033 warning(errno,
"CIMD2[%s]: cimd2_receive_msg: read_available failed",
2042 warning(0,
"CIMD2[%s]: cimd2_receive_msg: service center closed connection.",
2047 warning(0,
"CIMD2[%s]: cimd2_receive_msg: read failed",
2060 debug(
"bb.sms.cimd2", 0,
"CIMD2[%s]: received: <%s>",
2067 error(0,
"CIMD2[%s]: cimd2_receive_msg: unexpected response packet",
2088 Octstr *destination = NULL;
2089 Octstr *timestamp = NULL;
2090 Octstr *statuscode = NULL;
2126 if (!
msg->sms.msgdata) {
2127 msg->sms.msgdata = statuscode;
2151 else if (ret == 0) {
2169 double sleep = 0.0001;
2182 error(0,
"CIMD2[%s]: Couldn't connect to SMSC (retrying in %ld seconds).",
2203 debug(
"bb.sms.cimd2", 0,
"CIMD2[%s]: new message received",
2256 debug(
"bb.sms", 0,
"Shutting down SMSCConn CIMD2 %s (%s)",
2258 finish_sending ?
"slow" :
"instant");
2266 if (finish_sending == 0) {
2287 debug(
"bb.sms", 0,
"SMSCConn CIMD2 %s shut down.",
2301 debug(
"bb.sms", 0,
"SMSCConn CIMD2 %s, start called",
2309 debug(
"bb.sms", 0,
"SMSCConn CIMD2 %s, stop called",
2327 pdata = gw_malloc(
sizeof(
PrivData));
2360 if (pdata->
host == NULL) {
2361 error(0,
"CIMD2[%s]: Configuration file doesn't specify host",
2365 if (pdata->
port == 0) {
2366 error(0,
"CIMD2[%s]: Configuration file doesn't specify port",
2371 error(0,
"CIMD2[%s]: Configuration file doesn't specify username.",
2376 error(0,
"CIMD2[%s]: Configuration file doesn't specify password.",
2393 debug(
"bb.sms.cimd2", 0,
"CIMD2[%s]: Keepalive set to %ld seconds",
2402 warning(0,
"CIMD2[%s]: Truncating username to %d chars",
2410 warning(0,
"CIMD2[%s]: Truncating password to %d chars",
2419 error(0,
"CIMD2[%s]: Couldn't start I/O thread.",
void error(int err, const char *fmt,...)
void info(int err, const char *fmt,...)
int octstr_write_to_socket(int socket, Octstr *ostr)
Msg * msg_duplicate(Msg *msg)
void bb_smscconn_connected(SMSCConn *conn)
static Octstr * packet_get_sms_parm(struct packet *packet, int parmno)
static int packet_check_parameter(struct packet *packet, long pos, long len, SMSCConn *conn)
static Octstr * packet_get_hex_parm(struct packet *packet, int parmno)
int smsc_cimd2_create(SMSCConn *conn, CfgGroup *grp)
gw_assert(wtls_machine->packet_to_send !=NULL)
void gwlist_append(List *list, void *item)
void bb_smscconn_killed(void)
static void cimd2_send_response(struct packet *request, PrivData *pdata)
int octstr_check_range(Octstr *ostr, long pos, long len, octstr_func_t filter)
static void packet_add_parm(struct packet *packet, int parmtype, int parmno, Octstr *value, SMSCConn *conn)
void gwlist_produce(List *list, void *item)
void gwthread_join(long thread)
long gwlist_len(List *list)
static Octstr * packet_get_parm(struct packet *packet, int parmno)
int octstr_append_from_socket(Octstr *ostr, int socket)
static int parm_valid_address(Octstr *value)
static void packet_parse_header(struct packet *packet)
void(* stop_conn)(SMSCConn *conn)
static void packet_check_can_receive(struct packet *packet, SMSCConn *conn)
static int parm_maxlen(int parmno)
void charset_utf8_to_gsm(Octstr *ostr)
static void cimd2_handle_request(struct packet *request, SMSCConn *conn)
#define cfg_get(grp, varname)
static struct packet * cimd2_get_packet(PrivData *pdata, Octstr **ts)
static Octstr * packet_get_address_parm(struct packet *packet, int parmno)
static void packet_set_checksum(struct packet *packet)
static void packet_add_sms_parm(struct packet *packet, int parmno, Octstr *value, SMSCConn *conn)
static int cimd2_shutdown_cb(SMSCConn *conn, int finish_sending)
void octstr_append_cstr(Octstr *ostr, const char *cstr)
void octstr_insert_data(Octstr *ostr, long pos, const char *data, long len)
Msg * dlr_find(const Octstr *smsc, const Octstr *ts, const Octstr *dst, int typ, int use_dst)
void dlr_add(const Octstr *smsc, const Octstr *ts, Msg *msg, int use_dst)
#define octstr_get_cstr(ostr)
#define octstr_copy(ostr, from, len)
void octstr_binary_to_hex(Octstr *ostr, int uppercase)
void(* start_conn)(SMSCConn *conn)
long octstr_search_char(const Octstr *ostr, int ch, long pos)
void log_thread_to(int idx)
static void cimd2_logout(SMSCConn *conn)
static int packet_display_error(struct packet *packet, SMSCConn *conn)
static void io_thread(void *arg)
static const char * parm_name(int parmno)
static int operation_can_send(int operation)
static void packet_set_send_sequence(struct packet *packet, PrivData *pdata)
static Msg * cimd2_accept_delivery_report_message(struct packet *request, SMSCConn *conn)
smscconn_killed_t why_killed
static void cimd2_close_socket(PrivData *pdata)
static int cimd2_request(struct packet *request, SMSCConn *conn, Octstr **ts)
static long packet_get_int_parm(struct packet *packet, int parmno)
void msg_destroy_item(void *msg)
Octstr * octstr_imm(const char *cstr)
static void packet_set_sequence(struct packet *packet, int seq)
void octstr_insert(Octstr *ostr1, const Octstr *ostr2, long pos)
static int parm_index(int parmno)
static void cimd2_start_cb(SMSCConn *conn)
static int cimd2_send_alive(SMSCConn *conn)
void * gwlist_extract_first(List *list)
void octstr_delete(Octstr *ostr1, long pos, long len)
void gwlist_remove_producer(List *list)
static int cimd2_receive_msg(SMSCConn *conn, Msg **msg)
long bb_smscconn_receive(SMSCConn *conn, Msg *sms)
static Octstr * operation_name(int operation)
static int isphonedigit(int c)
static void packet_add_string_parm(struct packet *packet, int parmno, Octstr *value, SMSCConn *conn)
int octstr_ncompare(const Octstr *ostr1, const Octstr *ostr2, long n)
static const struct @19 parameters[]
#define octstr_duplicate(ostr)
#define octstr_dump(ostr, level,...)
static void packet_add_hex_parm(struct packet *packet, int parmno, Octstr *value, SMSCConn *conn)
void msg_destroy(Msg *msg)
static Msg * sms_receive(SMSCConn *conn)
void warning(int err, const char *fmt,...)
int tcpip_connect_to_server_with_port(char *hostname, int port, int our_port, const char *source_addr)
Octstr * octstr_format(const char *fmt,...)
void octstr_destroy(Octstr *ostr)
#define gwthread_create(func, arg)
#define octstr_create(cstr)
static int parm_type(int parmno)
int fields_to_dcs(Msg *msg, int mode)
void gwthread_sleep(double seconds)
static Msg * cimd2_accept_message(struct packet *request, SMSCConn *conn)
volatile sig_atomic_t is_stopped
static int operation_can_receive(int operation)
static void packet_add_int_parm(struct packet *packet, int parmno, long value, SMSCConn *conn)
static void packet_destroy(struct packet *packet)
long octstr_len(const Octstr *ostr)
static void convert_gsm_to_cimd2(Octstr *text)
int cfg_get_bool(int *n, CfgGroup *grp, Octstr *varname)
void bb_smscconn_sent(SMSCConn *conn, Msg *sms, Octstr *reply)
static int packet_check(struct packet *packet, SMSCConn *conn)
void * gwlist_consume(List *list)
static int cimd2_add_msg_cb(SMSCConn *conn, Msg *sms)
int read_available(int fd, long wait_usec)
int(* shutdown)(SMSCConn *conn, int finish_sending)
static void packet_add_address_parm(struct packet *packet, int parmno, Octstr *value, SMSCConn *conn)
static int parm_in_range(int parmno, long value)
void debug(const char *place, int err, const char *fmt,...)
int cfg_get_integer(long *n, CfgGroup *grp, Octstr *varname)
static int cimd2_submit_msg(SMSCConn *conn, Msg *msg)
void gwthread_wakeup(long thread)
static long cimd2_queued_cb(SMSCConn *conn)
#define MSG_PARAM_UNDEFINED
static struct @21 cimd2_errors[]
int octstr_hex_to_binary(Octstr *ostr)
long octstr_parse_long(long *nump, Octstr *ostr, long pos, int base)
long(* queued)(SMSCConn *conn)
void octstr_truncate(Octstr *ostr, int new_len)
int(* send_msg)(SMSCConn *conn, Msg *msg)
void bb_smscconn_send_failed(SMSCConn *conn, Msg *sms, int reason, Octstr *reply)
int dcs_to_fields(Msg **msg, int dcs)
static const struct @22 cimd_combinations[]
static void convert_cimd2_to_gsm(Octstr *text, SMSCConn *conn)
static int cimd2_login(SMSCConn *conn)
static struct packet * packet_encode_message(Msg *msg, Octstr *sender_prefix, SMSCConn *conn)
static struct packet * packet_parse(Octstr *packet_data)
void gwlist_add_producer(List *list)
static int response(List *push_headers, Octstr **username, Octstr **password)
int octstr_get_char(const Octstr *ostr, long pos)
static struct packet * packet_extract(Octstr *in, SMSCConn *conn)
void octstr_set_char(Octstr *ostr, long pos, int ch)
static int packet_check_header(struct packet *packet, SMSCConn *conn)
static int operation_find(int operation)
#define DLR_IS_SUCCESS_OR_FAIL(dlr)
static XMLRPCDocument * msg
int charset_gsm_truncate(Octstr *gsm, long max)
static void cimd2_destroy(PrivData *pdata)
static void cimd2_stop_cb(SMSCConn *conn)
static Octstr * packet_get_string_parm(struct packet *packet, int parmno)
static void reply(HTTPClient *c, List *push_headers)
static const struct @20 operations[]
int octstr_compare(const Octstr *ostr1, const Octstr *ostr2)
void charset_gsm_to_utf8(Octstr *ostr)
static struct packet * packet_create(int operation, int seq)
void gwlist_destroy(List *list, gwlist_item_destructor_t *destructor)