00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082 #include "gwlib/gwlib.h"
00083 #include "msg.h"
00084 #include "smsc_p.h"
00085 #include "smasi_pdu.h"
00086 #include "smscconn_p.h"
00087 #include "bb_smscconn_cb.h"
00088 #include "sms.h"
00089 #include "dlr.h"
00090
00091 #define DEBUG 1
00092
00093 #ifndef DEBUG
00094 static void dump_pdu(const char *msg, Octstr *id, SMASI_PDU *pdu) { }
00095 #else
00096 static void dump_pdu(const char *msg, Octstr *id, SMASI_PDU *pdu)
00097 {
00098 debug("bb.sms.smasi", 0, "SMASI[%s]: %s", octstr_get_cstr(id), msg);
00099 smasi_pdu_dump(pdu);
00100 }
00101 #endif
00102
00103
00104
00105
00106
00107
00108 #define SMASI_DEFAULT_PORT 21500
00109 #define SMASI_DEFAULT_PRIORITY 0
00110 #define MAX_PENDING_SUBMITS 10
00111 #define SMASI_THROTTLING_SLEEP_TIME 15
00112 #define SMASI_ENQUIRE_LINK_INTERVAL 30.0
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125 #define SMASI_OVERRIDE_SOURCE_TON 1
00126 #define SMASI_OVERRIDE_SOURCE_NPI -1
00127 #define SMASI_OVERRIDE_DEST_TON -1
00128 #define SMASI_OVERRIDE_DEST_NPI -1
00129
00130
00131
00132
00133
00134
00135 typedef struct {
00136 SMSCConn * conn;
00137 int thread_handle;
00138 List *msgs_to_send;
00139 Dict *sent_msgs;
00140 List *received_msgs;
00141 Counter *message_id_counter;
00142 Octstr *host;
00143 long port;
00144 Octstr *username;
00145 Octstr * password;
00146 Octstr * my_number;
00147 long source_addr_ton;
00148 long source_addr_npi;
00149 long dest_addr_ton;
00150 long dest_addr_npi;
00151 long priority;
00152 time_t throttling_err_time;
00153 int quitting;
00154 long enquire_link_interval;
00155 int logged_off;
00156 } SMASI;
00157
00158
00159 static SMASI *smasi_create(SMSCConn *conn)
00160 {
00161
00162 SMASI *smasi = gw_malloc(sizeof(SMASI));
00163
00164 smasi->conn = conn;
00165
00166 smasi->thread_handle = -1;
00167 smasi->msgs_to_send = gwlist_create();
00168 smasi->sent_msgs = dict_create(16, NULL);
00169 smasi->received_msgs = gwlist_create();
00170 smasi->message_id_counter = counter_create();
00171 smasi->host = NULL;
00172 smasi->username = NULL;
00173 smasi->password = NULL;
00174 smasi->source_addr_ton = -1;
00175 smasi->source_addr_npi = -1;
00176 smasi->dest_addr_ton = -1;
00177 smasi->dest_addr_npi = -1;
00178 smasi->my_number = NULL;
00179 smasi->port = 21500;
00180 smasi->quitting = 0;
00181 smasi->logged_off = 0;
00182 smasi->priority = 0;
00183 smasi->throttling_err_time = 0;
00184 smasi->enquire_link_interval = 30;
00185
00186 gwlist_add_producer(smasi->msgs_to_send);
00187
00188 return smasi;
00189 }
00190
00191
00192 static void smasi_destroy(SMASI *smasi)
00193 {
00194 if (smasi == NULL) return;
00195
00196 gwlist_destroy(smasi->msgs_to_send, msg_destroy_item);
00197 dict_destroy(smasi->sent_msgs);
00198 gwlist_destroy(smasi->received_msgs, msg_destroy_item);
00199 counter_destroy(smasi->message_id_counter);
00200 octstr_destroy(smasi->host);
00201 octstr_destroy(smasi->username);
00202 octstr_destroy(smasi->password);
00203 gw_free(smasi);
00204 }
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218 static Octstr *colon = NULL;
00219 static Octstr *assign = NULL;
00220 static Octstr *comma = NULL;
00221 static Octstr *cr = NULL;
00222 static Octstr *lf = NULL;
00223
00224
00225
00226
00227
00228
00229 static void escape_data(Octstr *data)
00230 {
00231 long pos = 0;
00232
00233
00234
00235
00236
00237 while (pos < octstr_len(data)) {
00238 Octstr * escaped = NULL;
00239 int check = octstr_get_char(data, pos);
00240
00241 if (check == ':') escaped = colon;
00242 else if (check == '=') escaped = assign;
00243 else if (check == ',') escaped = comma;
00244 else if (check == '\n') escaped = cr;
00245 else if (check == '\r') escaped = lf;
00246
00247 if (escaped != NULL) {
00248
00249
00250
00251
00252 octstr_delete(data, pos, 1);
00253 octstr_insert(data, escaped, pos);
00254 pos += octstr_len(escaped);
00255 } else {
00256
00257 pos++;
00258 }
00259 }
00260 }
00261
00262
00263
00264
00265
00266
00267 static void unescape_data(Octstr *data)
00268 {
00269 long pos = 0;
00270
00271
00272
00273
00274 while (pos < octstr_len(data)) {
00275 int check = octstr_get_char(data, pos);
00276
00277 if (check == ':') {
00278 char byte = 0;
00279 int msb = octstr_get_char(data, pos + 1);
00280 int lsb = octstr_get_char(data, pos + 2);
00281
00282 if (msb == '0') msb = 0;
00283 else if (msb >= '1' && msb <= '9') msb -= '1' + 1;
00284 else msb -= 'a' + 10;
00285
00286 if (lsb == '0') lsb = 0;
00287 else if (lsb >= '1' && lsb <= '9') lsb -= '1' + 1;
00288 else lsb -= 'a' + 10;
00289
00290 byte = msb << 4 | lsb;
00291
00292
00293 octstr_delete(data, pos, 3);
00294 octstr_insert_data(data, pos, &byte, 1);
00295 }
00296 pos++;
00297 }
00298 }
00299
00300
00301
00302
00303
00304
00305 static void encode_binary_data(Octstr *data)
00306 {
00307 Octstr *result = octstr_create("");
00308 long pos = 0;
00309
00310 while (pos < octstr_len(data)) {
00311 int encode = octstr_get_char(data, pos);
00312 int msb = (encode & 0xf0) >> 4;
00313 int lsb = (encode & 0x0f) >> 0;
00314
00315 if (msb == 0) msb = '0';
00316 else if (msb < 10) msb = '1' + msb - 1;
00317 else msb = 'a' + msb - 10;
00318
00319 if (lsb == 0) lsb = '0';
00320 else if (lsb < 10) lsb = '1' + lsb - 1;
00321 else lsb = 'a' + lsb - 10;
00322
00323 octstr_append_char(result, ':');
00324 octstr_append_char(result, msb);
00325 octstr_append_char(result, lsb);
00326
00327 pos++;
00328 }
00329
00330 octstr_delete(data, 0, octstr_len(data));
00331 octstr_append(data, result);
00332 octstr_destroy(result);
00333 }
00334
00335
00336
00337
00338
00339
00340
00341 static void decode_binary_data(Octstr *data)
00342 {
00343 long pos = 0;
00344
00345 while (pos < octstr_len(data)) {
00346 int check = octstr_get_char(data, pos);
00347
00348 if (check == ':') {
00349 Octstr *byte;
00350 int msb = octstr_get_char(data, pos + 1);
00351 int lsb = octstr_get_char(data, pos + 2);
00352
00353 if (msb != -1 && lsb != -1) {
00354 byte = octstr_create("");
00355 octstr_append_char(byte, msb);
00356 octstr_append_char(byte, lsb);
00357
00358 if (octstr_hex_to_binary(byte) != -1) {
00359
00360 octstr_delete(data, pos, 3);
00361 octstr_insert(data, byte, pos);
00362 } else {
00363 error(0, "Malformed binary encoded data.");
00364 }
00365
00366 octstr_destroy(byte);
00367 }
00368 }
00369 pos++;
00370 }
00371 }
00372
00373
00374
00375
00376
00377
00378 static Octstr *get_ton_npi_value(int override, int message)
00379 {
00380 if (override != -1) {
00381 debug("bb.sms.smasi", 0, "SMASI: Manually forced ton or npi to `%d'",
00382 override);
00383 return (octstr_format("%ld", override));
00384 } else {
00385 return (octstr_format("%ld", message));
00386 }
00387 }
00388
00389
00390
00391
00392
00393
00394
00395 static Octstr *get_source_addr_ton(SMASI *smasi, Msg *msg)
00396 {
00397 return get_ton_npi_value(smasi->source_addr_ton,
00398 GSM_ADDR_TON_INTERNATIONAL);
00399 }
00400
00401
00402
00403
00404
00405
00406
00407 static Octstr *get_source_addr_npi(SMASI *smasi, Msg *msg)
00408 {
00409 return get_ton_npi_value(smasi->source_addr_npi,
00410 GSM_ADDR_NPI_E164);
00411 }
00412
00413
00414
00415
00416
00417
00418
00419 static Octstr *get_dest_addr_ton(SMASI *smasi, Msg *msg)
00420 {
00421 return get_ton_npi_value(smasi->dest_addr_ton,
00422 GSM_ADDR_TON_INTERNATIONAL);
00423 }
00424
00425
00426
00427
00428
00429
00430
00431 static Octstr *get_dest_addr_npi(SMASI *smasi, Msg *msg)
00432 {
00433 return get_ton_npi_value(smasi->dest_addr_npi,
00434 GSM_ADDR_NPI_E164);
00435 }
00436
00437
00438
00439
00440
00441
00442 static Octstr *get_originator_type(SMASI *smasi, Octstr *originator)
00443 {
00444
00445 if (octstr_get_char(originator, 0) == '+') {
00446 if (!octstr_check_range(originator, 1, 256, gw_isdigit)) {
00447 return octstr_format("%ld", GSM_ADDR_TON_ALPHANUMERIC);
00448 } else {
00449
00450
00451
00452 octstr_delete(originator, 0, 1);
00453 return octstr_format("%ld", GSM_ADDR_TON_INTERNATIONAL);
00454 }
00455 } else if (!octstr_check_range(originator, 0, 256, gw_isdigit)) {
00456 return octstr_format("%ld", GSM_ADDR_TON_ALPHANUMERIC);
00457 }
00458
00459
00460 return octstr_format("%ld", GSM_ADDR_TON_INTERNATIONAL);
00461 }
00462
00463
00464
00465
00466
00467 static SMASI_PDU *msg_to_pdu(SMASI *smasi, Msg *msg)
00468 {
00469 SMASI_PDU *pdu = smasi_pdu_create(SubmitReq);
00470
00471 pdu->u.SubmitReq.Destination = octstr_duplicate(msg->sms.receiver);
00472 pdu->u.SubmitReq.Body = octstr_duplicate(msg->sms.msgdata);
00473 pdu->u.SubmitReq.Originator = octstr_duplicate(msg->sms.sender);
00474
00475 pdu->u.SubmitReq.OriginatorType =
00476 get_originator_type(smasi, pdu->u.SubmitReq.Originator);
00477
00478 pdu->u.SubmitReq.Sequence =
00479 octstr_format("%ld", counter_increase(smasi->message_id_counter));
00480
00481
00482
00483 if (octstr_get_char(pdu->u.SubmitReq.Destination, 0) == '+')
00484 octstr_delete(pdu->u.SubmitReq.Destination, 0, 1);
00485
00486
00487
00488
00489 pdu->u.SubmitReq.OriginatorType = get_source_addr_ton(smasi, msg);
00490 pdu->u.SubmitReq.OriginatorPlan = get_source_addr_npi(smasi, msg);
00491 pdu->u.SubmitReq.DestinationType = get_dest_addr_ton(smasi, msg);
00492 pdu->u.SubmitReq.DestinationPlan = get_dest_addr_npi(smasi, msg);
00493
00494
00495 if (smasi->priority >= 0 && smasi->priority <= 3) {
00496 pdu->u.SubmitReq.MqPriority = octstr_format("%ld", smasi->priority);
00497 } else {
00498 pdu->u.SubmitReq.MqPriority = octstr_format("%ld", 0);
00499 }
00500
00501
00502 if (msg->sms.coding != DC_UNDEF) {
00503 if (msg->sms.coding == DC_7BIT)
00504 pdu->u.SubmitReq.MsEncoding = octstr_create("7bit");
00505 else if (msg->sms.coding == DC_8BIT)
00506 pdu->u.SubmitReq.MsEncoding = octstr_create("8bit");
00507 else if (msg->sms.coding == DC_UCS2)
00508 pdu->u.SubmitReq.MsEncoding = octstr_create("16bit");
00509
00510
00511 }
00512
00513
00514 if (msg->sms.mclass != MC_UNDEF)
00515 pdu->u.SubmitReq.Class = octstr_format("%ld", msg->sms.mclass);
00516
00517
00518 pdu->u.SubmitReq.ProtocolID = octstr_format("%ld",
00519 (msg->sms.pid == SMS_PARAM_UNDEFINED ? 0 : msg->sms.pid));
00520
00521
00522 if (msg->sms.udhdata && octstr_len(msg->sms.udhdata) > 0) {
00523
00524 pdu->u.SubmitReq.UserDataHeader =
00525 octstr_duplicate(msg->sms.udhdata);
00526
00527 pdu->u.SubmitReq.BodyEncoding =
00528 octstr_create("Data");
00529
00530 if (pdu->u.SubmitReq.MsEncoding)
00531 octstr_destroy(pdu->u.SubmitReq.MsEncoding);
00532
00533 pdu->u.SubmitReq.MsEncoding =
00534 octstr_create("transparent");
00535
00536
00537 encode_binary_data(pdu->u.SubmitReq.UserDataHeader);
00538 encode_binary_data(pdu->u.SubmitReq.Body);
00539 } else {
00540
00541
00542 escape_data(pdu->u.SubmitReq.Body);
00543 }
00544
00545 return pdu;
00546 }
00547
00548
00549
00550
00551
00552 static Msg *pdu_to_msg(SMASI_PDU *pdu)
00553 {
00554 Msg *msg = NULL;
00555
00556 gw_assert(pdu->type == DeliverReq);
00557 gw_assert(pdu->u.DeliverReq.Originator);
00558 gw_assert(pdu->u.DeliverReq.Destination);
00559 gw_assert(pdu->u.DeliverReq.Body);
00560
00561 msg = msg_create(sms);
00562
00563 msg->sms.sender = octstr_duplicate(pdu->u.DeliverReq.Originator);
00564 msg->sms.receiver = octstr_duplicate(pdu->u.DeliverReq.Destination);
00565 msg->sms.msgdata = octstr_duplicate(pdu->u.DeliverReq.Body);
00566
00567
00568 if (pdu->u.DeliverReq.ProtocolId)
00569 if (octstr_parse_long(&msg->sms.pid,
00570 pdu->u.DeliverReq.ProtocolId, 0, 10) == -1)
00571 msg->sms.pid = SMS_PARAM_UNDEFINED;
00572
00573
00574 if (pdu->u.DeliverReq.MsEncoding) {
00575
00576
00577 if (octstr_str_compare(pdu->u.DeliverReq.MsEncoding, "7bit") == 0)
00578 msg->sms.coding = DC_7BIT;
00579 else if (octstr_str_compare(pdu->u.DeliverReq.MsEncoding, "8bit") == 0)
00580 msg->sms.coding = DC_8BIT;
00581 else if (octstr_str_compare(pdu->u.DeliverReq.MsEncoding, "UCS2") == 0)
00582 msg->sms.coding = DC_UCS2;
00583 else if (octstr_str_compare(pdu->u.DeliverReq.MsEncoding, "transparent") == 0)
00584 msg->sms.coding = DC_8BIT;
00585 } else {
00586
00587
00588 if (pdu->u.DeliverReq.UserDataHeader)
00589 msg->sms.coding = DC_8BIT;
00590 else
00591 msg->sms.coding = DC_7BIT;
00592 }
00593
00594
00595 if (msg->sms.coding == DC_8BIT) {
00596
00597 decode_binary_data(msg->sms.msgdata);
00598 if (pdu->u.DeliverReq.UserDataHeader &&
00599 octstr_len(pdu->u.DeliverReq.UserDataHeader) > 0) {
00600 msg->sms.udhdata = octstr_duplicate(pdu->u.DeliverReq.UserDataHeader);
00601 decode_binary_data(msg->sms.udhdata);
00602 }
00603
00604 } else {
00605 unescape_data(msg->sms.msgdata);
00606 }
00607
00608
00609 if (pdu->u.DeliverReq.Class &&
00610 octstr_parse_long(&msg->sms.mclass,
00611 pdu->u.DeliverReq.Class, 0, 10) == -1)
00612 msg->sms.mclass = MC_UNDEF;
00613
00614
00615 if (pdu->u.DeliverReq.ProtocolId &&
00616 octstr_parse_long(&msg->sms.pid,
00617 pdu->u.DeliverReq.ProtocolId, 0, 10) == -1)
00618 msg->sms.pid = SMS_PARAM_UNDEFINED;
00619
00620 return msg;
00621 }
00622
00623
00624
00625
00626
00627
00628 static void send_logoff(SMASI *smasi, Connection *conn)
00629 {
00630 SMASI_PDU *pdu = NULL;
00631 Octstr *os = NULL;
00632
00633 counter_increase(smasi->message_id_counter);
00634
00635 pdu = smasi_pdu_create(LogoffReq);
00636 pdu->u.LogoffReq.Reason = octstr_create("Client shutting down");
00637 dump_pdu("Sending !LogoffReq:", smasi->conn->id, pdu);
00638
00639 os = smasi_pdu_pack(pdu);
00640 conn_write(conn, os);
00641 octstr_destroy(os);
00642 smasi_pdu_destroy(pdu);
00643 }
00644
00645
00646 static void send_enquire_link(SMASI *smasi, Connection *conn, long *last_sent)
00647 {
00648 SMASI_PDU *pdu = NULL;
00649 Octstr *os = NULL;
00650
00651 if (date_universal_now() - *last_sent < smasi->enquire_link_interval)
00652 return;
00653 *last_sent = date_universal_now();
00654
00655 pdu = smasi_pdu_create(EnquireLinkReq);
00656 dump_pdu("Sending EnquireLinkReq:", smasi->conn->id, pdu);
00657 os = smasi_pdu_pack(pdu);
00658 if (os)
00659 conn_write(conn, os);
00660 octstr_destroy(os);
00661 smasi_pdu_destroy(pdu);
00662 }
00663
00664
00665 static int send_pdu(Connection *conn, Octstr *id, SMASI_PDU *pdu)
00666 {
00667 Octstr * os = NULL;
00668 int ret = 0;
00669
00670 dump_pdu("Sending PDU:", id, pdu);
00671 os = smasi_pdu_pack(pdu);
00672 if (os) ret = conn_write(conn, os);
00673 else ret = -1;
00674
00675 octstr_destroy(os);
00676 return ret;
00677 }
00678
00679
00680
00681
00682
00683
00684
00685 static int read_pdu(SMASI *smasi, Connection *conn, SMASI_PDU **pdu)
00686 {
00687 Octstr *os;
00688
00689 os = smasi_pdu_read(conn);
00690 if (os == NULL) {
00691 if (conn_eof(conn) || conn_error(conn))
00692 return -1;
00693 return 0;
00694 }
00695
00696 *pdu = smasi_pdu_unpack(os);
00697 if (*pdu == NULL) {
00698 error(0, "SMASI[%s]: PDU unpacking failed.",
00699 octstr_get_cstr(smasi->conn->id));
00700 debug("bb.sms.smasi", 0, "SMASI[%s]: Failed PDU follows.",
00701 octstr_get_cstr(smasi->conn->id));
00702 octstr_dump(os, 0);
00703 octstr_destroy(os);
00704 return -1;
00705 }
00706 octstr_destroy(os);
00707 return 1;
00708 }
00709
00710
00711 static void handle_pdu(SMASI *smasi, Connection *conn,
00712 SMASI_PDU *pdu, long *pending_submits)
00713 {
00714 SMASI_PDU *resp = NULL;
00715 Msg *msg = NULL;
00716 long reason;
00717
00718 switch (pdu->type) {
00719
00720 case DeliverReq:
00721 msg = pdu_to_msg(pdu);
00722
00723 msg_dump(msg, 0);
00724
00725 if (smasi->my_number && octstr_len(smasi->my_number)) {
00726 octstr_destroy(msg->sms.receiver);
00727 msg->sms.receiver = octstr_duplicate(smasi->my_number);
00728 }
00729
00730 time(&msg->sms.time);
00731 msg->sms.smsc_id = octstr_duplicate(smasi->conn->id);
00732 bb_smscconn_receive(smasi->conn, msg);
00733 resp = smasi_pdu_create(DeliverConf);
00734
00735 if (pdu->u.DeliverReq.Sequence)
00736 resp->u.DeliverConf.Sequence =
00737 octstr_duplicate(pdu->u.DeliverReq.Sequence);
00738
00739 if (pdu->u.DeliverReq.MsgReference)
00740 resp->u.DeliverConf.MsgReference =
00741 octstr_duplicate(pdu->u.DeliverReq.MsgReference);
00742 break;
00743
00744 case SubmitConf:
00745 if (pdu->u.SubmitConf.Sequence) {
00746 msg = dict_remove(smasi->sent_msgs,
00747 pdu->u.SubmitConf.Sequence);
00748 } else {
00749 msg = NULL;
00750 }
00751
00752 if (msg == NULL) {
00753 warning(0, "SMASI[%s]: SMSC sent SubmitConf for unknown message.",
00754 octstr_get_cstr(smasi->conn->id));
00755 } else {
00756 debug("bb.sms.smasi",0,
00757 "SMSC[%s]: SMSC confirmed msg seq <%s> ref <%s>",
00758 octstr_get_cstr(smasi->conn->id),
00759 octstr_get_cstr(pdu->u.SubmitConf.Sequence),
00760 octstr_get_cstr(pdu->u.SubmitConf.MsgReference));
00761
00762 bb_smscconn_sent(smasi->conn, msg, NULL);
00763
00764 --(*pending_submits);
00765 }
00766 break;
00767
00768 case SubmitRej:
00769 if (pdu->u.SubmitRej.Sequence) {
00770 msg = dict_remove(smasi->sent_msgs,
00771 pdu->u.SubmitRej.Sequence);
00772 } else {
00773 msg = NULL;
00774 }
00775
00776 error(0, "SMASI[%s]: SMSC returned error code %s for "
00777 "message ref <%s>", octstr_get_cstr(smasi->conn->id),
00778 octstr_get_cstr(pdu->u.SubmitRej.RejectCode),
00779 octstr_get_cstr(pdu->u.SubmitRej.MsgReference));
00780
00781 if (msg == NULL) {
00782 warning(0, "SMASI[%s]: SMSC sent SubmitRej for unknown message.",
00783 octstr_get_cstr(smasi->conn->id));
00784 } else {
00785 reason = SMSCCONN_FAILED_REJECTED;
00786 bb_smscconn_send_failed(smasi->conn, msg, reason,
00787 octstr_create("REJECTED"));
00788 --(*pending_submits);
00789 }
00790 break;
00791
00792 case LogonConf:
00793 *pending_submits = 0;
00794
00795 smasi->conn->status = SMSCCONN_ACTIVE;
00796 smasi->conn->connect_time = time(NULL);
00797
00798 bb_smscconn_connected(smasi->conn);
00799
00800 info(0, "SMASI[%s]: connection to SMSC established.",
00801 octstr_get_cstr(smasi->conn->id));
00802 break;
00803
00804 case LogonRej:
00805 if (octstr_len(pdu->u.LogonRej.Reason) > 0) {
00806 error(0, "SMASI[%s]: SMSC rejected login with reason <%s>",
00807 octstr_get_cstr(smasi->conn->id),
00808 octstr_get_cstr(pdu->u.LogonRej.Reason));
00809 } else {
00810 error(0, "SMASI[%s]: SMSC rejected login without reason",
00811 octstr_get_cstr(smasi->conn->id));
00812 }
00813 break;
00814
00815 case LogoffConf:
00816 info(0, "SMASI[%s]: SMSC confirmed logoff.",
00817 octstr_get_cstr(smasi->conn->id));
00818 smasi->logged_off = 1;
00819 break;
00820
00821 default:
00822 warning(0, "SMASI[%s]: Unknown PDU type <%s>, ignored.",
00823 octstr_get_cstr(smasi->conn->id), pdu->type_name);
00824 break;
00825 }
00826
00827 if (resp != NULL) {
00828 send_pdu(conn, smasi->conn->id, resp);
00829 smasi_pdu_destroy(resp);
00830 }
00831 }
00832
00833
00834
00835
00836
00837
00838
00839
00840
00841
00842
00843 static Connection *open_connection(SMASI *smasi)
00844 {
00845 Connection *conn = conn_open_tcp_with_port(smasi->host, smasi->port, 0, smasi->conn->our_host);
00846
00847 if (conn == NULL) {
00848 error(0, "SMASI[%s]: Couldn't connect to server.",
00849 octstr_get_cstr(smasi->conn->id));
00850 return NULL;
00851 } else {
00852 SMASI_PDU *logon = smasi_pdu_create(LogonReq);
00853
00854 logon->u.LogonReq.Name = octstr_duplicate(smasi->username);
00855 logon->u.LogonReq.Password = octstr_duplicate(smasi->password);
00856
00857 counter_increase(smasi->message_id_counter);
00858
00859 send_pdu(conn, smasi->conn->id, logon);
00860
00861 smasi_pdu_destroy(logon);
00862 }
00863
00864 return conn;
00865 }
00866
00867
00868 static void send_messages(SMASI *smasi, Connection *conn,
00869 long *pending_submits)
00870 {
00871 double delay = 0;
00872
00873 if (*pending_submits == -1) return;
00874
00875 if (smasi->conn->throughput > 0) {
00876 delay = 1.0 / smasi->conn->throughput;
00877 }
00878
00879 while (*pending_submits < MAX_PENDING_SUBMITS) {
00880 SMASI_PDU *pdu = NULL;
00881
00882 Msg *msg = gwlist_extract_first(smasi->msgs_to_send);
00883
00884 if (msg == NULL) break;
00885
00886
00887 pdu = msg_to_pdu(smasi, msg);
00888
00889 if (pdu->u.SubmitReq.Sequence)
00890 dict_put(smasi->sent_msgs, pdu->u.SubmitReq.Sequence, msg);
00891
00892 send_pdu(conn, smasi->conn->id, pdu);
00893
00894 smasi_pdu_destroy(pdu);
00895
00896
00897 if (smasi->conn->throughput > 0)
00898 gwthread_sleep(delay);
00899
00900 ++(*pending_submits);
00901 }
00902 }
00903
00904
00905
00906
00907
00908
00909
00910
00911 static void smasi_thread(void *arg)
00912 {
00913 long pending_submits;
00914 long len;
00915 SMASI_PDU *pdu;
00916 SMASI *smasi;
00917 int logoff_already_sent = 0;
00918 int ret;
00919 Connection *conn;
00920 long last_enquire_sent;
00921 double timeout;
00922
00923 smasi = arg;
00924
00925
00926 log_thread_to(smasi->conn->log_idx);
00927
00928 while (!smasi->quitting) {
00929
00930 conn = open_connection(smasi);
00931 if (conn == NULL) {
00932 error(0, "SMASI[%s]: Could not connect to SMSC center " \
00933 "(retrying in %ld seconds).",
00934 octstr_get_cstr(smasi->conn->id), smasi->conn->reconnect_delay);
00935
00936 gwthread_sleep(smasi->conn->reconnect_delay);
00937 smasi->conn->status = SMSCCONN_RECONNECTING;
00938 continue;
00939 }
00940
00941 last_enquire_sent = date_universal_now();
00942 pending_submits = -1;
00943 len = 0;
00944
00945 for (;;) {
00946 timeout = last_enquire_sent + smasi->enquire_link_interval
00947 - date_universal_now();
00948
00949
00950 if (conn_wait(conn, timeout) == -1) {
00951 error(0, "SMASI[%s]: I/O error or other error. Re-connecting.",
00952 octstr_get_cstr(smasi->conn->id));
00953 break;
00954 }
00955
00956
00957 if (smasi->quitting && !logoff_already_sent) {
00958 send_logoff(smasi, conn);
00959 logoff_already_sent = 1;
00960 }
00961
00962
00963 send_enquire_link(smasi, conn, &last_enquire_sent);
00964
00965
00966 while ((ret = read_pdu(smasi, conn, &pdu)) == 1) {
00967
00968 dump_pdu("Got PDU:", smasi->conn->id, pdu);
00969
00970
00971 handle_pdu(smasi, conn, pdu, &pending_submits);
00972
00973 smasi_pdu_destroy(pdu);
00974
00975
00976 if (smasi->logged_off) break;
00977
00978
00979 if ((!smasi->throttling_err_time ||
00980 ((time(NULL) - smasi->throttling_err_time) >
00981 SMASI_THROTTLING_SLEEP_TIME
00982 && !(smasi->throttling_err_time = 0))))
00983 send_messages(smasi, conn, &pending_submits);
00984 }
00985
00986
00987 if (ret == -1) {
00988 error(0, "SMASI[%s]: I/O error or other error. Re-connecting.",
00989 octstr_get_cstr(smasi->conn->id));
00990 break;
00991 }
00992
00993
00994 if (smasi->logged_off) break;
00995
00996 if ((!smasi->throttling_err_time ||
00997 ((time(NULL) - smasi->throttling_err_time) >
00998 SMASI_THROTTLING_SLEEP_TIME
00999 && !(smasi->throttling_err_time = 0))))
01000 send_messages(smasi, conn, &pending_submits);
01001
01002 }
01003
01004 conn_destroy(conn);
01005 conn = NULL;
01006 }
01007 }
01008
01009
01010
01011
01012
01013
01014 static long queued_cb(SMSCConn *conn)
01015 {
01016 SMASI *smasi = conn->data;
01017
01018 conn->load = (smasi ? (conn->status != SMSCCONN_DEAD ?
01019 gwlist_len(smasi->msgs_to_send) : 0) : 0);
01020
01021 return conn->load;
01022 }
01023
01024
01025 static int send_msg_cb(SMSCConn *conn, Msg *msg)
01026 {
01027 SMASI *smasi = conn->data;
01028
01029 gwlist_produce(smasi->msgs_to_send, msg_duplicate(msg));
01030 gwthread_wakeup(smasi->thread_handle);
01031
01032 return 0;
01033 }
01034
01035
01036 static int shutdown_cb(SMSCConn *conn, int finish_sending)
01037 {
01038 SMASI *smasi = NULL;
01039
01040 debug("bb.sms.smasi", 0, "Shutting down SMSCConn %s (%s)",
01041 octstr_get_cstr(conn->name), finish_sending ? "slow" : "instant");
01042
01043 conn->why_killed = SMSCCONN_KILLED_SHUTDOWN;
01044
01045 smasi = conn->data;
01046 smasi->quitting = 1;
01047 gwthread_wakeup(smasi->thread_handle);
01048 gwthread_join(smasi->thread_handle);
01049 smasi_destroy(smasi);
01050
01051 debug("bb.sms.smasi", 0, "SMSCConn %s shut down.",
01052 octstr_get_cstr(conn->name));
01053 conn->status = SMSCCONN_DEAD;
01054 bb_smscconn_killed();
01055
01056
01057 octstr_destroy(colon);
01058 octstr_destroy(assign);
01059 octstr_destroy(comma);
01060 octstr_destroy(cr);
01061 octstr_destroy(lf);
01062
01063 return 0;
01064 }
01065
01066
01067
01068
01069
01070
01071
01072
01073 static int init_configuration(SMASI *smasi, CfgGroup *config)
01074 {
01075
01076 smasi->host = cfg_get(config, octstr_imm("host"));
01077 smasi->username = cfg_get(config, octstr_imm("smsc-username"));
01078 smasi->password = cfg_get(config, octstr_imm("smsc-password"));
01079
01080
01081 if (smasi->host == NULL) {
01082 error(0,"SMASI: Configuration file doesn't specify host");
01083 return -1;
01084 }
01085 if (smasi->username == NULL) {
01086 error(0, "SMASI: Configuration file doesn't specify username.");
01087 return -1;
01088 }
01089 if (smasi->password == NULL) {
01090 error(0, "SMASI: Configuration file doesn't specify password.");
01091 return -1;
01092 }
01093
01094
01095 smasi->my_number = cfg_get(config, octstr_imm("my-number"));
01096 if (cfg_get_integer(&smasi->port, config, octstr_imm("port")) == -1)
01097 smasi->port = SMASI_DEFAULT_PORT;
01098 if (cfg_get_integer(&smasi->source_addr_ton, config,
01099 octstr_imm("source-addr-ton")) == -1)
01100 smasi->source_addr_ton = SMASI_OVERRIDE_SOURCE_TON;
01101 if (cfg_get_integer(&smasi->source_addr_npi, config,
01102 octstr_imm("source-addr-npi")) == -1)
01103 smasi->source_addr_npi = SMASI_OVERRIDE_SOURCE_NPI;
01104 if (cfg_get_integer(&smasi->dest_addr_ton, config,
01105 octstr_imm("dest-addr-ton")) == -1)
01106 smasi->dest_addr_ton = SMASI_OVERRIDE_DEST_TON;
01107 if (cfg_get_integer(&smasi->dest_addr_npi, config,
01108 octstr_imm("dest-addr-npi")) == -1)
01109 smasi->dest_addr_npi = SMASI_OVERRIDE_DEST_NPI;
01110 if (cfg_get_integer(&smasi->priority, config,
01111 octstr_imm("priority")) == -1)
01112 smasi->priority = SMASI_DEFAULT_PRIORITY;
01113 if (cfg_get_integer(&smasi->enquire_link_interval, config,
01114 octstr_imm("enquire-link-interval")) == -1)
01115 smasi->enquire_link_interval = SMASI_ENQUIRE_LINK_INTERVAL;
01116
01117
01118 smasi->conn->data = smasi;
01119 smasi->conn->name = octstr_format("SMASI:%S:%d:%S",
01120 smasi->host, smasi->port, smasi->username);
01121
01122 smasi->conn->id = cfg_get(config, octstr_imm("smsc-id"));
01123
01124 if (smasi->conn->id == NULL)
01125 smasi->conn->id = octstr_duplicate(smasi->conn->name);
01126
01127 return 0;
01128 }
01129
01130
01131 int smsc_smasi_create(SMSCConn *conn, CfgGroup *config)
01132 {
01133 SMASI *smasi = NULL;
01134
01135
01136 colon = octstr_create(":3a");
01137 assign = octstr_create(":3d");
01138 comma = octstr_create(":2c");
01139 cr = octstr_create(":0a");
01140 lf = octstr_create(":0d");
01141
01142
01143
01144
01145 smasi = smasi_create(conn);
01146
01147 if (init_configuration(smasi, config) != 0)
01148 panic(0, "SMASI SMSC module configuration invalid.");
01149
01150 conn->status = SMSCCONN_CONNECTING;
01151
01152
01153
01154
01155 smasi->thread_handle = gwthread_create(smasi_thread, smasi);
01156
01157 if (smasi->thread_handle == -1) {
01158 error(0, "SMASI[%s]: Couldn't start SMASI thread.",
01159 octstr_get_cstr(smasi->conn->id));
01160 smasi_destroy(conn->data);
01161 return -1;
01162 }
01163
01164
01165 conn->shutdown = shutdown_cb;
01166 conn->queued = queued_cb;
01167 conn->send_msg = send_msg_cb;
01168
01169 return 0;
01170 }
01171
See file LICENSE for details about the license agreement for using,
modifying, copying or deriving work from this software.