diff --git a/Makefile.in b/Makefile.in index 11b630a3..5f5aeefd 100644 --- a/Makefile.in +++ b/Makefile.in @@ -3,18 +3,19 @@ PROJS = chan_dongles.so chan_donglem_so_OBJS = app.o at_command.o at_parse.o at_queue.o at_read.o at_response.o \ chan_dongle.o channel.o char_conv.o cli.o helpers.o manager.o \ - memmem.o ringbuffer.o cpvt.o dc_config.o pdu.o mixbuffer.o pdiscovery.o smsdb.o + memmem.o ringbuffer.o cpvt.o dc_config.o pdu.o mixbuffer.o pdiscovery.o error.o smsdb.o chan_dongles_so_OBJS = single.o -test1_OBJS = test/test1.o ringbuffer.o mixbuffer.o -gen_OBJS = test/gen.o char_conv.o pdu.o -parse_OBJS = test/parse.o at_parse.o char_conv.o pdu.o +test1_OBJS = test/test1.o ringbuffer.o mixbuffer.o error.o +gen_OBJS = test/gen.o char_conv.o pdu.o error.o +parse_OBJS = test/parse.o at_parse.o char_conv.o pdu.o error.o discovery_OBJS = tools/discovery.o tools/tty.o SOURCES = app.c at_command.c at_parse.c at_queue.c at_read.c at_response.c \ chan_dongle.c channel.c char_conv.c cli.c cpvt.c dc_config.c helpers.c \ - manager.c memmem.c ringbuffer.c single.c pdu.c mixbuffer.c pdiscovery.c smsdb.c + manager.c memmem.c ringbuffer.c single.c pdu.c mixbuffer.c pdiscovery.c \ + error.c smsdb.c test_SOURCES = test/test1.c test/parse.c test/gen.c tools_SOURCES = tools/discovery.c tools/tty.c @@ -22,7 +23,7 @@ tools_SOURCES = tools/discovery.c tools/tty.c HEADERS = app.h at_command.h at_parse.h at_queue.h at_read.h at_response.h \ chan_dongle.h channel.h char_conv.h cli.h cpvt.h dc_config.h export.h \ helpers.h manager.h memmem.h ringbuffer.h pdu.h mixbuffer.h pdiscovery.h \ - mutils.h smsdb.h + mutils.h error.h smsdb.h tools_HEADERS = tools/tty.h diff --git a/README.md b/README.md index 07f29719..641604c6 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ single quotes.* exten => *#123#,n,Playback(vm-goodbye) exten => *#123#,n,Hangup() - exten => _#X.,1,DongleSendSMS(dongle0,${EXTEN:1},"Please call me",1440,yes) + exten => _#X.,1,DongleSendSMS(dongle0,${EXTEN:1},"Please call me",1440,yes,"magicID") exten => _#X.,n,Answer() exten => _#X.,n,Wait(2) exten => _#X.,n,Playback(vm-goodbye) diff --git a/app.c b/app.c index 3a3ce371..5f18b37c 100644 --- a/app.c +++ b/app.c @@ -22,6 +22,7 @@ #include "app.h" /* app_register() app_unregister() */ #include "chan_dongle.h" /* struct pvt */ #include "helpers.h" /* send_sms() ITEMS_OF() */ +#include "error.h" struct ast_channel; @@ -74,9 +75,6 @@ static int app_status_exec (struct ast_channel* channel, const char* data) static int app_send_sms_exec (attribute_unused struct ast_channel* channel, const char* data) { char* parse; - const char* msg; - int status; - void * msgid; AST_DECLARE_APP_ARGS (args, AST_APP_ARG (device); @@ -84,6 +82,7 @@ static int app_send_sms_exec (attribute_unused struct ast_channel* channel, cons AST_APP_ARG (message); AST_APP_ARG (validity); AST_APP_ARG (report); + AST_APP_ARG (payload); ); if (ast_strlen_zero (data)) @@ -107,18 +106,16 @@ static int app_send_sms_exec (attribute_unused struct ast_channel* channel, cons return -1; } - msg = send_sms(args.device, args.number, args.message, args.validity, args.report, &status, &msgid); - if(!status) - ast_log (LOG_ERROR, "[%s] %s with id %p\n", args.device, msg, msgid); - return !status; + if (send_sms(args.device, args.number, args.message, args.validity, args.report, args.payload, strlen(args.payload) + 1) < 0) { + ast_log(LOG_ERROR, "[%s] %s\n", args.device, error2str(chan_dongle_err)); + return -1; + } + return 0; } static int app_send_ussd_exec(attribute_unused struct ast_channel* channel, const char* data) { char* parse; - const char* msg; - int status; - void* msgid; AST_DECLARE_APP_ARGS(args, AST_APP_ARG(device); @@ -146,12 +143,11 @@ static int app_send_ussd_exec(attribute_unused struct ast_channel* channel, cons return -1; } - msg = send_ussd(args.device, args.ussd, &status, &msgid); - if(!status) - { - ast_log(LOG_ERROR, "[%s] %s with id %p\n", args.device, msg, msgid); + if (send_ussd(args.device, args.ussd) < 0) { + ast_log(LOG_ERROR, "[%s] %s\n", args.device, error2str(chan_dongle_err)); + return -1; } - return !status; + return 0; } @@ -176,13 +172,14 @@ static const struct dongle_application { "DongleSendSMS", app_send_sms_exec, - "DongleSendSMS(Device,Dest,Message,Validity,Report)", - "DongleSendSMS(Device,Dest,Message,Validity,Report)\n" + "DongleSendSMS(Device,Dest,Message,Validity,Report,Payload)", + "DongleSendSMS(Device,Dest,Message,Validity,Report,Payload)\n" " Device - Id of device from dongle.conf\n" " Dest - destination\n" " Message - text of the message\n" " Validity - Validity period in minutes\n" " Report - Boolean flag for report request\n" + " Payload - Unstructured data that will be included in delivery report\n" }, { "DongleSendUSSD", diff --git a/at_command.c b/at_command.c index 39876eb7..c9392851 100644 --- a/at_command.c +++ b/at_command.c @@ -27,6 +27,7 @@ #include "chan_dongle.h" /* struct pvt */ #include "pdu.h" /* build_pdu() */ #include "smsdb.h" +#include "error.h" static const char cmd_at[] = "AT\r"; static const char cmd_chld1x[] = "AT+CHLD=1%d\r"; @@ -34,12 +35,6 @@ static const char cmd_chld2[] = "AT+CHLD=2\r"; static const char cmd_clcc[] = "AT+CLCC\r"; static const char cmd_ddsetex2[] = "AT^DDSETEX=2\r"; -typedef struct csms_part_data -{ - struct cpvt *cpvt; - void **id; -} csms_part_data_t; - /*! * \brief Format and fill generic command * \param cmd -- the command structure @@ -136,10 +131,11 @@ EXPORT_DEF int at_enqueue_initialization(struct cpvt *cpvt, at_cmd_t from_comman static const char cmd17[] = "AT^CVOICE?\r"; // static const char cmd18[] = "AT+CLIP=0\r"; static const char cmd19[] = "AT+CSSN=1,1\r"; + static const char cmd20[] = "AT+CMGF=0\r"; static const char cmd21[] = "AT+CSCS=\"UCS2\"\r"; static const char cmd22[] = "AT+CPMS=\"SM\",\"SM\",\"SM\"\r"; - static const char cmd23[] = "AT+CNMI=2,1,0,0,0\r"; + static const char cmd23[] = "AT+CNMI=2,1,0,2,0\r"; static const char cmd24[] = "AT+CSQ\r"; static const at_queue_cmd_t st_cmds[] = { @@ -166,9 +162,9 @@ EXPORT_DEF int at_enqueue_initialization(struct cpvt *cpvt, at_cmd_t from_comman ATQ_CMD_DECLARE_ST(CMD_AT_CSCA, cmd6), /* Get SMS Service center address */ // ATQ_CMD_DECLARE_ST(CMD_AT_CLIP, cmd18), /* disable Calling line identification presentation in unsolicited response +CLIP: ,[,,[,[][,]] */ ATQ_CMD_DECLARE_ST(CMD_AT_CSSN, cmd19), /* activate Supplementary Service Notification with CSSI and CSSU */ - ATQ_CMD_DECLARE_DYN(CMD_AT_CMGF), /* Set Message Format */ + ATQ_CMD_DECLARE_ST(CMD_AT_CMGF, cmd20), /* Set Message Format */ - ATQ_CMD_DECLARE_STI(CMD_AT_CSCS, cmd21), /* UCS-2 text encoding */ +// ATQ_CMD_DECLARE_STI(CMD_AT_CSCS, cmd21), /* UCS-2 text encoding */ ATQ_CMD_DECLARE_ST(CMD_AT_CPMS, cmd22), /* SMS Storage Selection */ /* pvt->initialized = 1 after successful of CMD_AT_CNMI */ @@ -179,7 +175,6 @@ EXPORT_DEF int at_enqueue_initialization(struct cpvt *cpvt, at_cmd_t from_comman int begin = -1; int err; char * ptmp1 = NULL; - char * ptmp2 = NULL; pvt_t * pvt = cpvt->pvt; at_queue_cmd_t cmds[ITEMS_OF(st_cmds)]; @@ -208,13 +203,6 @@ EXPORT_DEF int at_enqueue_initialization(struct cpvt *cpvt, at_cmd_t from_comman goto failure; ptmp1 = cmds[out].data; } - else if(cmds[out].cmd == CMD_AT_CMGF) - { - err = at_fill_generic_cmd(&cmds[out], "AT+CMGF=0\r"); - if(err) - goto failure; - ptmp2 = cmds[out].data; - } if(cmds[out].cmd == from_command) begin = out; out++; @@ -226,8 +214,6 @@ EXPORT_DEF int at_enqueue_initialization(struct cpvt *cpvt, at_cmd_t from_comman failure: if(ptmp1) ast_free(ptmp1); - if(ptmp2) - ast_free(ptmp2); return err; } @@ -242,12 +228,16 @@ EXPORT_DEF int at_enqueue_cops(struct cpvt *cpvt) static const char cmd[] = "AT+COPS?\r"; static at_queue_cmd_t at_cmd = ATQ_CMD_DECLARE_ST(CMD_AT_COPS, cmd); - return at_queue_insert_const(cpvt, &at_cmd, 1, 0); + if (at_queue_insert_const(cpvt, &at_cmd, 1, 0) != 0) { + chan_dongle_err = E_QUEUE; + return -1; + } + return 0; } /* SMS sending */ -EXPORT_DEF int at_enqueue_pdu(struct cpvt *cpvt, const char *pdu, attribute_unused const char *u1, attribute_unused unsigned u2, attribute_unused int u3, void **id) +static int at_enqueue_pdu(struct cpvt *cpvt, const char *pdu, size_t length, size_t tpdulen, int uid) { char * ptr = (char *) pdu; char buf[8+25+1]; @@ -256,16 +246,6 @@ EXPORT_DEF int at_enqueue_pdu(struct cpvt *cpvt, const char *pdu, attribute_unus { CMD_AT_SMSTEXT, RES_OK, ATQ_CMD_FLAG_DEFAULT, { ATQ_CMD_TIMEOUT_LONG, 0}, NULL, 0 } }; - size_t length = strlen(pdu); - size_t pdulen = length; - - int scalen = pdu_parse_sca(&ptr, &pdulen); - - if(scalen < 2 || length % 2 != 0) - { - return -EINVAL; - } - at_cmd[1].data = ast_malloc(length + 2); if(!at_cmd[1].data) { @@ -276,9 +256,9 @@ EXPORT_DEF int at_enqueue_pdu(struct cpvt *cpvt, const char *pdu, attribute_unus memcpy(at_cmd[1].data, pdu, length); at_cmd[1].data[length] = 0x1A; - at_cmd[1].data[length+1] = 0x0; + at_cmd[1].data[length + 1] = 0x0; - at_cmd[0].length = snprintf(buf, sizeof(buf), "AT+CMGS=%d\r", (int)(pdulen / 2)); + at_cmd[0].length = snprintf(buf, sizeof(buf), "AT+CMGS=%d\r", (int)tpdulen); at_cmd[0].data = ast_strdup(buf); if(!at_cmd[0].data) { @@ -286,50 +266,62 @@ EXPORT_DEF int at_enqueue_pdu(struct cpvt *cpvt, const char *pdu, attribute_unus return -ENOMEM; } -/* ast_debug (5, "[%s] PDU Head '%s'\n", PVT_ID(pvt), buf); - ast_debug (5, "[%s] PDU Body '%s'\n", PVT_ID(pvt), at_cmd[1].data); -*/ - return at_queue_insert_task(cpvt, at_cmd, ITEMS_OF(at_cmd), 0, (struct at_queue_task **)id); + if (at_queue_insert_uid(cpvt, at_cmd, ITEMS_OF(at_cmd), 0, uid) != 0) { + chan_dongle_err = E_QUEUE; + return -1; + } + return 0; } /*! - * \brief Enqueue a partial SMS message + * \brief Enqueue a SMS message * \param cpvt -- cpvt structure * \param number -- the destination of the message * \param msg -- utf-8 encoded message */ -static int at_enqueue_sms_part(const char *buf, unsigned length, void *s) -{ - (void)(length); - - csms_part_data_t *dta = s; - return at_enqueue_pdu(dta->cpvt, buf, NULL, 0, 0, dta->id); -} -EXPORT_DEF int at_enqueue_sms(struct cpvt *cpvt, const char *destination, const char *msg, unsigned validity_minutes, int report_req, void **id) +EXPORT_DEF int at_enqueue_sms(struct cpvt *cpvt, const char *destination, const char *msg, unsigned validity_minutes, int report_req, const char *payload, size_t payload_len) { ssize_t res; pvt_t* pvt = cpvt->pvt; /* set default validity period */ - if(validity_minutes <= 0) + if (validity_minutes <= 0) validity_minutes = 3 * 24 * 60; -/* res = pdu_build(pdu_buf, sizeof(pdu_buf), pvt->sms_scenter, destination, msg, validity_minutes, report_req); -*/ - csms_part_data_t dta = { cpvt, id }; - uint16_t csmsref = smsdb_outgoing_get(PVT_ID(pvt)); - res = pdu_build_mult(at_enqueue_sms_part, "", destination, msg, validity_minutes, report_req, csmsref, &dta); - if(res <= 0) - { - if(res == -E2BIG) - { - ast_verb (3, "[%s] SMS Message too long, PDU has limit 140 octets\n", PVT_ID(pvt)); - ast_log (LOG_WARNING, "[%s] SMS Message too long, PDU has limit 140 octets\n", PVT_ID(pvt)); + + int msg_len = strlen(msg); + uint16_t msg_ucs2[msg_len * 2]; + res = utf8_to_ucs2(msg, msg_len, msg_ucs2, sizeof(msg_ucs2)); + if (res < 0) { + chan_dongle_err = E_PARSE_UTF8; + return -1; + } + + char hexbuf[PDU_LENGTH * 2 + 1]; + + pdu_part_t pdus[255]; + int csmsref = smsdb_get_refid(pvt->imsi, destination); + if (csmsref < 0) { + chan_dongle_err = E_SMSDB; + return -1; + } + res = pdu_build_mult(pdus, "" /* pvt->sms_scenter */, destination, msg_ucs2, res, validity_minutes, !!report_req, csmsref); + if (res < 0) { + /* pdu_build_mult sets chan_dongle_err */ + return -1; + } + int uid = smsdb_outgoing_add(pvt->imsi, destination, res, validity_minutes * 60, report_req, payload, payload_len); + if (uid < 0) { + chan_dongle_err = E_SMSDB; + return -1; + } + for (int i = 0; i < res; ++i) { + hexify(pdus[i].buffer, pdus[i].length, hexbuf); + if (at_enqueue_pdu(cpvt, hexbuf, pdus[i].length * 2, pdus[i].tpdu_length, uid) < 0) { + return -1; } - /* TODO: complain on other errors */ - return res; } - return res; + return 0; } /*! @@ -338,12 +330,11 @@ EXPORT_DEF int at_enqueue_sms(struct cpvt *cpvt, const char *destination, const * \param code the CUSD code to send */ -EXPORT_DEF int at_enqueue_ussd(struct cpvt *cpvt, const char *code, attribute_unused const char *u1, attribute_unused unsigned u2, attribute_unused int u3, void **id) +EXPORT_DEF int at_enqueue_ussd(struct cpvt *cpvt, const char *code) { static const char cmd[] = "AT+CUSD=1,\""; static const char cmd_end[] = "\",15\r"; - at_queue_cmd_t at_cmd = ATQ_CMD_DECLARE_DYN(CMD_AT_CUSD); /* TODO: may be increase timeout ? */ - str_encoding_t cusd_encoding ; + at_queue_cmd_t at_cmd = ATQ_CMD_DECLARE_DYN(CMD_AT_CUSD); ssize_t res; int length; char buf[4096]; @@ -351,30 +342,45 @@ EXPORT_DEF int at_enqueue_ussd(struct cpvt *cpvt, const char *code, attribute_un memcpy (buf, cmd, STRLEN(cmd)); length = STRLEN(cmd); - - if (pvt->cusd_use_7bit_encoding) - cusd_encoding = STR_ENCODING_GSM7_HEX_PAD_0; - else if (pvt->use_ucs2_encoding) - cusd_encoding = STR_ENCODING_UCS2_HEX; - else - cusd_encoding = STR_ENCODING_ASCII; - res = str_encode(cusd_encoding, code, strlen (code), buf + STRLEN(cmd), sizeof (buf) - STRLEN(cmd) - STRLEN(cmd_end) - 1); - if (res <= 0) - { - ast_log (LOG_ERROR, "[%s] Error converting USSD code: %s\n", PVT_ID(pvt), code); + int code_len = strlen(code); + + // use 7 bit encoding. 15 is 00001111 in binary and means 'Language using the GSM 7 bit default alphabet; Language unspecified' accodring to GSM 23.038 + uint16_t code16[code_len * 2]; + uint8_t code_packed[4069]; + res = utf8_to_ucs2(code, code_len, code16, sizeof(code16)); + if (res < 0) { + chan_dongle_err = E_PARSE_UTF8; + return -1; + } + res = gsm7_encode(code16, res, code16); + if (res < 0) { + chan_dongle_err = E_ENCODE_GSM7; return -1; } + res = gsm7_pack(code16, res, code_packed, sizeof(code_packed), 0); + if (res < 0) { + chan_dongle_err = E_PACK_GSM7; + return -1; + } + res = (res + 1) / 2; + hexify(code_packed, res, buf + STRLEN(cmd)); + length += res * 2; - length += res; memcpy(buf + length, cmd_end, STRLEN(cmd_end)+1); length += STRLEN(cmd_end); at_cmd.length = length; at_cmd.data = ast_strdup (buf); - if(!at_cmd.data) + if (!at_cmd.data) { + chan_dongle_err = E_UNKNOWN; return -1; + } - return at_queue_insert_task(cpvt, &at_cmd, 1, 0, (struct at_queue_task **)id); + if (at_queue_insert(cpvt, &at_cmd, 1, 0) != 0) { + chan_dongle_err = E_QUEUE; + return -1; + } + return 0; } @@ -399,7 +405,7 @@ EXPORT_DEF int at_enqueue_dtmf(struct cpvt *cpvt, char digit) case 'B': case 'C': case 'D': - return -1974; + return -1974; // TODO: ??? case '0': case '1': case '2': @@ -424,8 +430,7 @@ EXPORT_DEF int at_enqueue_dtmf(struct cpvt *cpvt, char digit) * \return 0 on success */ -EXPORT_DEF int at_enqueue_set_ccwa(struct cpvt *cpvt, attribute_unused const char *u1, attribute_unused const char *u2, - unsigned call_waiting, attribute_unused int u3, attribute_unused void **u4) +EXPORT_DEF int at_enqueue_set_ccwa(struct cpvt *cpvt, unsigned call_waiting) { static const char cmd_ccwa_get[] = "AT+CCWA=1,2,1\r"; static const char cmd_ccwa_set[] = "AT+CCWA=%d,%d,%d\r"; @@ -445,8 +450,10 @@ EXPORT_DEF int at_enqueue_set_ccwa(struct cpvt *cpvt, attribute_unused const cha value = call_waiting; err = call_waiting == CALL_WAITING_ALLOWED ? 1 : 0; err = at_fill_generic_cmd(&cmds[0], cmd_ccwa_set, err, err, CCWA_CLASS_VOICE); - if(err) - return err; + if (err) { + chan_dongle_err = E_UNKNOWN; + return -1; + } } else { @@ -456,7 +463,11 @@ EXPORT_DEF int at_enqueue_set_ccwa(struct cpvt *cpvt, attribute_unused const cha } CONF_SHARED(cpvt->pvt, callwaiting) = value; - return at_queue_insert(cpvt, pcmd, count, 0); + if (at_queue_insert(cpvt, pcmd, count, 0) != 0) { + chan_dongle_err = E_QUEUE; + return -1; + } + return 0; } /*! @@ -465,13 +476,16 @@ EXPORT_DEF int at_enqueue_set_ccwa(struct cpvt *cpvt, attribute_unused const cha * \return 0 on success */ -EXPORT_DEF int at_enqueue_reset(struct cpvt *cpvt, attribute_unused const char *u1, attribute_unused const char *u2, - attribute_unused unsigned int u3, attribute_unused int u4, attribute_unused void **u5) +EXPORT_DEF int at_enqueue_reset(struct cpvt *cpvt) { static const char cmd[] = "AT+CFUN=1,1\r"; static const at_queue_cmd_t at_cmd = ATQ_CMD_DECLARE_ST(CMD_AT_CFUN, cmd); - return at_queue_insert_const(cpvt, &at_cmd, 1, 0); + if (at_queue_insert_const(cpvt, &at_cmd, 1, 0) != 0) { + chan_dongle_err = E_QUEUE; + return -1; + } + return 0; } @@ -502,8 +516,10 @@ EXPORT_DEF int at_enqueue_dial(struct cpvt *cpvt, const char *number, int clir) if(clir != -1) { err = at_fill_generic_cmd(&cmds[cmdsno], "AT+CLIR=%d\r", clir); - if(err) - return err; + if (err) { + chan_dongle_err = E_UNKNOWN; + return -1; + } tmp = cmds[cmdsno].data; ATQ_CMD_INIT_DYNI(cmds[cmdsno], CMD_AT_CLIR); cmdsno++; @@ -513,7 +529,8 @@ EXPORT_DEF int at_enqueue_dial(struct cpvt *cpvt, const char *number, int clir) if(err) { ast_free(tmp); - return err; + chan_dongle_err = E_UNKNOWN; + return -1; } ATQ_CMD_INIT_DYNI(cmds[cmdsno], CMD_AT_D); @@ -527,12 +544,13 @@ EXPORT_DEF int at_enqueue_dial(struct cpvt *cpvt, const char *number, int clir) cmdsno++; - err = at_queue_insert(cpvt, cmds, cmdsno, 1); -/* set CALL_FLAG_NEED_HANGUP early because ATD may be still in queue while local hangup called */ - if(!err) - CPVT_SET_FLAGS(cpvt, CALL_FLAG_NEED_HANGUP); - - return err; + if (at_queue_insert(cpvt, cmds, cmdsno, 1) != 0) { + chan_dongle_err = E_QUEUE; + return -1; + } + /* set CALL_FLAG_NEED_HANGUP early because ATD may be still in queue while local hangup called */ + CPVT_SET_FLAGS(cpvt, CALL_FLAG_NEED_HANGUP); + return 0; } /*! @@ -545,8 +563,7 @@ EXPORT_DEF int at_enqueue_answer(struct cpvt *cpvt) at_queue_cmd_t cmds[] = { ATQ_CMD_DECLARE_DYN(CMD_AT_A), ATQ_CMD_DECLARE_ST(CMD_AT_DDSETEX, cmd_ddsetex2), - }; - int err; + };\ int count = ITEMS_OF(cmds); const char * cmd1; @@ -568,10 +585,15 @@ EXPORT_DEF int at_enqueue_answer(struct cpvt *cpvt) return -1; } - err = at_fill_generic_cmd(&cmds[0], cmd1, cpvt->call_idx); - if(err == 0) - err = at_queue_insert(cpvt, cmds, count, 1); - return err; + if (at_fill_generic_cmd(&cmds[0], cmd1, cpvt->call_idx) != 0) { + chan_dongle_err = E_UNKNOWN; + return -1; + } + if (at_queue_insert(cpvt, cmds, count, 1) != 0) { + chan_dongle_err = E_QUEUE; + return -1; + } + return 0; } /*! @@ -597,11 +619,15 @@ EXPORT_DEF int at_enqueue_activate(struct cpvt *cpvt) return -1; } - - err = at_fill_generic_cmd(&cmds[0], "AT+CHLD=2%d\r", cpvt->call_idx); - if(err == 0) - err = at_queue_insert(cpvt, cmds, ITEMS_OF(cmds), 1); - return err; + if (at_fill_generic_cmd(&cmds[0], "AT+CHLD=2%d\r", cpvt->call_idx) != 0) { + chan_dongle_err = E_UNKNOWN; + return -1; + } + if (at_queue_insert(cpvt, cmds, ITEMS_OF(cmds), 1) != 0) { + chan_dongle_err = E_QUEUE; + return -1; + } + return 0; } /*! @@ -616,7 +642,11 @@ EXPORT_DEF int at_enqueue_flip_hold(struct cpvt *cpvt) ATQ_CMD_DECLARE_ST(CMD_AT_CLCC, cmd_clcc), }; - return at_queue_insert_const(cpvt, cmds, ITEMS_OF(cmds), 1); + if (at_queue_insert_const(cpvt, cmds, ITEMS_OF(cmds), 1) != 0) { + chan_dongle_err = E_QUEUE; + return -1; + } + return 0; } /*! @@ -630,7 +660,11 @@ EXPORT_DEF int at_enqueue_ping(struct cpvt *cpvt) ATQ_CMD_DECLARE_STIT(CMD_AT, cmd_at, ATQ_CMD_TIMEOUT_SHORT, 0), }; - return at_queue_insert_const(cpvt, cmds, ITEMS_OF(cmds), 1); + if (at_queue_insert_const(cpvt, cmds, ITEMS_OF(cmds), 1) != 0) { + chan_dongle_err = E_QUEUE; + return -1; + } + return 0; } /*! @@ -639,10 +673,13 @@ EXPORT_DEF int at_enqueue_ping(struct cpvt *cpvt) * \param input -- user's command * \return 0 on success */ -EXPORT_DEF int at_enqueue_user_cmd(struct cpvt *cpvt, const char *input, attribute_unused const char *u1, - attribute_unused unsigned u2, attribute_unused int u3, attribute_unused void **u4) +EXPORT_DEF int at_enqueue_user_cmd(struct cpvt *cpvt, const char *input) { - return at_enqueue_generic(cpvt, CMD_USER, 1, "%s\r", input); + if (at_enqueue_generic(cpvt, CMD_USER, 1, "%s\r", input) != 0) { + chan_dongle_err = E_QUEUE; + return -1; + } + return 0; } /*! @@ -662,8 +699,10 @@ EXPORT_DEF int at_enqueue_retrieve_sms(struct cpvt *cpvt, int index, int delete) unsigned cmdsno = ITEMS_OF (cmds); err = at_fill_generic_cmd (&cmds[0], "AT+CMGR=%d\r", index); - if (err) - return err; + if (err) { + chan_dongle_err = E_UNKNOWN; + return -1; + } if (delete) { @@ -671,7 +710,8 @@ EXPORT_DEF int at_enqueue_retrieve_sms(struct cpvt *cpvt, int index, int delete) if(err) { ast_free (cmds[0].data); - return err; + chan_dongle_err = E_UNKNOWN; + return -1; } } else @@ -679,7 +719,11 @@ EXPORT_DEF int at_enqueue_retrieve_sms(struct cpvt *cpvt, int index, int delete) cmdsno--; } - return at_queue_insert (cpvt, cmds, cmdsno, 0); + if (at_queue_insert(cpvt, cmds, cmdsno, 0) != 0) { + chan_dongle_err = E_QUEUE; + return -1; + } + return 0; } /*! @@ -783,8 +827,10 @@ EXPORT_DEF int at_enqueue_hangup(struct cpvt *cpvt, int call_idx) { cmds[0].cmd = CMD_AT_CHLD_1x; err = at_fill_generic_cmd(&cmds[0], cmd_chld1x, call_idx); - if(err) - return err; + if (err) { + chan_dongle_err = E_UNKNOWN; + return -1; + } } } @@ -792,7 +838,11 @@ EXPORT_DEF int at_enqueue_hangup(struct cpvt *cpvt, int call_idx) if(cpvt->state == CALL_STATE_INIT) pvt->last_dialed_cpvt = 0; - return at_queue_insert(cpvt, cmds, ITEMS_OF(cmds), 1); + if (at_queue_insert(cpvt, cmds, ITEMS_OF(cmds), 1) != 0) { + chan_dongle_err = E_QUEUE; + return -1; + } + return 0; } /*! @@ -809,7 +859,12 @@ EXPORT_DEF int at_enqueue_volsync(struct cpvt *cpvt) ATQ_CMD_DECLARE_ST(CMD_AT_CLVL, cmd1), ATQ_CMD_DECLARE_ST(CMD_AT_CLVL, cmd2), }; - return at_queue_insert_const (cpvt, cmds, ITEMS_OF(cmds), 1); + + if (at_queue_insert_const(cpvt, cmds, ITEMS_OF(cmds), 1) != 0) { + chan_dongle_err = E_QUEUE; + return -1; + } + return 0; } /*! @@ -821,7 +876,11 @@ EXPORT_DEF int at_enqueue_clcc(struct cpvt *cpvt) { static const at_queue_cmd_t at_cmd = ATQ_CMD_DECLARE_ST(CMD_AT_CLCC, cmd_clcc); - return at_queue_insert_const(cpvt, &at_cmd, 1, 1); + if (at_queue_insert_const(cpvt, &at_cmd, 1, 1) != 0) { + chan_dongle_err = E_QUEUE; + return -1; + } + return 0; } /*! @@ -837,7 +896,11 @@ EXPORT_DEF int at_enqueue_conference(struct cpvt *cpvt) ATQ_CMD_DECLARE_ST(CMD_AT_CLCC, cmd_clcc), }; - return at_queue_insert_const(cpvt, cmds, ITEMS_OF(cmds), 1); + if (at_queue_insert_const(cpvt, cmds, ITEMS_OF(cmds), 1) != 0) { + chan_dongle_err = E_QUEUE; + return -1; + } + return 0; } diff --git a/at_command.h b/at_command.h index 8ad4feb0..4eb87672 100644 --- a/at_command.h +++ b/at_command.h @@ -152,15 +152,14 @@ EXPORT_DECL const char *at_cmd2str(at_cmd_t cmd); EXPORT_DECL int at_enqueue_initialization(struct cpvt *cpvt, at_cmd_t from_command); EXPORT_DECL int at_enqueue_ping(struct cpvt *cpvt); EXPORT_DECL int at_enqueue_cops(struct cpvt *cpvt); -EXPORT_DECL int at_enqueue_sms(struct cpvt *cpvt, const char *number, const char *msg, unsigned validity_min, int report_req, void **id); -EXPORT_DECL int at_enqueue_pdu(struct cpvt *cpvt, const char *pdu, const char *u1, unsigned u2, int u3, void **id); -EXPORT_DECL int at_enqueue_ussd(struct cpvt *cpvt, const char *code, const char *u1, unsigned u2, int u3, void **id); +EXPORT_DECL int at_enqueue_sms(struct cpvt *cpvt, const char *number, const char *msg, unsigned validity_min, int report_req, const char *payload, size_t payload_len); +EXPORT_DECL int at_enqueue_ussd(struct cpvt *cpvt, const char *code); EXPORT_DECL int at_enqueue_dtmf(struct cpvt *cpvt, char digit); -EXPORT_DECL int at_enqueue_set_ccwa(struct cpvt *cpvt, const char *u1, const char *u2, unsigned call_waiting, int u3, void **u4); -EXPORT_DECL int at_enqueue_reset(struct cpvt *cpvt, const char *u1, const char *u2, unsigned u3, int u4, void **u5); +EXPORT_DECL int at_enqueue_set_ccwa(struct cpvt *cpvt, unsigned call_waiting); +EXPORT_DECL int at_enqueue_reset(struct cpvt *cpvt); EXPORT_DECL int at_enqueue_dial(struct cpvt *cpvt, const char *number, int clir); EXPORT_DECL int at_enqueue_answer(struct cpvt *cpvt); -EXPORT_DECL int at_enqueue_user_cmd(struct cpvt *cpvt, const char *input, const char *u1, unsigned u2, int u3, void **u4); +EXPORT_DECL int at_enqueue_user_cmd(struct cpvt *cpvt, const char *input); EXPORT_DECL int at_enqueue_retrieve_sms(struct cpvt *cpvt, int index, int delete); EXPORT_DECL int at_enqueue_hangup(struct cpvt *cpvt, int call_idx); EXPORT_DECL int at_enqueue_volsync(struct cpvt *cpvt); diff --git a/at_parse.c b/at_parse.c index 42dac006..ee85acb0 100644 --- a/at_parse.c +++ b/at_parse.c @@ -20,6 +20,7 @@ #include "mutils.h" /* ITEMS_OF() */ #include "chan_dongle.h" #include "pdu.h" /* pdu_parse() */ +#include "error.h" #/* */ static unsigned mark_line(char * line, const char * delimiters, char * pointers[]) @@ -283,82 +284,27 @@ EXPORT_DEF int at_parse_cmti (const char* str) return sscanf (str, "+CMTI: %*[^,],%u", &index) == 1 ? index : -1; } +/*! + * \brief Parse a CMSI notification + * \param str -- string to parse (null terminated) + * \param len -- string lenght + * @note str will be modified when the CMTI message is parsed + * \return -1 on error (parse error) or the index of the new sms message + */ -static const char * parse_cmgr_text(char ** str, size_t len, char * oa, size_t oa_len, str_encoding_t * oa_enc, char ** msg, str_encoding_t * msg_enc, pdu_udh_t *udh) +EXPORT_DEF int at_parse_cdsi (const char* str) { - (void)(udh); - /* - * parse cmgr info in the following TEXT format: - * +CMGR: "","+123456789",,timestamp - * - * OK - * or - * +CMGR: "","002B....",,timestamp - * - * OK - */ - - char delimiters[] = ",,,\n"; - char * marks[STRLEN(delimiters)]; - size_t length; - - unsigned count = mark_line(*str, delimiters, marks); - if(count == ITEMS_OF(marks)) - { - /* unquote number */ - marks[0]++; - if(marks[0][0] == '"') - marks[0]++; - if(marks[1][-1] == '"') - marks[1]--; - length = marks[1] - marks[0] + 1; - if(oa_len < length) - return "Not enought space for store number"; - *oa_enc = get_encoding(marks[0], length - 1); - marks[1][0] = 0; - memcpy(oa, marks[0], length); - - *msg = marks[3] + 1; - length = len - (*msg - *str); - *msg_enc = get_encoding(*msg, length); - return NULL; - } - else if(count > 0) - *str = marks[count - 1]; - - return "Can't parse +CMGR response text"; -} + int index; -static const char* parse_cmgr_pdu(char** str, attribute_unused size_t len, char* oa, size_t oa_len, str_encoding_t* oa_enc, char** msg, str_encoding_t* msg_enc, pdu_udh_t *udh) -{ /* - * parse cmgr info in the following PDU format - * +CMGR: message_status,[address_text],TPDU_length - * SMSC_number_and_TPDU - * OK - * - * sample - * +CMGR: 1,,31 - * 07911234567890F3040B911234556780F20008012150220040210C041F04400438043204350442 - * OK + * parse cmti info in the following format: + * +CMTI: , */ - char delimiters[] = ",,\n"; - char * marks[STRLEN(delimiters)]; - char * end; - size_t tpdu_length; + return sscanf (str, "+CDSI: %*[^,],%u", &index) == 1 ? index : -1; +} - if(mark_line(*str, delimiters, marks) == ITEMS_OF(marks)) - { - tpdu_length = strtol(marks[1] + 1, &end, 10); - if(tpdu_length <= 0 || end[0] != '\r') - return "Invalid TPDU length in CMGR PDU status line"; - *str = marks[2] + 1; - return pdu_parse(str, tpdu_length, oa, oa_len, oa_enc, msg, msg_enc, udh); - } - return "Can't parse +CMGR response"; -} /*! * \brief Parse a CMGR message @@ -371,31 +317,105 @@ static const char* parse_cmgr_pdu(char** str, attribute_unused size_t len, char* * \retval -1 parse error */ -EXPORT_DEF const char * at_parse_cmgr(char ** str, size_t len, char * oa, size_t oa_len, str_encoding_t * oa_enc, char ** msg, str_encoding_t * msg_enc, pdu_udh_t *udh) +EXPORT_DEF int at_parse_cmgr(char *str, size_t len, int *tpdu_type, char *sca, size_t sca_len, char *oa, size_t oa_len, char *scts, int *mr, int *st, char *dt, char *msg, size_t *msg_len, pdu_udh_t *udh) { - const char* rv = "Can't parse +CMGR response line"; - /* skip "+CMGR:" */ - *str += 6; + str += 6; len -= 6; /* skip leading spaces */ - while(len > 0 && str[0][0] == ' ') - { - (*str)++; - len--; + while (len > 0 && *str == ' ') { + ++str; + --len; } - if(len > 0) - { - /* check PDU or TEXT mode */ - const char* (*fptr)(char** str, size_t len, char* num, size_t num_len, str_encoding_t * oa_enc, char** msg, str_encoding_t * msg_enc, pdu_udh_t *udh); - fptr = str[0][0] == '"' ? parse_cmgr_text : parse_cmgr_pdu; + if (len <= 0) { + chan_dongle_err = E_PARSE_CMGR_LINE; + return -1; + } + if (str[0] == '"') { + chan_dongle_err = E_DEPRECATED_CMGR_TEXT; + return -1; + } + + + /* + * parse cmgr info in the following PDU format + * +CMGR: message_status,[address_text],TPDU_length + * SMSC_number_and_TPDU + * OK + * + * sample + * +CMGR: 1,,31 + * 07911234567890F3040B911234556780F20008012150220040210C041F04400438043204350442 + * OK + */ - rv = (*fptr)(str, len, oa, oa_len, oa_enc, msg, msg_enc, udh); + char delimiters[] = ",,\n"; + char *marks[STRLEN(delimiters)]; + char *end; + size_t tpdu_length; + int16_t msg16_tmp[256]; + + if (mark_line(str, delimiters, marks) != ITEMS_OF(marks)) { + chan_dongle_err = E_PARSE_CMGR_LINE; } + tpdu_length = strtol(marks[1] + 1, &end, 10); + if (tpdu_length <= 0 || end[0] != '\r') { + chan_dongle_err = E_INVALID_TPDU_LENGTH; + return -1; + } + str = marks[2] + 1; - return rv; + int pdu_length = (unhex(str, str) + 1) / 2; + if (pdu_length < 0) { + chan_dongle_err = E_MALFORMED_HEXSTR; + return -1; + } + int res, i = 0; + res = pdu_parse_sca(str + i, pdu_length - i, sca, sca_len); + if (res < 0) { + /* tpdu_parse_sca sets chan_dongle_err */ + return -1; + } + i += res; + if (tpdu_length > pdu_length - i) { + chan_dongle_err = E_INVALID_TPDU_LENGTH; + return -1; + } + res = tpdu_parse_type(str + i, pdu_length - i, tpdu_type); + if (res < 0) { + /* tpdu_parse_type sets chan_dongle_err */ + return -1; + } + i += res; + switch (PDUTYPE_MTI(*tpdu_type)) { + case PDUTYPE_MTI_SMS_STATUS_REPORT: + res = tpdu_parse_status_report(str + i, pdu_length - i, mr, oa, oa_len, scts, dt, st); + if (res < 0) { + /* tpdu_parse_status_report sets chan_dongle_err */ + return -1; + } + break; + case PDUTYPE_MTI_SMS_DELIVER: + res = tpdu_parse_deliver(str + i, pdu_length - i, *tpdu_type, oa, oa_len, scts, msg16_tmp, udh); + if (res < 0) { + /* tpdu_parse_deliver sets chan_dongle_err */ + return -1; + } + res = ucs2_to_utf8(msg16_tmp, res, msg, res * 2 + 2); + if (res < 0) { + chan_dongle_err = E_PARSE_UCS2; + return -1; + } + *msg_len = res; + msg[res] = '\0'; + break; + default: + chan_dongle_err = E_INVALID_TPDU_TYPE; + return -1; + } + return 0; } /*! diff --git a/at_parse.h b/at_parse.h index 0134a830..2999f274 100644 --- a/at_parse.h +++ b/at_parse.h @@ -1,5 +1,6 @@ /* - Copyright (C) 2010 bg + Copyright (C) 2010 bg + Copyright (C) 2020 Max von Buelow */ #ifndef CHAN_DONGLE_AT_PARSE_H_INCLUDED #define CHAN_DONGLE_AT_PARSE_H_INCLUDED @@ -15,7 +16,8 @@ EXPORT_DECL char* at_parse_cnum (char* str); EXPORT_DECL char* at_parse_cops (char* str); EXPORT_DECL int at_parse_creg (char* str, unsigned len, int* gsm_reg, int* gsm_reg_status, char** lac, char** ci); EXPORT_DECL int at_parse_cmti (const char* str); -EXPORT_DECL const char* at_parse_cmgr (char** str, size_t len, char* oa, size_t oa_len, str_encoding_t* oa_enc, char** msg, str_encoding_t* msg_enc, pdu_udh_t *udh); +EXPORT_DECL int at_parse_cdsi (const char* str); +EXPORT_DECL int at_parse_cmgr(char *str, size_t len, int *tpdu_type, char *sca, size_t sca_len, char *oa, size_t oa_len, char *scts, int *mr, int *st, char *dt, char *msg, size_t *msg_len, pdu_udh_t *udh); EXPORT_DECL int at_parse_cmgs (const char* str); EXPORT_DECL int at_parse_cusd (char* str, int * type, char ** cusd, int * dcs); EXPORT_DECL int at_parse_cpin (char* str, size_t len); diff --git a/at_queue.c b/at_queue.c index c5ac2553..32460d19 100644 --- a/at_queue.c +++ b/at_queue.c @@ -289,12 +289,13 @@ EXPORT_DEF int at_queue_insert_const (struct cpvt * cpvt, const at_queue_cmd_t * } #/* */ -EXPORT_DEF int at_queue_insert_task (struct cpvt * cpvt, at_queue_cmd_t * cmds, unsigned cmdsno, int athead, at_queue_task_t ** task) +EXPORT_DEF int at_queue_insert_uid(struct cpvt * cpvt, at_queue_cmd_t * cmds, unsigned cmdsno, int athead, int uid) { unsigned idx; - task[0] = at_queue_add(cpvt, cmds, cmdsno, athead); + at_queue_task_t *task = at_queue_add(cpvt, cmds, cmdsno, athead); + task->uid = uid; - if(!task[0]) + if(!task) { for(idx = 0; idx < cmdsno; idx++) { @@ -302,18 +303,16 @@ EXPORT_DEF int at_queue_insert_task (struct cpvt * cpvt, at_queue_cmd_t * cmds, } } - if(at_queue_run(cpvt->pvt)) - task[0] = NULL; + if (at_queue_run(cpvt->pvt)) + task = NULL; - return task[0] == NULL; + return task == NULL; } #/* */ EXPORT_DEF int at_queue_insert(struct cpvt * cpvt, at_queue_cmd_t * cmds, unsigned cmdsno, int athead) { - at_queue_task_t * task; - - return at_queue_insert_task(cpvt, cmds, cmdsno, athead, &task); + return at_queue_insert_uid(cpvt, cmds, cmdsno, athead, 0); } diff --git a/at_queue.h b/at_queue.h index 1ea25277..5eaea76a 100644 --- a/at_queue.h +++ b/at_queue.h @@ -80,12 +80,13 @@ typedef struct at_queue_task struct cpvt* cpvt; at_queue_cmd_t cmds[0]; + int uid; } at_queue_task_t; EXPORT_DECL int at_queue_insert_const (struct cpvt * cpvt, const at_queue_cmd_t * cmds, unsigned cmdsno, int athead); EXPORT_DECL int at_queue_insert (struct cpvt * cpvt, at_queue_cmd_t * cmds, unsigned cmdsno, int athead); -EXPORT_DECL int at_queue_insert_task (struct cpvt * cpvt, at_queue_cmd_t * cmds, unsigned cmdsno, int athead, at_queue_task_t ** task); +EXPORT_DECL int at_queue_insert_uid (struct cpvt * cpvt, at_queue_cmd_t * cmds, unsigned cmdsno, int athead, int uid); EXPORT_DECL void at_queue_handle_result (struct pvt * pvt, at_res_t res); EXPORT_DECL void at_queue_flush (struct pvt * pvt); EXPORT_DECL const at_queue_task_t * at_queue_head_task (const struct pvt * pvt); diff --git a/at_response.c b/at_response.c index 6b4f274c..2122c249 100644 --- a/at_response.c +++ b/at_response.c @@ -26,6 +26,7 @@ #include "manager.h" #include "channel.h" /* channel_queue_hangup() channel_queue_control() */ #include "smsdb.h" +#include "error.h" #define DEF_STR(str) str,STRLEN(str) @@ -48,6 +49,7 @@ static const at_response_t at_responses_list[] = { { RES_CMGR, "+CMGR",DEF_STR("+CMGR:") }, { RES_CMS_ERROR, "+CMS ERROR",DEF_STR("+CMS ERROR:") }, { RES_CMTI, "+CMTI",DEF_STR("+CMTI:") }, + { RES_CDSI, "+CDSI",DEF_STR("+CDSI:") }, { RES_CNUM, "+CNUM",DEF_STR("+CNUM:") }, /* and "ERROR+CNUM:" */ { RES_CONF,"^CONF",DEF_STR("^CONF:") }, @@ -252,7 +254,6 @@ static int at_response_ok (struct pvt* pvt, at_res_t res) pvt->outgoing_sms = 0; pvt_try_restate(pvt); - manager_event_sent_notify(PVT_ID(pvt), "SMS", task, "Sent"); /* TODO: move to +CMGS: handler */ ast_verb (3, "[%s] Successfully sent SMS message %p\n", PVT_ID(pvt), task); ast_log (LOG_NOTICE, "[%s] Successfully sent SMS message %p\n", PVT_ID(pvt), task); @@ -263,7 +264,6 @@ static int at_response_ok (struct pvt* pvt, at_res_t res) break; case CMD_AT_CUSD: - manager_event_sent_notify(PVT_ID(pvt), "USSD", task, "Sent"); ast_verb (3, "[%s] Successfully sent USSD %p\n", PVT_ID(pvt), task); ast_log (LOG_NOTICE, "[%s] Successfully sent USSD %p\n", PVT_ID(pvt), task); break; @@ -497,9 +497,29 @@ static int at_response_error (struct pvt* pvt, at_res_t res) pvt->outgoing_sms = 0; pvt_try_restate(pvt); - manager_event_sent_notify(PVT_ID(pvt), "SMS", task, "NotSent"); + { + char payload[SMSDB_PAYLOAD_MAX_LEN]; + char dst[SMSDB_DST_MAX_LEN]; + ssize_t payload_len = smsdb_outgoing_clear(task->uid, dst, payload); + if (payload_len >= 0) { + ast_verb (3, "[%s] Error payload: %.*s\n", PVT_ID(pvt), payload_len, payload); + channel_var_t vars[] = + { + { "SMS_REPORT_PAYLOAD", payload }, + { "SMS_REPORT_TS", "" }, + { "SMS_REPORT_DT", "" }, + { "SMS_REPORT_SUCCESS", "0" }, + { "SMS_REPORT_TYPE", "i" }, + { "SMS_REPORT", "" }, + { NULL, NULL }, + }; + start_local_channel(pvt, "report", dst, vars); + manager_event_report(PVT_ID(pvt), payload, payload_len, "", "", 0, 0, ""); + } + } + ast_verb (3, "[%s] Error sending SMS message %p\n", PVT_ID(pvt), task); - ast_log (LOG_ERROR, "[%s] Error sending SMS message %p\n", PVT_ID(pvt), task); + ast_log (LOG_ERROR, "[%s] Error sending SMS message %p %s\n", PVT_ID(pvt), task, at_cmd2str (ecmd->cmd)); break; case CMD_AT_DTMF: @@ -516,7 +536,6 @@ static int at_response_error (struct pvt* pvt, at_res_t res) break; case CMD_AT_CUSD: - manager_event_sent_notify(PVT_ID(pvt), "USSD", task, "NotSent"); ast_verb (3, "[%s] Error sending USSD %p\n", PVT_ID(pvt), task); ast_log (LOG_ERROR, "[%s] Error sending USSD %p\n", PVT_ID(pvt), task); break; @@ -1182,6 +1201,41 @@ static int at_response_cmti (struct pvt* pvt, const char* str) return 0; } +static int at_response_cdsi (struct pvt* pvt, const char* str) +{ +// FIXME: check format in PDU mode + int index = at_parse_cdsi (str); + + if (CONF_SHARED(pvt, disablesms)) + { + ast_log (LOG_WARNING, "[%s] SMS reception has been disabled in the configuration.\n", PVT_ID(pvt)); + return 0; + } + + if (index > -1) + { + ast_debug (1, "[%s] Incoming SMS message\n", PVT_ID(pvt)); + + if (pvt_enabled(pvt)) + { + if (at_enqueue_retrieve_sms (&pvt->sys_chan, index, CONF_SHARED(pvt, autodeletesms))) + { + ast_log (LOG_ERROR, "[%s] Error sending CMGR to retrieve SMS message\n", PVT_ID(pvt)); + return -1; + } + pvt->incoming_sms = 1; + } + } + else + { + /* Not sure why this happens, but we don't want to disconnect standing calls. + * [Jun 14 19:57:57] ERROR[3056]: at_response.c:1173 at_response_cmti: + * [m1-1] Error parsing incoming sms message alert '+CMTI: "SM",-1' */ + ast_log(LOG_WARNING, "[%s] Error parsing incoming sms message alert '%s', ignoring\n", PVT_ID(pvt), str); + } + + return 0; +} /*! * \brief Handle +CMGR response @@ -1194,115 +1248,106 @@ static int at_response_cmti (struct pvt* pvt, const char* str) static int at_response_cmgr (struct pvt* pvt, const char * str, size_t len) { - char oa[512] = ""; - char* msg = NULL; - str_encoding_t oa_enc; - str_encoding_t msg_enc; - const char* err; - char* err_pos; - char* cmgr; - ssize_t res; - char sms_utf8_str[4096]; - char* number; - char from_number_utf8_str[1024]; + char oa[512] = "", sca[512] = ""; + char scts[64], dt[64]; + int mr, st; + char* msg[4096]; + int res; char text_base64[40800]; size_t msg_len; + int tpdu_type; pdu_udh_t udh; pdu_udh_init(&udh); + char fullmsg[160 * 255]; + int fullmsg_len; + int csms_cnt; + char buf[512]; + char payload[SMSDB_PAYLOAD_MAX_LEN]; + ssize_t payload_len; + int status_report[256]; - const struct at_queue_cmd * ecmd = at_queue_head_cmd (pvt); + const struct at_queue_cmd * ecmd = at_queue_head_cmd(pvt); manager_event_message("DongleNewCMGR", PVT_ID(pvt), str); - if (ecmd) - { - if (ecmd->res == RES_CMGR || ecmd->cmd == CMD_USER) - { + if (ecmd) { + if (ecmd->res == RES_CMGR || ecmd->cmd == CMD_USER) { at_queue_handle_result (pvt, RES_CMGR); pvt->incoming_sms = 0; pvt_try_restate(pvt); - cmgr = err_pos = ast_strdupa (str); - err = at_parse_cmgr (&err_pos, len, oa, sizeof(oa), &oa_enc, &msg, &msg_enc, &udh); // YYY - if (err == (void*)0x1) /* HACK! */ - { - char buf[64]; - int res = (int)(long)msg; /* HACK */ - snprintf(buf, 64, "Delivered\r\nForeignID: %d", res); - manager_event_sent_notify(PVT_ID(pvt), "SMS", 0x0 /* task is popped */, buf); + res = at_parse_cmgr(str, len, &tpdu_type, sca, sizeof(sca), oa, sizeof(oa), scts, &mr, &st, dt, msg, &msg_len, &udh); + if (res < 0) { + ast_log(LOG_WARNING, "[%s] Error parsing incoming message: %s\n", PVT_ID(pvt), error2str(chan_dongle_err)); return 0; } - else if (err) - { - ast_log (LOG_WARNING, "[%s] Error parsing incoming message '%s' at position %d: %s\n", PVT_ID(pvt), str, (int)(err_pos - cmgr), err); - return 0; - } - - ast_debug (1, "[%s] Successfully read SMS message\n", PVT_ID(pvt)); - - /* last chance to define encodings */ - if (oa_enc == STR_ENCODING_UNKNOWN) - oa_enc = pvt->use_ucs2_encoding ? STR_ENCODING_UCS2_HEX : STR_ENCODING_ASCII; - - if (msg_enc == STR_ENCODING_UNKNOWN) - msg_enc = pvt->use_ucs2_encoding ? STR_ENCODING_UCS2_HEX : STR_ENCODING_ASCII; - - /* decode number and message */ - res = str_decode (oa_enc, oa, strlen(oa), from_number_utf8_str, sizeof (from_number_utf8_str), 0, 0); - if (res < 0) - { - ast_log (LOG_ERROR, "[%s] Error decode SMS originator address: '%s', message is '%s'\n", PVT_ID(pvt), oa, str); - number = oa; - return 0; - } - else - number = from_number_utf8_str; - - msg_len = strlen(msg); - res = str_decode (msg_enc, msg, msg_len, sms_utf8_str, sizeof (sms_utf8_str), udh.ls, udh.ss); - if (res < 0) - { - ast_log (LOG_ERROR, "[%s] Error decode SMS text '%s' from encoding %d, message is '%s'\n", PVT_ID(pvt), msg, msg_enc, str); - return 0; - } - else - { - msg = sms_utf8_str; - msg_len = res; - } - - ast_verb (1, "[%s] Got SMS part from %s: '%s' REF: %d %d %d\n", PVT_ID(pvt), number, msg, udh.ref, udh.parts, udh.order); - char fullmsg[40800]; // TODO: ucs-2 - int fullmsg_len; - if (udh.parts > 1) { - int cnt = smsdb_put(pvt->imsi, number, udh.ref, udh.parts, udh.order, msg, fullmsg); - if (cnt <= 0) { - ast_log (LOG_ERROR, "[%s] Error putting SMS to SMSDB\n", PVT_ID(pvt)); - return 0; + switch (PDUTYPE_MTI(tpdu_type)) { + case PDUTYPE_MTI_SMS_STATUS_REPORT: + ast_verb(1, "[%s] Got status report with ref %d from %s and status code %d\n", PVT_ID(pvt), mr, oa, st); + snprintf(buf, 64, "Delivered\r\nForeignID: %d", mr); + payload_len = smsdb_outgoing_part_status(pvt->imsi, oa, mr, st, status_report, payload); + if (payload_len >= 0) { + int success = 1; + char status_report_str[255 * 4 + 1]; + int srroff = 0; + for (int i = 0; status_report[i] != -1; ++i) { + success &= !(status_report[i] & 0x40); + sprintf(status_report_str + srroff, "%03d,", status_report[i]); + srroff += 4; + } + status_report_str[srroff] = '\0'; + ast_verb(1, "[%s] Success: %d; Payload: %.*s; Report string: %s\n", PVT_ID(pvt), success, payload_len, payload, status_report_str); + payload[payload_len] = '\0'; + channel_var_t vars[] = + { + { "SMS_REPORT_PAYLOAD", payload } , + { "SMS_REPORT_TS", scts }, + { "SMS_REPORT_DT", dt }, + { "SMS_REPORT_SUCCESS", success ? "1" : "0" }, + { "SMS_REPORT_TYPE", "e" }, + { "SMS_REPORT", status_report_str }, + { NULL, NULL }, + }; + start_local_channel(pvt, "report", oa, vars); + manager_event_report(PVT_ID(pvt), payload, payload_len, scts, dt, success, 1, status_report_str); } - if (cnt < udh.parts) { - ast_verb (1, "[%s] Waiting for following parts\n", PVT_ID(pvt)); - return 0; + break; + case PDUTYPE_MTI_SMS_DELIVER: + ast_debug (1, "[%s] Successfully read SM\n", PVT_ID(pvt)); + if (udh.parts > 1) { + ast_verb (1, "[%s] Got SM part from %s: '%s'; [ref=%d, parts=%d, order=%d]\n", PVT_ID(pvt), oa, msg, udh.ref, udh.parts, udh.order); + csms_cnt = smsdb_put(pvt->imsi, oa, udh.ref, udh.parts, udh.order, msg, fullmsg); + if (csms_cnt <= 0) { + ast_log(LOG_ERROR, "[%s] Error putting SMS to SMSDB\n", PVT_ID(pvt)); + return 0; + } + if (csms_cnt < udh.parts) { + ast_verb (1, "[%s] Waiting for following parts\n", PVT_ID(pvt)); + return 0; + } + fullmsg_len = strlen(fullmsg); + } else { + ast_verb (1, "[%s] Got signle SM from %s: '%s'\n", PVT_ID(pvt), oa, msg); + strncpy(fullmsg, msg, msg_len); + fullmsg[msg_len] = '\0'; + fullmsg_len = msg_len; } - fullmsg_len = strlen(fullmsg); - } else { - strncpy(fullmsg, msg, msg_len); - fullmsg_len = msg_len; - } - ast_verb (1, "[%s] Got full SMS from %s: '%s'\n", PVT_ID(pvt), number, fullmsg); - ast_base64encode (text_base64, (unsigned char*)fullmsg, fullmsg_len, sizeof(text_base64)); + ast_verb (1, "[%s] Got full SMS from %s: '%s'\n", PVT_ID(pvt), oa, fullmsg); + ast_base64encode (text_base64, (unsigned char*)fullmsg, fullmsg_len, sizeof(text_base64)); - manager_event_new_sms(PVT_ID(pvt), number, fullmsg); - manager_event_new_sms_base64(PVT_ID(pvt), number, text_base64); - { - channel_var_t vars[] = + manager_event_new_sms(PVT_ID(pvt), oa, fullmsg); + manager_event_new_sms_base64(PVT_ID(pvt), oa, text_base64); { - { "SMS", fullmsg } , - { "SMS_BASE64", text_base64 }, - { "CMGR", (char *)str }, - { NULL, NULL }, - }; - start_local_channel (pvt, "sms", number, vars); + channel_var_t vars[] = + { + { "SMS", fullmsg } , + { "SMS_BASE64", text_base64 }, + { "SMS_TS", scts }, + { NULL, NULL }, + }; + start_local_channel (pvt, "sms", oa, vars); + } + break; } } else @@ -1372,7 +1417,6 @@ static int at_response_cusd (struct pvt * pvt, char * str, size_t len) int dcs; char cusd_utf8_str[1024]; char text_base64[16384]; - str_encoding_t ussd_encoding; char typebuf[2]; const char* typestr; @@ -1394,27 +1438,44 @@ static int at_response_cusd (struct pvt * pvt, char * str, size_t len) typebuf[0] = type + '0'; typebuf[1] = 0; - // FIXME: strictly check USSD encoding and detect encoding - if ((dcs == 0 || dcs == 15) && !pvt->cusd_use_ucs2_decoding) - ussd_encoding = STR_ENCODING_GSM7_HEX_PAD_0; - else - ussd_encoding = STR_ENCODING_UCS2_HEX; - res = str_decode (ussd_encoding, cusd, strlen (cusd), cusd_utf8_str, sizeof (cusd_utf8_str), 0, 0); - if(res >= 0) - { - cusd = cusd_utf8_str; + // sanitize DCS + if (dcs & 0x40) { + dcs = (dcs & 0xc) >> 2; + if (dcs == 3) dcs = 0; + } else { + dcs = 0; } - else - { - ast_log (LOG_ERROR, "[%s] Error decode CUSD: %s\n", PVT_ID(pvt), cusd); + + uint16_t out_ucs2[1024]; + ast_verb (1, "[%s] USSD DCS=%d (0: gsm7, 1: ascii, 2: ucs2)\n", PVT_ID(pvt), dcs); + if (dcs == 0) { // GSM-7 + int cusd_nibbles = unhex(cusd, cusd); + res = gsm7_unpack_decode(cusd, cusd_nibbles, out_ucs2, sizeof(out_ucs2) / 2, 0, 0, 0); + if (res < 0) { + return -1; + } + res = ucs2_to_utf8(out_ucs2, res, cusd_utf8_str, sizeof(cusd_utf8_str) - 1); + } else if (dcs == 1) { // ASCII + res = strlen(cusd); + if (res > sizeof(cusd_utf8_str) - 1) { + res = -1; + } else { + memcpy(cusd_utf8_str, cusd, res); + } + } else if (dcs == 2) { // UCS-2 + int cusd_nibbles = unhex(cusd, cusd); + res = ucs2_to_utf8(out_ucs2, (cusd_nibbles + 1) / 4, cusd_utf8_str, sizeof(cusd_utf8_str) - 1); + } + if (res < 0) { return -1; } + cusd_utf8_str[res] = '\0'; - ast_verb (1, "[%s] Got USSD type %d '%s': '%s'\n", PVT_ID(pvt), type, typestr, cusd); - ast_base64encode (text_base64, (unsigned char*)cusd, strlen(cusd), sizeof(text_base64)); + ast_verb (1, "[%s] Got USSD type %d '%s': '%s'\n", PVT_ID(pvt), type, typestr, cusd_utf8_str); + ast_base64encode (text_base64, (unsigned char*)cusd_utf8_str, res, sizeof(text_base64)); // TODO: pass type - manager_event_new_ussd(PVT_ID(pvt), cusd); + manager_event_new_ussd(PVT_ID(pvt), cusd_utf8_str); manager_event_message("DongleNewUSSDBase64", PVT_ID(pvt), text_base64); { @@ -1422,13 +1483,12 @@ static int at_response_cusd (struct pvt * pvt, char * str, size_t len) { { "USSD_TYPE", typebuf }, { "USSD_TYPE_STR", ast_strdupa(typestr) }, - { "USSD", cusd }, + { "USSD", cusd_utf8_str }, { "USSD_BASE64", text_base64 }, { NULL, NULL }, }; start_local_channel(pvt, "ussd", "ussd", vars); } - return 0; } @@ -1574,7 +1634,7 @@ static int at_response_creg (struct pvt* pvt, char* str, size_t len) //#ifdef ISSUE_CCWA_STATUS_CHECK /* only if gsm_registered 0 -> 1 ? */ if(!pvt->gsm_registered && CONF_SHARED(pvt, callwaiting) != CALL_WAITING_AUTO) - at_enqueue_set_ccwa(&pvt->sys_chan, 0, 0, CONF_SHARED(pvt, callwaiting), 0, NULL); + at_enqueue_set_ccwa(&pvt->sys_chan, CONF_SHARED(pvt, callwaiting)); //#endif pvt->gsm_registered = 1; manager_event_device_status(PVT_ID(pvt), "Register"); @@ -1626,36 +1686,8 @@ static int at_response_cgmi (struct pvt* pvt, const char* str) #/* */ static int at_response_cgmm (struct pvt* pvt, const char* str) { - unsigned i; - /* NOTE: in order of appears, replace with sorter and binary search */ - static const char * const seven_bit_modems[] = { - "E1550", - "E1750", - "E160X", - "E150", - "E173", - "E1552", - "E171", - "E153", - "E156B", - "E1752", - "E261", - "E3531" - }; - ast_copy_string (pvt->model, str, sizeof (pvt->model)); - pvt->cusd_use_7bit_encoding = 0; - pvt->cusd_use_ucs2_decoding = 1; - for(i = 0; i < ITEMS_OF(seven_bit_modems); ++i) - { - if(!strcmp (pvt->model, seven_bit_modems[i])) - { - pvt->cusd_use_7bit_encoding = 1; - pvt->cusd_use_ucs2_decoding = 0; - break; - } - } return 0; } @@ -1735,7 +1767,8 @@ int at_response (struct pvt* pvt, const struct iovec iov[2], int iovcnt, at_res_ { char* str; size_t len; - const struct at_queue_cmd* ecmd = at_queue_head_cmd(pvt); + const at_queue_task_t *task = at_queue_head_task(pvt); + const at_queue_cmd_t *ecmd = at_queue_task_cmd(task); if(iov[0].iov_len + iov[1].iov_len > 0) @@ -1756,8 +1789,7 @@ int at_response (struct pvt* pvt, const struct iovec iov[2], int iovcnt, at_res_ } str[len] = '\0'; -/* ast_debug (5, "[%s] [%.*s]\n", PVT_ID(pvt), (int) len, str); -*/ +// ast_debug (5, "[%s] [%.*s]\n", PVT_ID(pvt), (int) len, str); if(ecmd && ecmd->cmd == CMD_USER) { ast_verb(1, "[%s] Got Response for user's command:'%s'\n", PVT_ID(pvt), str); @@ -1775,15 +1807,27 @@ int at_response (struct pvt* pvt, const struct iovec iov[2], int iovcnt, at_res_ return 0; case RES_CMGS: - /* Abuse the fact that we know how the manager - * message are formatted: CRLF separated headers - * with colon between key and value */ { - char buf[64]; int res = at_parse_cmgs(str); - const at_queue_task_t * task = at_queue_head_task (pvt); - snprintf(buf, 64, "Sending\r\nForeignID: %d", res); - manager_event_sent_notify(PVT_ID(pvt), "SMS", task, buf); + + char payload[SMSDB_PAYLOAD_MAX_LEN]; + char dst[SMSDB_DST_MAX_LEN]; + ssize_t payload_len = smsdb_outgoing_part_put(task->uid, res, dst, payload); + if (payload_len >= 0) { + ast_verb (3, "[%s] Error payload: %.*s\n", PVT_ID(pvt), payload_len, payload); + channel_var_t vars[] = + { + { "SMS_REPORT_PAYLOAD", payload }, + { "SMS_REPORT_TS", "" }, + { "SMS_REPORT_DT", "" }, + { "SMS_REPORT_SUCCESS", "1" }, + { "SMS_REPORT_TYPE", "i" }, + { "SMS_REPORT", "" }, + { NULL, NULL }, + }; + start_local_channel(pvt, "report", dst, vars); + manager_event_report(PVT_ID(pvt), payload, payload_len, "", "", 1, 0, ""); + } } return 0; @@ -1837,6 +1881,8 @@ int at_response (struct pvt* pvt, const struct iovec iov[2], int iovcnt, at_res_ case RES_CLIP: return at_response_clip (pvt, str, len); */ + case RES_CDSI: + return at_response_cdsi (pvt, str); case RES_CMTI: return at_response_cmti (pvt, str); diff --git a/at_response.h b/at_response.h index d00444bd..87f5b7b4 100644 --- a/at_response.h +++ b/at_response.h @@ -22,6 +22,7 @@ typedef enum { RES_CMGR, RES_CMS_ERROR, RES_CMTI, + RES_CDSI, RES_CNUM, RES_CONF, diff --git a/chan_dongle.c b/chan_dongle.c index e2f27489..f3abc2ff 100644 --- a/chan_dongle.c +++ b/chan_dongle.c @@ -28,6 +28,7 @@ * \author Dave Bowerman * \author Dmitry Vagin * \author bg + * \author Max von Buelow * * \ingroup channel_drivers */ @@ -70,6 +71,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Rev: " PACKAGE_REVISION " $") #include "dc_config.h" /* dc_uconfig_fill() dc_gconfig_fill() dc_sconfig_fill() */ #include "pdiscovery.h" /* pdiscovery_lookup() pdiscovery_init() pdiscovery_fini() */ #include "smsdb.h" +#include "error.h" EXPORT_DEF const char * const dev_state_strs[4] = { "stop", "restart", "remove", "start" }; EXPORT_DEF public_state_t * gpublic; @@ -300,8 +302,6 @@ static void disconnect_dongle (struct pvt* pvt) { /* unaffected in case of restart */ pvt->use_ucs2_encoding = 0; - pvt->cusd_use_7bit_encoding = 0; - pvt->cusd_use_ucs2_decoding = 1; pvt->gsm_reg_status = -1; pvt->rssi = 0; pvt->linkmode = 0; @@ -371,6 +371,28 @@ EXPORT_DEF void clean_read_data(const char * devname, int fd) } } +static void handle_expired_reports(struct pvt *pvt) +{ + char dst[SMSDB_DST_MAX_LEN]; + char payload[SMSDB_PAYLOAD_MAX_LEN]; + ssize_t payload_len = smsdb_outgoing_purge_one(dst, payload); + if (payload_len >= 0) { + ast_verb (3, "[%s] TTL payload: %.*s\n", PVT_ID(pvt), payload_len, payload); + channel_var_t vars[] = + { + { "SMS_REPORT_PAYLOAD", payload }, + { "SMS_REPORT_TS", "" }, + { "SMS_REPORT_DT", "" }, + { "SMS_REPORT_SUCCESS", "0" }, + { "SMS_REPORT_TYPE", "t" }, + { "SMS_REPORT", "" }, + { NULL, NULL }, + }; + start_local_channel(pvt, "report", dst, vars); + manager_event_report(PVT_ID(pvt), payload, payload_len, "", "", 0, 2, ""); + } +} + /*! * \brief Check if the module is unloading. @@ -417,6 +439,8 @@ static void* do_monitor_phone (void* data) { ast_mutex_lock (&pvt->lock); + handle_expired_reports(pvt); + if (port_status (pvt->data_fd) || port_status (pvt->audio_fd)) { ast_log (LOG_ERROR, "[%s] Lost connection to Dongle\n", dev); @@ -934,24 +958,20 @@ EXPORT_DEF struct pvt * find_device_ex(struct public_state * state, const char * } #/* return locked pvt or NULL */ -EXPORT_DEF struct pvt * find_device_ext (const char * name, const char ** reason) +EXPORT_DEF struct pvt * find_device_ext (const char * name) { char * res = ""; struct pvt * pvt = find_device(name); - if(pvt) - { - if(!pvt_enabled(pvt)) - { + if (pvt) { + if (!pvt_enabled(pvt)) { ast_mutex_unlock (&pvt->lock); - res = "device disabled"; + chan_dongle_err = E_DEVICE_DISABLED; pvt = NULL; } + } else { + chan_dongle_err = E_DEVICE_NOT_FOUND; } - else - res = "no such device"; - if(reason) - *reason = res; return pvt; } @@ -1422,7 +1442,6 @@ static struct pvt * pvt_create(const pvt_config_t * settings) pvt->audio_fd = -1; pvt->data_fd = -1; pvt->timeout = DATA_READ_TIMEOUT; - pvt->cusd_use_ucs2_decoding = 1; pvt->gsm_reg_status = -1; ast_copy_string (pvt->provider_name, "NONE", sizeof (pvt->provider_name)); @@ -1765,6 +1784,7 @@ static int unload_module() pdiscovery_fini(); ast_free(gpublic); + smsdb_atexit(); gpublic = NULL; return 0; } diff --git a/chan_dongle.h b/chan_dongle.h index 7d9f1c97..8693214e 100644 --- a/chan_dongle.h +++ b/chan_dongle.h @@ -144,8 +144,6 @@ typedef struct pvt /* device caps */ unsigned int use_ucs2_encoding:1; - unsigned int cusd_use_7bit_encoding:1; - unsigned int cusd_use_ucs2_decoding:1; /* device state */ int gsm_reg_status; @@ -249,7 +247,7 @@ INLINE_DECL struct pvt * find_device (const char* name) return find_device_ex(gpublic, name); } -EXPORT_DECL struct pvt * find_device_ext(const char* name, const char ** reason); +EXPORT_DECL struct pvt * find_device_ext(const char* name); EXPORT_DECL struct pvt * find_device_by_resource_ex(struct public_state * state, const char * resource, int opts, const struct ast_channel * requestor, int * exists); EXPORT_DECL void pvt_dsp_setup(struct pvt * pvt, const char * id, dc_dtmf_setting_t dtmf_new); diff --git a/channel.c b/channel.c index f793f97c..3cb2815e 100644 --- a/channel.c +++ b/channel.c @@ -1019,7 +1019,7 @@ static int channel_devicestate (void* data) ast_debug (1, "Checking device state for device %s\n", device); - pvt = find_device_ext (device, NULL); + pvt = find_device_ext(device); if (pvt) { if (pvt->connected) diff --git a/char_conv.c b/char_conv.c index 33342030..ce1d64ef 100644 --- a/char_conv.c +++ b/char_conv.c @@ -23,205 +23,133 @@ #include "mutils.h" /* ITEMS_OF() */ #include "gsm7_luts.h" -EXPORT_DEF ssize_t convert_string (const char* in, size_t in_length, char* out, size_t out_size, char* from, char* to) -{ - ICONV_CONST char* in_ptr = (ICONV_CONST char*) in; - size_t in_bytesleft = in_length; - char* out_ptr = out; - size_t out_bytesleft = out_size - 1; - ICONV_T cd = (ICONV_T) -1; - ssize_t res; - - cd = iconv_open (to, from); - if (cd == (ICONV_T) -1) - { - return -2; +static char lut_hex2val[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 +}; +static const char *lut_val2hex = "0123456789ABCDEF"; + +static ssize_t convert_string(const char *in, size_t in_length, char *out, size_t out_size, char *from, char *to) +{ + ICONV_CONST char *in_ptr = (ICONV_CONST char*)in; + size_t in_bytesleft = in_length; + char *out_ptr = out; + size_t out_bytesleft = out_size - 1; + ICONV_T cd = (ICONV_T)-1; + ssize_t res; + + cd = iconv_open(to, from); + if (cd == (ICONV_T)-1) { + return -1; } - res = iconv (cd, &in_ptr, &in_bytesleft, &out_ptr, &out_bytesleft); - if (res < 0) - { - iconv_close (cd); - return -3; + res = iconv(cd, &in_ptr, &in_bytesleft, &out_ptr, &out_bytesleft); + if (res < 0) { + iconv_close(cd); + return -1; } - iconv_close (cd); + iconv_close(cd); - *out_ptr = '\0'; - - return (out_ptr - out); + return out_ptr - out; } -#/* convert 1 hex digits of PDU to byte, return < 0 on error */ -EXPORT_DEF int parse_hexdigit(int hex) +EXPORT_DEF ssize_t utf8_to_ucs2(const char *in, size_t in_length, uint16_t *out, size_t out_size) { - if(hex >= '0' && hex <= '9') - return hex - '0'; - if(hex >= 'a' && hex <= 'f') - return hex - 'a' + 10; - if(hex >= 'A' && hex <= 'F') - return hex - 'A' + 10; - return -1; + ssize_t res = convert_string(in, in_length, (char*)out, out_size * 2, "UTF-8", "UTF-16BE"); + if (res < 0) return res; + return res / 2; } - -static ssize_t hexstr_to_8bitchars (const char* in, size_t in_length, char* out, size_t out_size) +EXPORT_DEF ssize_t ucs2_to_utf8(const uint16_t *in, size_t in_length, char *out, size_t out_size) { - int d1, d2; - - /* odd number of chars check */ - if (in_length & 0x1) - return -EINVAL; - - in_length = in_length >> 1; - - if (out_size - 1 < in_length) - { - return -ENOMEM; - } - out_size = in_length; - - for (; in_length; --in_length) - { - d1 = parse_hexdigit(*in++); - if(d1 < 0) - return -EINVAL; - d2 = parse_hexdigit(*in++); - if(d2 < 0) - return -EINVAL; - *out++ = (d1 << 4) | d2; - } - - *out = 0; - - return out_size; + return convert_string((const char*)in, in_length * 2, out, out_size, "UTF-16BE", "UTF-8"); } -static ssize_t chars8bit_to_hexstr (const char* in, size_t in_length, char* out, size_t out_size) -{ - static const char hex_table[] = "0123456789ABCDEF"; - const unsigned char *in2 = (const unsigned char *)in; /* for save time of first & 0x0F */ - - if (out_size - 1 < in_length * 2) - { - return -1; - } - out_size = in_length * 2; - - for (; in_length; --in_length, ++in2) - { - *out++ = hex_table[*in2 >> 4]; - *out++ = hex_table[*in2 & 0xF]; - } - - *out = 0; - return out_size; +static char hexchar2val(char h) +{ + return lut_hex2val[h]; } -static ssize_t chars16bit_to_hexstr (const uint16_t* in, size_t in_length, char* out, size_t out_size) +static char val2hexchar(char h) { - return chars8bit_to_hexstr((const char*)in, in_length * 2, out, out_size); + return lut_val2hex[h]; } - -static ssize_t hexstr_ucs2_to_utf8 (const char* in, size_t in_length, char* out, size_t out_size) +EXPORT_DEF int unhex(const char *in, uint8_t *out) { - char buf[out_size]; - ssize_t res; - - if (out_size - 1 < in_length / 2) - { - return -1; - } - - res = hexstr_to_8bitchars (in, in_length, buf, out_size); - if (res < 0) - { - return res; + int len = 0, nibbles = 0; + while (in[0]) { + nibbles += 1 + !!in[1]; + char p0 = hexchar2val(*in++); + char p1 = *in ? hexchar2val(*in++) : 0; + if (p0 == -1 || p1 == -1) { + return -1; + } + out[len++] = p0 << 4 | p1; } - - /* Since UTF-16BE is a superset of UCS-2BE -- using unused code - * points from UCS-2 -- we can safely assume that UTF-16BE works - * here. */ - res = convert_string (buf, res, out, out_size, "UTF-16BE", "UTF-8"); - - return res; + return nibbles; } - -static ssize_t utf8_to_hexstr_ucs2 (const char* in, size_t in_length, char* out, size_t out_size) +EXPORT_DEF void hexify(const uint8_t *in, size_t in_length, char *out) { - char buf[out_size]; - ssize_t res; - - if (out_size - 1 < in_length * 4) - { - return -1; + // code from end of string to allow in-place encoding + for (int i = in_length - 1; i >= 0; --i) { + char c0 = val2hexchar(in[i] >> 4), c1 = val2hexchar(in[i] & 15); + out[i * 2] = c0; + out[i * 2 + 1] = c1; } - - /* Since UTF-16BE is a superset of UCS-2BE -- using unused code - * points from UCS-2 -- we can safely assume that UTF-16BE works - * here. */ - res = convert_string (in, in_length, buf, out_size, "UTF-8", "UTF-16BE"); - if (res < 0) - { - return res; - } - - res = chars8bit_to_hexstr (buf, res, out, out_size); - - return res; + out[in_length * 2] = '\0'; } -static ssize_t char_to_hexstr_7bit_padded(const char* in, size_t in_length, char* out, - size_t out_size, unsigned out_padding) +#/* */ +static const uint8_t *get_char_gsm7_encoding(uint16_t c) { - size_t i; - size_t x; - char buf[4]; - unsigned value = 0; - - /* compute number of bytes we need for the final string, rounded up */ - x = ((out_padding + (7 * in_length) + 7) / 8); - /* compute number of hex characters we need for the final string */ - x = (2 * x) + 1 /* terminating zero */; - - /* check that the buffer is not too small */ - if (x > out_size) - return -1; + int minor = c >> 8, major = c & 255; + int subtab = LUT_GSM7_REV1[major]; + if (subtab == -1) return LUT_GSM7_REV2_INV; + return LUT_GSM7_REV2[subtab][minor]; +} - for (x = i = 0; i != in_length; i++) { - value |= (in[i] & 0x7F) << out_padding; - out_padding += 7; - if (out_padding < 8) - continue; - /* output one byte in hex */ - snprintf (buf, sizeof(buf), "%02X", value & 0xFF); - memcpy (out + x, buf, 2); - x = x + 2; - value >>= 8; - out_padding -= 8; - } - if (out_padding != 0) { - snprintf (buf, sizeof(buf), "%02X", value & 0xFF); - memcpy (out + x, buf, 2); - x = x + 2; +EXPORT_DEF ssize_t gsm7_encode(const uint16_t *in, size_t in_length, uint16_t *out) +{ + // TODO: Should we check for other tables or just use UCS-2? + unsigned bytes = 0; + const uint8_t *escenc = get_char_gsm7_encoding(0x1B00); + for (unsigned i = 0; i < in_length; ++i) { + const uint8_t *enc = get_char_gsm7_encoding(in[i]); + uint8_t c = enc[0]; + if (c == GSM7_INVALID) { + return -1; + } + if (c > 127) { + bytes += 2; + out[i] = (escenc[0] << 8) | (c - 128); + } else { + ++bytes; + out[i] = c; + } } - /* zero terminate final string */ - out[x] = '\0'; - - /* return total string length, excluding terminating zero */ - return x; + return bytes; } -static ssize_t chars16bit_to_hexstr_7bit(const uint16_t* in, size_t in_length, char* out, - size_t out_size, unsigned out_padding) +EXPORT_DEF ssize_t gsm7_pack(const uint16_t *in, size_t in_length, char *out, size_t out_size, unsigned out_padding) { - size_t i; - size_t x; - char buf[4]; + size_t i, x; unsigned value = 0; /* compute number of bytes we need for the final string, rounded up */ - x = ((out_padding + (7 * in_length) + 7) / 8); - /* compute number of hex characters we need for the final string */ - x = (2 * x) + 1 /* terminating zero */; + x = ((out_padding + (7 * in_length) + 7) / 8) + 1; /* check that the buffer is not too small */ if (x > out_size) @@ -229,179 +157,72 @@ static ssize_t chars16bit_to_hexstr_7bit(const uint16_t* in, size_t in_length, c for (x = i = 0; i != in_length; i++) { char c[] = { in[i] >> 8, in[i] & 255 }; -// printf("%d %d-%d %s\n", in[i], c[0], c[1], LUT_GSM7_LS[0][c[1]]); for (int j = c[0] == 0; j < 2; ++j) { value |= (c[j] & 0x7F) << out_padding; out_padding += 7; if (out_padding < 8) continue; - /* output one byte in hex */ - snprintf (buf, sizeof(buf), "%02X", value & 0xFF); - memcpy (out + x, buf, 2); - x = x + 2; + /* output one byte */ + out[x++] = value & 0xff; value >>= 8; out_padding -= 8; } } if (out_padding != 0) { - snprintf (buf, sizeof(buf), "%02X", value & 0xFF); - memcpy (out + x, buf, 2); - x = x + 2; + out[x++] = value & 0xff; } - /* zero terminate final string */ - out[x] = '\0'; - /* return total string length, excluding terminating zero */ - return x; + /* return total string length in nibbles, excluding terminating zero */ + return x * 2 - (out_padding == 1 || out_padding == 2 || out_padding == 3 ? 1 : 0); } - -static ssize_t hexstr_7bit_to_char_padded(const char* in, size_t in_length, char* out, - size_t out_size, unsigned in_padding, uint8_t ls, uint8_t ss) +EXPORT_DEF ssize_t gsm7_unpack_decode(const char *in, size_t in_nibbles, uint16_t *out, size_t out_size, unsigned in_padding, uint8_t ls, uint8_t ss) { if (ls > 13) ls = 0; if (ss > 13) ss = 0; size_t i; size_t x; - char buf[4]; unsigned value = 0; - unsigned hexval; - - /* compute number of bytes */ - in_length /= 2; + unsigned c; if (out_size == 0) { return -1; } /* check if string is empty */ - if (in_length == 0) { + if (in_nibbles < 2) { out[0] = '\0'; - return (0); + return 0; } -#if 0 - /* compute number of characters */ - x = (((in_length * 8) - in_padding) / 7) + 1 /* terminating zero */; -#endif - out_size -= 1; /* reserve room for terminating zero */ - /* account for the bit padding */ in_padding = 7 - in_padding; - /* clear temporary buffer */ - memset(buf, 0, sizeof(buf)); - /* parse the hexstring */ int esc = 0; - for (x = i = 0; i != in_length; i++) { + for (x = i = 0; i < in_nibbles; ++i) { if (x >= out_size) return -1; - memcpy (buf, in + i * 2, 2); - if (sscanf (buf, "%x", &hexval) != 1) - return -1; - value |= (hexval & 0xFF) << in_padding; - in_padding += 8; + c = in[i / 2]; + if (i & 1) c >>= 4; + uint8_t n = c & 0xf; + value |= n << in_padding; + in_padding += 4; - while (in_padding >= (2 * 7)) { + while (in_padding >= 7 * 2) { in_padding -= 7; value >>= 7; { - const char *val = (esc ? LUT_GSM7_SS : LUT_GSM7_LS)[esc ? ss : ls][value & 0x7F]; - if (val[0] == '\x1b' && !val[1]) { + uint16_t val = (esc ? LUT_GSM7_SS16 : LUT_GSM7_LS16)[esc ? ss : ls][value & 0x7f]; + if (val == 0x1b) { esc = 1; } else { esc = 0; - do { - out[x++] = *val++; - } while (*val && x < out_size); + out[x++] = ((val & 0xff) << 8) | (val >> 8); } } } } - /* zero terminate final string */ - out[x] = '\0'; - - /* return total string length, excluding terminating zero */ return x; } - -#/* */ -ssize_t just_copy (const char* in, size_t in_length, char* out, size_t out_size) -{ - // FIXME: or copy out_size-1 bytes only ? - if (in_length <= out_size - 1) - { - memcpy(out, in, in_length); - out[in_length] = 0; - return in_length; - } - return -ENOMEM; -} - -#/* */ -EXPORT_DEF ssize_t str_encode(str_encoding_t encoding, const char* in, size_t in_length, char* out, size_t out_size) -{ - if (encoding >= STR_ENCODING_GSM7_HEX_PAD_0 && encoding <= STR_ENCODING_GSM7_HEX_PAD_6) { - return char_to_hexstr_7bit_padded(in, in_length, out, out_size, encoding - STR_ENCODING_GSM7_HEX_PAD_0); - } - switch (encoding) { - case STR_ENCODING_ASCII: - return just_copy(in, in_length, out, out_size); - case STR_ENCODING_UCS2_HEX: - return utf8_to_hexstr_ucs2(in, in_length, out, out_size); - default: - return -EINVAL; - } -} - -#/* */ -EXPORT_DEF ssize_t str_encode16(str_encoding_t encoding, const uint16_t* in, size_t in_length, char* out, size_t out_size) -{ - if (encoding == STR_ENCODING_UCS2_HEX) { - return chars16bit_to_hexstr(in, in_length, out, out_size); - } else if (encoding >= STR_ENCODING_GSM7_HEX_PAD_0 && encoding <= STR_ENCODING_GSM7_HEX_PAD_6) { - return chars16bit_to_hexstr_7bit(in, in_length, out, out_size, encoding - STR_ENCODING_GSM7_HEX_PAD_0); - } - return -EINVAL; -} - -EXPORT_DEF ssize_t str_decode(str_encoding_t encoding, const char* in, size_t in_length, char* out, size_t out_size, uint8_t ls, uint8_t ss) -{ - if (encoding >= STR_ENCODING_GSM7_HEX_PAD_0 && encoding <= STR_ENCODING_GSM7_HEX_PAD_6) { - return hexstr_7bit_to_char_padded(in, in_length, out, out_size, encoding - STR_ENCODING_GSM7_HEX_PAD_0, ls, ss); - } - switch (encoding) { - case STR_ENCODING_ASCII: - return just_copy(in, in_length, out, out_size); - case STR_ENCODING_8BIT_HEX: - return hexstr_to_8bitchars(in, in_length, out, out_size); - case STR_ENCODING_UCS2_HEX: - return hexstr_ucs2_to_utf8(in, in_length, out, out_size); - default: - return -EINVAL; - } -} - -#/* */ -EXPORT_DEF const uint8_t *get_char_gsm7_encoding(uint16_t c) -{ - int minor = c >> 8, major = c & 255; - int subtab = LUT_GSM7_REV1[major]; - if (subtab == -1) return LUT_GSM7_REV2_INV; - return LUT_GSM7_REV2[subtab][minor]; -} -EXPORT_DEF str_encoding_t get_encoding(const char* in, size_t length) -{ - size_t x; - for(x = 0; x < length; ++x) - { - if(parse_hexdigit(in[x]) < 0) { - return STR_ENCODING_ASCII; - } - } - // TODO: STR_ENCODING_GSM7_HEX_PAD_X or STR_ENCODING_8BIT_HEX or STR_ENCODING_UCS2_HEX - - return STR_ENCODING_UNKNOWN; -} diff --git a/char_conv.h b/char_conv.h index e128e647..9c18cc6e 100644 --- a/char_conv.h +++ b/char_conv.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2010 bg + Copyright (C) 2020 Max von Buelow */ #ifndef CHAN_DONGLE_CHAR_CONV_H_INCLUDED #define CHAN_DONGLE_CHAR_CONV_H_INCLUDED @@ -8,36 +8,12 @@ #include "export.h" /* EXPORT_DECL EXPORT_DEF */ #include -/* encoding types of strings to/from device */ -/* for simplefy first 3 values same as in PDU DCS bits 3..2 */ -/* NOTE: order is magic see definition of recoders in char_conv.c */ -typedef enum { - STR_ENCODING_GSM7_HEX_PAD_0 = 0, /* 7bit encoding, 0 bits of padding */ - STR_ENCODING_GSM7_HEX_PAD_1, /* 7bit encoding, 1 bit of padding */ - STR_ENCODING_GSM7_HEX_PAD_2, /* 7bit encoding, 2 bits of padding */ - STR_ENCODING_GSM7_HEX_PAD_3, /* 7bit encoding, 3 bits padding */ - STR_ENCODING_GSM7_HEX_PAD_4, /* 7bit encoding, 4 bits padding */ - STR_ENCODING_GSM7_HEX_PAD_5, /* 7bit encoding, 5 bits padding */ - STR_ENCODING_GSM7_HEX_PAD_6, /* 7bit encoding, 6 bits padding */ - STR_ENCODING_8BIT_HEX, /* 8bit encoding */ - STR_ENCODING_UCS2_HEX, /* UCS-2 in hex like PDU */ -/* TODO: check its really 7bit input from device */ - STR_ENCODING_ASCII, /* 7bit ASCII no need recode to utf-8 */ -// STR_ENCODING_8BIT, /* 8bit */ -// STR_ENCODING_UCS2, /* UCS2 */ - STR_ENCODING_UNKNOWN, /* still unknown */ -} str_encoding_t; - -EXPORT_DECL ssize_t convert_string (const char* in, size_t in_length, char* out, size_t out_size, char* from, char* to); - -/* recode in both directions */ -EXPORT_DECL ssize_t str_encode(str_encoding_t encoding, const char* in, size_t in_length, char* out, size_t out_size); -EXPORT_DECL ssize_t str_encode16(str_encoding_t encoding, const uint16_t* in, size_t in_length, char* out, size_t out_size); -EXPORT_DECL ssize_t str_decode(str_encoding_t encoding, const char* in, size_t in_length, char* out, size_t out_size, uint8_t ls, uint8_t ss); - -EXPORT_DECL int parse_hexdigit(int hex); -EXPORT_DECL str_encoding_t get_encoding(const char * in, size_t in_length); - -EXPORT_DECL const uint8_t *get_char_gsm7_encoding(uint16_t c); +EXPORT_DECL ssize_t utf8_to_ucs2(const char *in, size_t in_length, uint16_t *out, size_t out_size); +EXPORT_DECL ssize_t ucs2_to_utf8(const uint16_t *in, size_t in_length, char *out, size_t out_size); +EXPORT_DECL int unhex(const char *in, uint8_t *out); +EXPORT_DECL void hexify(const uint8_t *in, size_t in_length, char *out); +EXPORT_DECL ssize_t gsm7_encode(const uint16_t *in, size_t in_length, uint16_t *out); +EXPORT_DECL ssize_t gsm7_pack(const uint16_t *in, size_t in_length, char *out, size_t out_size, unsigned out_padding); +EXPORT_DECL ssize_t gsm7_unpack_decode(const char *in, size_t in_length, uint16_t *out, size_t out_size, unsigned in_padding, uint8_t ls, uint8_t ss); #endif /* CHAN_DONGLE_CHAR_CONV_H_INCLUDED */ diff --git a/cli.c b/cli.c index 02f4618a..6705647e 100644 --- a/cli.c +++ b/cli.c @@ -20,6 +20,7 @@ #include "chan_dongle.h" /* devices */ #include "helpers.h" /* ITEMS_OF() send_ccwa_set() send_reset() send_sms() send_ussd() */ #include "pdiscovery.h" /* pdiscovery_list_begin() pdiscovery_list_next() pdiscovery_list_end() */ +#include "error.h" static const char * restate2str_msg(restate_time_t when); @@ -215,8 +216,6 @@ static char* cli_show_device_state (struct ast_cli_entry* e, int cmd, struct ast ast_cli (a->fd, " Subscriber Number : %s\n", pvt->subscriber_number); ast_cli (a->fd, " SMS Service Center : %s\n", pvt->sms_scenter); ast_cli (a->fd, " Use UCS-2 encoding : %s\n", pvt->use_ucs2_encoding ? "Yes" : "No"); - ast_cli (a->fd, " USSD use 7 bit encoding : %s\n", pvt->cusd_use_7bit_encoding ? "Yes" : "No"); - ast_cli (a->fd, " USSD use UCS-2 decoding : %s\n", pvt->cusd_use_ucs2_decoding ? "Yes" : "No"); ast_cli (a->fd, " Tasks in queue : %u\n", PVT_STATE(pvt, at_tasks)); ast_cli (a->fd, " Commands in queue : %u\n", PVT_STATE(pvt, at_cmds)); ast_cli (a->fd, " Call Waiting : %s\n", pvt->has_call_waiting ? "Enabled" : "Disabled" ); @@ -422,9 +421,6 @@ static char* cli_cmd (struct ast_cli_entry* e, int cmd, struct ast_cli_args* a) static char* cli_ussd (struct ast_cli_entry* e, int cmd, struct ast_cli_args* a) { - const char * msg; - int status; - void * msgid; switch (cmd) { @@ -449,22 +445,16 @@ static char* cli_ussd (struct ast_cli_entry* e, int cmd, struct ast_cli_args* a) return CLI_SHOWUSAGE; } - msg = send_ussd(a->argv[2], a->argv[3], &status, &msgid); - if(status) - ast_cli (a->fd, "[%s] %s with id %p\n", a->argv[2], msg, msgid); - else - ast_cli (a->fd, "[%s] %s\n", a->argv[2], msg); + int res = send_ussd(a->argv[2], a->argv[3]); + ast_cli(a->fd, "[%s] %s\n", a->argv[2], res < 0 ? error2str(chan_dongle_err) : "USSD queued for send"); return CLI_SUCCESS; } static char* cli_sms (struct ast_cli_entry* e, int cmd, struct ast_cli_args* a) { - const char * msg; struct ast_str * buf; int i; - int status; - void * msgid; switch (cmd) { @@ -501,51 +491,9 @@ static char* cli_sms (struct ast_cli_entry* e, int cmd, struct ast_cli_args* a) } } - msg = send_sms(a->argv[2], a->argv[3], ast_str_buffer(buf), 0, "1", &status, &msgid); + int res = send_sms(a->argv[2], a->argv[3], ast_str_buffer(buf), 0, "1", "UNKNOWN", 8); ast_free (buf); - - if(status) - ast_cli(a->fd, "[%s] %s with id %p\n", a->argv[2], msg, msgid); - else - ast_cli(a->fd, "[%s] %s\n", a->argv[2], msg); - - return CLI_SUCCESS; -} - -static char * cli_pdu(struct ast_cli_entry * e, int cmd, struct ast_cli_args * a) -{ - const char * msg; - int status; - void * msgid; - - switch (cmd) - { - case CLI_INIT: - e->command = "dongle pdu"; - e->usage = - "Usage: dongle pdu \n" - " Send a of sms from \n"; - return NULL; - - case CLI_GENERATE: - if (a->pos == 2) - { - return complete_device (a->word, a->n); - } - return NULL; - } - - if (a->argc != 4) - { - return CLI_SHOWUSAGE; - } - - msg = send_pdu(a->argv[2], a->argv[3], &status, &msgid); - - if(status) - ast_cli(a->fd, "[%s] %s with id %p\n", a->argv[2], msg, msgid); - else - ast_cli(a->fd, "[%s] %s\n", a->argv[2], msg); + ast_cli(a->fd, "[%s] %s\n", a->argv[2], res < 0 ? error2str(chan_dongle_err) : "SMS queued for send"); return CLI_SUCCESS; } @@ -559,7 +507,6 @@ typedef char * const * ast_cli_complete2_t; static char* cli_ccwa_set (struct ast_cli_entry* e, int cmd, struct ast_cli_args* a) { static const char * const choices[] = { "enable", "disable", NULL }; - const char * msg; call_waiting_t enable; switch (cmd) @@ -594,16 +541,14 @@ static char* cli_ccwa_set (struct ast_cli_entry* e, int cmd, struct ast_cli_args else return CLI_SHOWUSAGE; - msg = send_ccwa_set(a->argv[3], enable, NULL); - ast_cli (a->fd, "[%s] %s\n", a->argv[3], msg); + int res = send_ccwa_set(a->argv[3], enable); + ast_cli(a->fd, "[%s] %s\n", a->argv[3], res < 0 ? error2str(chan_dongle_err) : "Call-Waiting commands queued for execute"); return CLI_SUCCESS; } static char* cli_reset (struct ast_cli_entry* e, int cmd, struct ast_cli_args* a) { - const char * msg; - switch (cmd) { case CLI_INIT: @@ -626,8 +571,8 @@ static char* cli_reset (struct ast_cli_entry* e, int cmd, struct ast_cli_args* a return CLI_SHOWUSAGE; } - msg = send_reset(a->argv[2], NULL); - ast_cli (a->fd, "[%s] %s\n", a->argv[2], msg); + int res = send_reset(a->argv[2]); + ast_cli(a->fd, "[%s] %s\n", a->argv[2], res < 0 ? error2str(chan_dongle_err) : "Reset command queued for execute"); return CLI_SUCCESS; } @@ -662,7 +607,7 @@ static char* cli_restart_event(struct ast_cli_entry* e, int cmd, struct ast_cli_ }; const char * device = NULL; - const char * msg; + int res; int i; switch (cmd) @@ -712,8 +657,8 @@ static char* cli_restart_event(struct ast_cli_entry* e, int cmd, struct ast_cli_ if(device) { - msg = schedule_restart_event(event, i, device, NULL); - ast_cli(a->fd, "[%s] %s\n", device, msg); + res = schedule_restart_event(event, i, device); + ast_cli(a->fd, "[%s] %s\n", device, res < 0 ? error2str(chan_dongle_err) : dev_state2str_msg(event)); return CLI_SUCCESS; } break; @@ -743,8 +688,6 @@ static char * cli_remove(struct ast_cli_entry *e, int cmd, struct ast_cli_args * #/* */ static char* cli_start(struct ast_cli_entry* e, int cmd, struct ast_cli_args* a) { - const char * msg; - switch (cmd) { case CLI_INIT: @@ -764,8 +707,8 @@ static char* cli_start(struct ast_cli_entry* e, int cmd, struct ast_cli_args* a) return CLI_SHOWUSAGE; } - msg = schedule_restart_event(DEV_STATE_STARTED, RESTATE_TIME_NOW, a->argv[2], NULL); - ast_cli(a->fd, "[%s] %s\n", a->argv[2], msg); + int res = schedule_restart_event(DEV_STATE_STARTED, RESTATE_TIME_NOW, a->argv[2]); + ast_cli(a->fd, "[%s] %s\n", a->argv[2], res < 0 ? error2str(chan_dongle_err) : dev_state2str_msg(DEV_STATE_STARTED)); return CLI_SUCCESS; } @@ -917,7 +860,6 @@ static struct ast_cli_entry cli[] = { AST_CLI_DEFINE (cli_cmd, "Send commands to port for debugging"), AST_CLI_DEFINE (cli_ussd, "Send USSD commands to the dongle"), AST_CLI_DEFINE (cli_sms, "Send SMS from the dongle"), - AST_CLI_DEFINE (cli_pdu, "Send PDU of SMS from the dongle"), AST_CLI_DEFINE (cli_ccwa_set, "Enable/Disable Call-Waiting on the dongle"), AST_CLI_DEFINE (cli_reset, "Reset dongle now"), diff --git a/error.c b/error.c new file mode 100644 index 00000000..0c6d0c50 --- /dev/null +++ b/error.c @@ -0,0 +1,3 @@ +#include "error.h" + +EXPORT_DEF __thread int chan_dongle_err; diff --git a/error.h b/error.h new file mode 100644 index 00000000..28637ebc --- /dev/null +++ b/error.h @@ -0,0 +1,51 @@ +/* + Copyright (C) 2020 Max von Buelow +*/ + +#ifndef ERROR_H_INCLUDED +#define ERROR_H_INCLUDED +#include "export.h" /* EXPORT_DECL EXPORT_DEF */ + +enum error { + E_UNKNOWN = 0, + E_DEVICE_DISABLED, + E_DEVICE_NOT_FOUND, + E_DEVICE_DISCONNECTED, + E_INVALID_USSD, + E_INVALID_PHONE_NUMBER, + E_PARSE_UTF8, + E_PARSE_UCS2, + E_ENCODE_GSM7, + E_PACK_GSM7, + E_DECODE_GSM7, + E_SMSDB, + E_QUEUE, + E_BUILD_PDU, + E_PARSE_CMGR_LINE, + E_DEPRECATED_CMGR_TEXT, + E_INVALID_TPDU_LENGTH, + E_MALFORMED_HEXSTR, + E_INVALID_SCA, + E_INVALID_TPDU_TYPE, + E_PARSE_TPDU, + E_INVALID_TIMESTAMP, + E_INVALID_CHARSET, + E_BUILD_SCA, + E_BUILD_PHONE_NUMBER, + E_2BIG +}; + +INLINE_DECL const char *error2str(int err) +{ + static const char * const errors[] = { + "Unknown error", "Device disbaled", "Device not found", "Device disconnected", "Invalid USSD", "Invalid phone number", + "Cannot parse UTF-8", "Cannot parse UCS-2", "Cannot encode GSM7", "Cannot pack GSM7", "Cannot decode GSM7", "SMSDB error", "Queue error", "PDU building error", + "Can't parse +CMGR response line", "Parsing messages in TEXT mode is not supported anymore; This message should never appear. Nevertheless, if this message appears, please report on GitHub.", + "Invalid TPDU length in CMGR PDU status line", "Malformed hex string", "Invalid SCA", "Invalid TPDU type", "Cannot parse TPDU", "Invalid timestamp", "Invalid charset", "Cannot build SCA", "Cannot build phone number", "Input too large" + }; + return enum2str(err, errors, ITEMS_OF(errors)); +} + +EXPORT_DECL __thread int chan_dongle_err; + +#endif diff --git a/etc/extensions.conf b/etc/extensions.conf index a081d3ac..f33fc4da 100644 --- a/etc/extensions.conf +++ b/etc/extensions.conf @@ -81,7 +81,7 @@ exten => s,n,DongleStatus(s:25099,DONGLE5_STATUS) ; 2 device connected and free ; 3 device connected and in use -exten => s,n,DongleSendSMS(dongle0,+18004005422,"Hello how are you, Danila?",1440,yes) +exten => s,n,DongleSendSMS(dongle0,+18004005422,"Hello how are you, Danila?",1440,yes,"SomeMagicMessageID") ; send SMS on selected device and to specified number ; device name of Dongle device ; destination number in International format with leading '+' or w/o leading '+' diff --git a/gsm7_luts.h b/gsm7_luts.h index 6c304291..c183a590 100644 --- a/gsm7_luts.h +++ b/gsm7_luts.h @@ -2,290 +2,290 @@ #define CHAN_DONGLE_GSM7_LUTS_H_INCLUDED /* GSM 03.38 7bit alphabet */ -static const char *const LUT_GSM7_LS[14][128] = { - { - "@", "\u00a3", "$", "\u00a5", "\u00e8", "\u00e9", "\u00f9", "\u00ec", "\u00f2", "\u00c7", "\n", "\u00d8", "\u00f8", "\r", "\u00c5", "\u00e5", - "\u0394", "_", "\u03a6", "\u0393", "\u039b", "\u03a9", "\u03a0", "\u03a8", "\u03a3", "\u0398", "\u039e", "\x1b", "\u00c6", "\u00e6", "\u00df", "\u00c9", - " ", "!", "\"", "#", "\u00a4", "%", "&", "\'", "(", ")", "*", "+", ",", "-", ".", "/", - "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ";", "<", "=", ">", "\?", - "\u00a1", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", - "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "\u00c4", "\u00d6", "\u00d1", "\u00dc", "\u00a7", - "\u00bf", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", - "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "\u00e4", "\u00f6", "\u00f1", "\u00fc", "\u00e0", - }, - { - "@", "\u00a3", "$", "\u00a5", "\u20ac", "\u00e9", "\u00f9", "\u0131", "\u00f2", "\u00c7", "\n", "\u011e", "\u011f", "\r", "\u00c5", "\u00e5", - "\u0394", "_", "\u03a6", "\u0393", "\u039b", "\u03a9", "\u03a0", "\u03a8", "\u03a3", "\u0398", "\u039e", "\x1b", "\u015e", "\u015f", "\u00df", "\u00c9", - " ", "!", "\"", "#", "\u00a4", "%", "&", "\'", "(", ")", "*", "+", ",", "-", ".", "/", - "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ";", "<", "=", ">", "\?", - "\u0130", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", - "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "\u00c4", "\u00d6", "\u00d1", "\u00dc", "\u00a7", - "\u00e7", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", - "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "\u00e4", "\u00f6", "\u00f1", "\u00fc", "\u00e0", - }, - { - "@", "\u00a3", "$", "\u00a5", "\u00e8", "\u00e9", "\u00f9", "\u00ec", "\u00f2", "\u00c7", "\n", "\u00d8", "\u00f8", "\r", "\u00c5", "\u00e5", - "\u0394", "_", "\u03a6", "\u0393", "\u039b", "\u03a9", "\u03a0", "\u03a8", "\u03a3", "\u0398", "\u039e", "\x1b", "\u00c6", "\u00e6", "\u00df", "\u00c9", - " ", "!", "\"", "#", "\u00a4", "%", "&", "\'", "(", ")", "*", "+", ",", "-", ".", "/", - "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ";", "<", "=", ">", "\?", - "\u00a1", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", - "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "\u00c4", "\u00d6", "\u00d1", "\u00dc", "\u00a7", - "\u00bf", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", - "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "\u00e4", "\u00f6", "\u00f1", "\u00fc", "\u00e0", - }, - { - "@", "\u00a3", "$", "\u00a5", "\u00ea", "\u00e9", "\u00fa", "\u00ed", "\u00f3", "\u00e7", "\n", "\u00d4", "\u00f4", "\r", "\u00c1", "\u00e1", - "\u0394", "_", "\u00aa", "\u00c7", "\u00c0", "\u221e", "^", "\\", "\u20ac", "\u00d3", "|", "\x1b", "\u00c2", "\u00e2", "\u00ca", "\u00c9", - " ", "!", "\"", "#", "\u00ba", "%", "&", "\'", "(", ")", "*", "+", ",", "-", ".", "/", - "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ";", "<", "=", ">", "\?", - "\u00cd", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", - "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "\u00c3", "\u00d5", "\u00da", "\u00dc", "\u00a7", - "~", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", - "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "\u00e3", "\u00f5", "`", "\u00fc", "\u00e0", - }, - { - "\u0981", "\u0982", "\u0983", "\u0985", "\u0986", "\u0987", "\u0988", "\u0989", "\u098a", "\u098b", "\n", "\u098c", "\0", "\r", "\0", "\u098f", - "\u0990", "\0", "\0", "\u0993", "\u0994", "\u0995", "\u0996", "\u0997", "\u0998", "\u0999", "\u099a", "\x1b", "\u099b", "\u099c", "\u099d", "\u099e", - " ", "!", "\u099f", "\u09a0", "\u09a1", "\u09a2", "\u09a3", "\u09a4", ")", "(", "\u09a5", "\u09a6", ",", "\u09a7", ".", "\u09a8", - "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ";", "\0", "\u09aa", "\u09ab", "\?", - "\u09ac", "\u09ad", "\u09ae", "\u09af", "\u09b0", "\0", "\u09b2", "\0", "\0", "\0", "\u09b6", "\u09b7", "\u09b8", "\u09b9", "\u09bc", "\u09bd", - "\u09be", "\u09bf", "\u09c0", "\u09c1", "\u09c2", "\u09c3", "\u09c4", "\0", "\0", "\u09c7", "\u09c8", "\0", "\0", "\u09cb", "\u09cc", "\u09cd", - "\u09ce", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", - "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "\u09d7", "\u09dc", "\u09dd", "\u09f0", "\u09f1", - }, - { - "\u0a81", "\u0a82", "\u0a83", "\u0a85", "\u0a86", "\u0a87", "\u0a88", "\u0a89", "\u0a8a", "\u0a8b", "\n", "\u0a8c", "\u0a8d", "\r", "\0", "\u0a8f", - "\u0a90", "\u0a91", "\0", "\u0a93", "\u0a94", "\u0a95", "\u0a96", "\u0a97", "\u0a98", "\u0a99", "\u0a9a", "\x1b", "\u0a9b", "\u0a9c", "\u0a9d", "\u0a9e", - " ", "!", "\u0a9f", "\u0aa0", "\u0aa1", "\u0aa2", "\u0aa3", "\u0aa4", ")", "(", "\u0aa5", "\u0aa6", ",", "\u0aa7", ".", "\u0aa8", - "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ";", "\0", "\u0aaa", "\u0aab", "\?", - "\u0aac", "\u0aad", "\u0aae", "\u0aaf", "\u0ab0", "\0", "\u0ab2", "\u0ab3", "\0", "\u0ab5", "\u0ab6", "\u0ab7", "\u0ab8", "\u0ab9", "\u0abc", "\u0abd", - "\u0abe", "\u0abf", "\u0ac0", "\u0ac1", "\u0ac2", "\u0ac3", "\u0ac4", "\u0ac5", "\0", "\u0ac7", "\u0ac8", "\u0ac9", "\0", "\u0acb", "\u0acc", "\u0acd", - "\u0ad0", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", - "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "\u0ae0", "\u0ae1", "\u0ae2", "\u0ae3", "\u0af1", - }, - { - "\u0901", "\u0902", "\u0903", "\u0905", "\u0906", "\u0907", "\u0908", "\u0909", "\u090a", "\u090b", "\n", "\u090c", "\u090d", "\r", "\u090e", "\u090f", - "\u0910", "\u0911", "\u0912", "\u0913", "\u0914", "\u0915", "\u0916", "\u0917", "\u0918", "\u0919", "\u091a", "\x1b", "\u091b", "\u091c", "\u091d", "\u091e", - " ", "!", "\u091f", "\u0920", "\u0921", "\u0922", "\u0923", "\u0924", ")", "(", "\u0925", "\u0926", ",", "\u0927", ".", "\u0928", - "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ";", "\u0929", "\u092a", "\u092b", "\?", - "\u092c", "\u092d", "\u092e", "\u092f", "\u0930", "\u0931", "\u0932", "\u0933", "\u0934", "\u0935", "\u0936", "\u0937", "\u0938", "\u0939", "\u093c", "\u093d", - "\u093e", "\u093f", "\u0940", "\u0941", "\u0942", "\u0943", "\u0944", "\u0945", "\u0946", "\u0947", "\u0948", "\u0949", "\u094a", "\u094b", "\u094c", "\u094d", - "\u0950", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", - "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "\u0972", "\u097b", "\u097c", "\u097e", "\u097f", - }, - { - "\0", "\u0c82", "\u0c83", "\u0c85", "\u0c86", "\u0c87", "\u0c88", "\u0c89", "\u0c8a", "\u0c8b", "\n", "\u0c8c", "\0", "\r", "\u0c8e", "\u0c8f", - "\u0c90", "\0", "\u0c92", "\u0c93", "\u0c94", "\u0c95", "\u0c96", "\u0c97", "\u0c98", "\u0c99", "\u0c9a", "\x1b", "\u0c9b", "\u0c9c", "\u0c9d", "\u0c9e", - " ", "!", "\u0c9f", "\u0ca0", "\u0ca1", "\u0ca2", "\u0ca3", "\u0ca4", ")", "(", "\u0ca5", "\u0ca6", ",", "\u0ca7", ".", "\u0ca8", - "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ";", "\0", "\u0caa", "\u0cab", "\?", - "\u0cac", "\u0cad", "\u0cae", "\u0caf", "\u0cb0", "\u0cb1", "\u0cb2", "\u0cb3", "\0", "\u0cb5", "\u0cb6", "\u0cb7", "\u0cb8", "\u0cb9", "\u0cbc", "\u0cbd", - "\u0cbe", "\u0cbf", "\u0cc0", "\u0cc1", "\u0cc2", "\u0cc3", "\u0cc4", "\0", "\u0cc6", "\u0cc7", "\u0cc8", "\0", "\u0cca", "\u0ccb", "\u0ccc", "\u0ccd", - "\u0cd5", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", - "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "\u0cd6", "\u0ce0", "\u0ce1", "\u0ce2", "\u0ce3", - }, - { - "\0", "\u0d02", "\u0d03", "\u0d05", "\u0d06", "\u0d07", "\u0d08", "\u0d09", "\u0d0a", "\u0d0b", "\n", "\u0d0c", "\0", "\r", "\u0d0e", "\u0d0f", - "\u0d10", "\0", "\u0d12", "\u0d13", "\u0d14", "\u0d15", "\u0d16", "\u0d17", "\u0d18", "\u0d19", "\u0d1a", "\x1b", "\u0d1b", "\u0d1c", "\u0d1d", "\u0d1e", - " ", "!", "\u0d1f", "\u0d20", "\u0d21", "\u0d22", "\u0d23", "\u0d24", ")", "(", "\u0d25", "\u0d26", ",", "\u0d27", ".", "\u0d28", - "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ";", "\0", "\u0d2a", "\u0d2b", "\?", - "\u0d2c", "\u0d2d", "\u0d2e", "\u0d2f", "\u0d30", "\u0d31", "\u0d32", "\u0d33", "\u0d34", "\u0d35", "\u0d36", "\u0d37", "\u0d38", "\u0d39", "\0", "\u0d3d", - "\u0d3e", "\u0d3f", "\u0d40", "\u0d41", "\u0d42", "\u0d43", "\u0d44", "\0", "\u0d46", "\u0d47", "\u0d48", "\0", "\u0d4a", "\u0d4b", "\u0d4c", "\u0d4d", - "\u0d57", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", - "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "\u0d60", "\u0d61", "\u0d62", "\u0d63", "\u0d79", - }, - { - "\u0b01", "\u0b02", "\u0b03", "\u0b05", "\u0b06", "\u0b07", "\u0b08", "\u0b09", "\u0b0a", "\u0b0b", "\n", "\u0b0c", "\0", "\r", "\0", "\u0b0f", - "\u0b10", "\0", "\0", "\u0b13", "\u0b14", "\u0b15", "\u0b16", "\u0b17", "\u0b18", "\u0b19", "\u0b1a", "\x1b", "\u0b1b", "\u0b1c", "\u0b1d", "\u0b1e", - " ", "!", "\u0b1f", "\u0b20", "\u0b21", "\u0b22", "\u0b23", "\u0b24", ")", "(", "\u0b25", "\u0b26", ",", "\u0b27", ".", "\u0b28", - "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ";", "\0", "\u0b2a", "\u0b2b", "\?", - "\u0b2c", "\u0b2d", "\u0b2e", "\u0b2f", "\u0b30", "\0", "\u0b32", "\u0b33", "\0", "\u0b35", "\u0b36", "\u0b37", "\u0b38", "\u0b39", "\u0b3c", "\u0b3d", - "\u0b3e", "\u0b3f", "\u0b40", "\u0b41", "\u0b42", "\u0b43", "\u0b44", "\0", "\0", "\u0b47", "\u0b48", "\0", "\0", "\u0b4b", "\u0b4c", "\u0b4d", - "\u0b56", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", - "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "\u0b57", "\u0b60", "\u0b61", "\u0b62", "\u0b63", - }, - { - "\u0a01", "\u0a02", "\u0a03", "\u0a05", "\u0a06", "\u0a07", "\u0a08", "\u0a09", "\u0a0a", "\0", "\n", "\0", "\0", "\r", "\0", "\u0a0f", - "\u0a10", "\0", "\0", "\u0a13", "\u0a14", "\u0a15", "\u0a16", "\u0a17", "\u0a18", "\u0a19", "\u0a1a", "\x1b", "\u0a1b", "\u0a1c", "\u0a1d", "\u0a1e", - " ", "!", "\u0a1f", "\u0a20", "\u0a21", "\u0a22", "\u0a23", "\u0a24", ")", "(", "\u0a25", "\u0a26", ",", "\u0a27", ".", "\u0a28", - "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ";", "\0", "\u0a2a", "\u0a2b", "\?", - "\u0a2c", "\u0a2d", "\u0a2e", "\u0a2f", "\u0a30", "\0", "\u0a32", "\u0a33", "\0", "\u0a35", "\u0a36", "\0", "\u0a38", "\u0a39", "\u0a3c", "\0", - "\u0a3e", "\u0a3f", "\u0a40", "\u0a41", "\u0a42", "\0", "\0", "\0", "\0", "\u0a47", "\u0a48", "\0", "\0", "\u0a4b", "\u0a4c", "\u0a4d", - "\u0a51", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", - "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "\u0a70", "\u0a71", "\u0a72", "\u0a73", "\u0a74", - }, - { - "\0", "\u0b82", "\u0b83", "\u0b85", "\u0b86", "\u0b87", "\u0b88", "\u0b89", "\u0b8a", "\0", "\n", "\0", "\0", "\r", "\u0b8e", "\u0b8f", - "\u0b90", "\0", "\u0b92", "\u0b93", "\u0b94", "\u0b95", "\0", "\0", "\0", "\u0b99", "\u0b9a", "\x1b", "\0", "\u0b9c", "\0", "\u0b9e", - " ", "!", "\u0b9f", "\0", "\0", "\0", "\u0ba3", "\u0ba4", ")", "(", "\0", "\0", ",", "\0", ".", "\u0ba8", - "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ";", "\u0ba9", "\u0baa", "\0", "\?", - "\0", "\0", "\u0bae", "\u0baf", "\u0bb0", "\u0bb1", "\u0bb2", "\u0bb3", "\u0bb4", "\u0bb5", "\u0bb6", "\u0bb7", "\u0bb8", "\u0bb9", "\0", "\0", - "\u0bbe", "\u0bbf", "\u0bc0", "\u0bc1", "\u0bc2", "\0", "\0", "\0", "\u0bc6", "\u0bc7", "\u0bc8", "\0", "\u0bca", "\u0bcb", "\u0bcc", "\u0bcd", - "\u0bd0", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", - "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "\u0bd7", "\u0bf0", "\u0bf1", "\u0bf2", "\u0bf9", - }, - { - "\u0c01", "\u0c02", "\u0c03", "\u0c05", "\u0c06", "\u0c07", "\u0c08", "\u0c09", "\u0c0a", "\u0c0b", "\n", "\u0c0c", "\0", "\r", "\u0c0e", "\u0c0f", - "\u0c10", "\0", "\u0c12", "\u0c13", "\u0c14", "\u0c15", "\u0c16", "\u0c17", "\u0c18", "\u0c19", "\u0c1a", "\x1b", "\u0c1b", "\u0c1c", "\u0c1d", "\u0c1e", - " ", "!", "\u0c1f", "\u0c20", "\u0c21", "\u0c22", "\u0c23", "\u0c24", ")", "(", "\u0c25", "\u0c26", ",", "\u0c27", ".", "\u0c28", - "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ";", "\0", "\u0c2a", "\u0c2b", "\?", - "\u0c2c", "\u0c2d", "\u0c2e", "\u0c2f", "\u0c30", "\u0c31", "\u0c32", "\u0c33", "\0", "\u0c35", "\u0c36", "\u0c37", "\u0c38", "\u0c39", "\0", "\u0c3d", - "\u0c3e", "\u0c3f", "\u0c40", "\u0c41", "\u0c42", "\u0c43", "\u0c44", "\0", "\u0c46", "\u0c47", "\u0c48", "\0", "\u0c4a", "\u0c4b", "\u0c4c", "\u0c4d", - "\u0c55", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", - "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "\u0c56", "\u0c60", "\u0c61", "\u0c62", "\u0c63", - }, - { - "\u0627", "\u0622", "\u0628", "\u067b", "\u0680", "\u067e", "\u06a6", "\u062a", "\u06c2", "\u067f", "\n", "\u0679", "\u067d", "\r", "\u067a", "\u067c", - "\u062b", "\u062c", "\u0681", "\u0684", "\u0683", "\u0685", "\u0686", "\u0687", "\u062d", "\u062e", "\u062f", "\x1b", "\u068c", "\u0688", "\u0689", "\u068a", - " ", "!", "\u068f", "\u068d", "\u0630", "\u0631", "\u0691", "\u0693", ")", "(", "\u0699", "\u0632", ",", "\u0696", ".", "\u0698", - "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ";", "\u069a", "\u0633", "\u0634", "\?", - "\u0635", "\u0636", "\u0637", "\u0638", "\u0639", "\u0641", "\u0642", "\u06a9", "\u06aa", "\u06ab", "\u06af", "\u06b3", "\u06b1", "\u0644", "\u0645", "\u0646", - "\u06ba", "\u06bb", "\u06bc", "\u0648", "\u06c4", "\u06d5", "\u06c1", "\u06be", "\u0621", "\u06cc", "\u06d0", "\u06d2", "\u064d", "\u0650", "\u064f", "\u0657", - "\u0654", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", - "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "\u0655", "\u0651", "\u0653", "\u0656", "\u0670", - }, +static const uint16_t LUT_GSM7_LS16[14][128] = { + { + 0x40, 0xa3, 0x24, 0xa5, 0xe8, 0xe9, 0xf9, 0xec, 0xf2, 0xc7, 0xa, 0xd8, 0xf8, 0xd, 0xc5, 0xe5, + 0x394, 0x5f, 0x3a6, 0x393, 0x39b, 0x3a9, 0x3a0, 0x3a8, 0x3a3, 0x398, 0x39e, 0x1b, 0xc6, 0xe6, 0xdf, 0xc9, + 0x20, 0x21, 0x22, 0x23, 0xa4, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0xa1, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0xc4, 0xd6, 0xd1, 0xdc, 0xa7, + 0xbf, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0xe4, 0xf6, 0xf1, 0xfc, 0xe0 + }, + { + 0x40, 0xa3, 0x24, 0xa5, 0x20ac, 0xe9, 0xf9, 0x131, 0xf2, 0xc7, 0xa, 0x11e, 0x11f, 0xd, 0xc5, 0xe5, + 0x394, 0x5f, 0x3a6, 0x393, 0x39b, 0x3a9, 0x3a0, 0x3a8, 0x3a3, 0x398, 0x39e, 0x1b, 0x15e, 0x15f, 0xdf, 0xc9, + 0x20, 0x21, 0x22, 0x23, 0xa4, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x130, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0xc4, 0xd6, 0xd1, 0xdc, 0xa7, + 0xe7, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0xe4, 0xf6, 0xf1, 0xfc, 0xe0 + }, + { + 0x40, 0xa3, 0x24, 0xa5, 0xe8, 0xe9, 0xf9, 0xec, 0xf2, 0xc7, 0xa, 0xd8, 0xf8, 0xd, 0xc5, 0xe5, + 0x394, 0x5f, 0x3a6, 0x393, 0x39b, 0x3a9, 0x3a0, 0x3a8, 0x3a3, 0x398, 0x39e, 0x1b, 0xc6, 0xe6, 0xdf, 0xc9, + 0x20, 0x21, 0x22, 0x23, 0xa4, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0xa1, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0xc4, 0xd6, 0xd1, 0xdc, 0xa7, + 0xbf, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0xe4, 0xf6, 0xf1, 0xfc, 0xe0 + }, + { + 0x40, 0xa3, 0x24, 0xa5, 0xea, 0xe9, 0xfa, 0xed, 0xf3, 0xe7, 0xa, 0xd4, 0xf4, 0xd, 0xc1, 0xe1, + 0x394, 0x5f, 0xaa, 0xc7, 0xc0, 0x221e, 0x5e, 0x5c, 0x20ac, 0xd3, 0x7c, 0x1b, 0xc2, 0xe2, 0xca, 0xc9, + 0x20, 0x21, 0x22, 0x23, 0xba, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0xcd, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0xc3, 0xd5, 0xda, 0xdc, 0xa7, + 0x7e, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0xe3, 0xf5, 0x60, 0xfc, 0xe0 + }, + { + 0x981, 0x982, 0x983, 0x985, 0x986, 0x987, 0x988, 0x989, 0x98a, 0x98b, 0xa, 0x98c, 0x900, 0xd, 0x0, 0x98f, + 0x990, 0x900, 0x900, 0x993, 0x994, 0x995, 0x996, 0x997, 0x998, 0x999, 0x99a, 0x1b, 0x99b, 0x99c, 0x99d, 0x99e, + 0x20, 0x21, 0x99f, 0x9a0, 0x9a1, 0x9a2, 0x9a3, 0x9a4, 0x29, 0x28, 0x9a5, 0x9a6, 0x2c, 0x9a7, 0x2e, 0x9a8, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x0, 0x9aa, 0x9ab, 0x3f, + 0x9ac, 0x9ad, 0x9ae, 0x9af, 0x9b0, 0x900, 0x9b2, 0x900, 0x900, 0x900, 0x9b6, 0x9b7, 0x9b8, 0x9b9, 0x9bc, 0x9bd, + 0x9be, 0x9bf, 0x9c0, 0x9c1, 0x9c2, 0x9c3, 0x9c4, 0x900, 0x900, 0x9c7, 0x9c8, 0x900, 0x900, 0x9cb, 0x9cc, 0x9cd, + 0x9ce, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x9d7, 0x9dc, 0x9dd, 0x9f0, 0x9f1 + }, + { + 0xa81, 0xa82, 0xa83, 0xa85, 0xa86, 0xa87, 0xa88, 0xa89, 0xa8a, 0xa8b, 0xa, 0xa8c, 0xa8d, 0xd, 0x0, 0xa8f, + 0xa90, 0xa91, 0xa00, 0xa93, 0xa94, 0xa95, 0xa96, 0xa97, 0xa98, 0xa99, 0xa9a, 0x1b, 0xa9b, 0xa9c, 0xa9d, 0xa9e, + 0x20, 0x21, 0xa9f, 0xaa0, 0xaa1, 0xaa2, 0xaa3, 0xaa4, 0x29, 0x28, 0xaa5, 0xaa6, 0x2c, 0xaa7, 0x2e, 0xaa8, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x0, 0xaaa, 0xaab, 0x3f, + 0xaac, 0xaad, 0xaae, 0xaaf, 0xab0, 0xa00, 0xab2, 0xab3, 0xa00, 0xab5, 0xab6, 0xab7, 0xab8, 0xab9, 0xabc, 0xabd, + 0xabe, 0xabf, 0xac0, 0xac1, 0xac2, 0xac3, 0xac4, 0xac5, 0xa00, 0xac7, 0xac8, 0xac9, 0xa00, 0xacb, 0xacc, 0xacd, + 0xad0, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0xae0, 0xae1, 0xae2, 0xae3, 0xaf1 + }, + { + 0x901, 0x902, 0x903, 0x905, 0x906, 0x907, 0x908, 0x909, 0x90a, 0x90b, 0xa, 0x90c, 0x90d, 0xd, 0x90e, 0x90f, + 0x910, 0x911, 0x912, 0x913, 0x914, 0x915, 0x916, 0x917, 0x918, 0x919, 0x91a, 0x1b, 0x91b, 0x91c, 0x91d, 0x91e, + 0x20, 0x21, 0x91f, 0x920, 0x921, 0x922, 0x923, 0x924, 0x29, 0x28, 0x925, 0x926, 0x2c, 0x927, 0x2e, 0x928, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x929, 0x92a, 0x92b, 0x3f, + 0x92c, 0x92d, 0x92e, 0x92f, 0x930, 0x931, 0x932, 0x933, 0x934, 0x935, 0x936, 0x937, 0x938, 0x939, 0x93c, 0x93d, + 0x93e, 0x93f, 0x940, 0x941, 0x942, 0x943, 0x944, 0x945, 0x946, 0x947, 0x948, 0x949, 0x94a, 0x94b, 0x94c, 0x94d, + 0x950, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x972, 0x97b, 0x97c, 0x97e, 0x97f + }, + { + 0x900, 0xc82, 0xc83, 0xc85, 0xc86, 0xc87, 0xc88, 0xc89, 0xc8a, 0xc8b, 0xa, 0xc8c, 0xc00, 0xd, 0xc8e, 0xc8f, + 0xc90, 0xc00, 0xc92, 0xc93, 0xc94, 0xc95, 0xc96, 0xc97, 0xc98, 0xc99, 0xc9a, 0x1b, 0xc9b, 0xc9c, 0xc9d, 0xc9e, + 0x20, 0x21, 0xc9f, 0xca0, 0xca1, 0xca2, 0xca3, 0xca4, 0x29, 0x28, 0xca5, 0xca6, 0x2c, 0xca7, 0x2e, 0xca8, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x0, 0xcaa, 0xcab, 0x3f, + 0xcac, 0xcad, 0xcae, 0xcaf, 0xcb0, 0xcb1, 0xcb2, 0xcb3, 0xc00, 0xcb5, 0xcb6, 0xcb7, 0xcb8, 0xcb9, 0xcbc, 0xcbd, + 0xcbe, 0xcbf, 0xcc0, 0xcc1, 0xcc2, 0xcc3, 0xcc4, 0xc00, 0xcc6, 0xcc7, 0xcc8, 0xc00, 0xcca, 0xccb, 0xccc, 0xccd, + 0xcd5, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0xcd6, 0xce0, 0xce1, 0xce2, 0xce3 + }, + { + 0xc00, 0xd02, 0xd03, 0xd05, 0xd06, 0xd07, 0xd08, 0xd09, 0xd0a, 0xd0b, 0xa, 0xd0c, 0xd00, 0xd, 0xd0e, 0xd0f, + 0xd10, 0xd00, 0xd12, 0xd13, 0xd14, 0xd15, 0xd16, 0xd17, 0xd18, 0xd19, 0xd1a, 0x1b, 0xd1b, 0xd1c, 0xd1d, 0xd1e, + 0x20, 0x21, 0xd1f, 0xd20, 0xd21, 0xd22, 0xd23, 0xd24, 0x29, 0x28, 0xd25, 0xd26, 0x2c, 0xd27, 0x2e, 0xd28, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x0, 0xd2a, 0xd2b, 0x3f, + 0xd2c, 0xd2d, 0xd2e, 0xd2f, 0xd30, 0xd31, 0xd32, 0xd33, 0xd34, 0xd35, 0xd36, 0xd37, 0xd38, 0xd39, 0xd00, 0xd3d, + 0xd3e, 0xd3f, 0xd40, 0xd41, 0xd42, 0xd43, 0xd44, 0xd00, 0xd46, 0xd47, 0xd48, 0xd00, 0xd4a, 0xd4b, 0xd4c, 0xd4d, + 0xd57, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0xd60, 0xd61, 0xd62, 0xd63, 0xd79 + }, + { + 0xb01, 0xb02, 0xb03, 0xb05, 0xb06, 0xb07, 0xb08, 0xb09, 0xb0a, 0xb0b, 0xa, 0xb0c, 0xb00, 0xd, 0x0, 0xb0f, + 0xb10, 0xb00, 0xb00, 0xb13, 0xb14, 0xb15, 0xb16, 0xb17, 0xb18, 0xb19, 0xb1a, 0x1b, 0xb1b, 0xb1c, 0xb1d, 0xb1e, + 0x20, 0x21, 0xb1f, 0xb20, 0xb21, 0xb22, 0xb23, 0xb24, 0x29, 0x28, 0xb25, 0xb26, 0x2c, 0xb27, 0x2e, 0xb28, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x0, 0xb2a, 0xb2b, 0x3f, + 0xb2c, 0xb2d, 0xb2e, 0xb2f, 0xb30, 0xb00, 0xb32, 0xb33, 0xb00, 0xb35, 0xb36, 0xb37, 0xb38, 0xb39, 0xb3c, 0xb3d, + 0xb3e, 0xb3f, 0xb40, 0xb41, 0xb42, 0xb43, 0xb44, 0xb00, 0xb00, 0xb47, 0xb48, 0xb00, 0xb00, 0xb4b, 0xb4c, 0xb4d, + 0xb56, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0xb57, 0xb60, 0xb61, 0xb62, 0xb63 + }, + { + 0xa01, 0xa02, 0xa03, 0xa05, 0xa06, 0xa07, 0xa08, 0xa09, 0xa0a, 0xa00, 0xa, 0x0, 0x0, 0xd, 0x0, 0xa0f, + 0xa10, 0xa00, 0xa00, 0xa13, 0xa14, 0xa15, 0xa16, 0xa17, 0xa18, 0xa19, 0xa1a, 0x1b, 0xa1b, 0xa1c, 0xa1d, 0xa1e, + 0x20, 0x21, 0xa1f, 0xa20, 0xa21, 0xa22, 0xa23, 0xa24, 0x29, 0x28, 0xa25, 0xa26, 0x2c, 0xa27, 0x2e, 0xa28, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x0, 0xa2a, 0xa2b, 0x3f, + 0xa2c, 0xa2d, 0xa2e, 0xa2f, 0xa30, 0xa00, 0xa32, 0xa33, 0xa00, 0xa35, 0xa36, 0xa00, 0xa38, 0xa39, 0xa3c, 0xa00, + 0xa3e, 0xa3f, 0xa40, 0xa41, 0xa42, 0xa00, 0xa00, 0xa00, 0xa00, 0xa47, 0xa48, 0xa00, 0xa00, 0xa4b, 0xa4c, 0xa4d, + 0xa51, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0xa70, 0xa71, 0xa72, 0xa73, 0xa74 + }, + { + 0xa00, 0xb82, 0xb83, 0xb85, 0xb86, 0xb87, 0xb88, 0xb89, 0xb8a, 0xb00, 0xa, 0x0, 0x0, 0xd, 0xb8e, 0xb8f, + 0xb90, 0xb00, 0xb92, 0xb93, 0xb94, 0xb95, 0xb00, 0xb00, 0xb00, 0xb99, 0xb9a, 0x1b, 0x0, 0xb9c, 0xb00, 0xb9e, + 0x20, 0x21, 0xb9f, 0xb00, 0xb00, 0xb00, 0xba3, 0xba4, 0x29, 0x28, 0x0, 0x0, 0x2c, 0x0, 0x2e, 0xba8, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0xba9, 0xbaa, 0xb00, 0x3f, + 0x0, 0x0, 0xbae, 0xbaf, 0xbb0, 0xbb1, 0xbb2, 0xbb3, 0xbb4, 0xbb5, 0xbb6, 0xbb7, 0xbb8, 0xbb9, 0xb00, 0xb00, + 0xbbe, 0xbbf, 0xbc0, 0xbc1, 0xbc2, 0xb00, 0xb00, 0xb00, 0xbc6, 0xbc7, 0xbc8, 0xb00, 0xbca, 0xbcb, 0xbcc, 0xbcd, + 0xbd0, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0xbd7, 0xbf0, 0xbf1, 0xbf2, 0xbf9 + }, + { + 0xc01, 0xc02, 0xc03, 0xc05, 0xc06, 0xc07, 0xc08, 0xc09, 0xc0a, 0xc0b, 0xa, 0xc0c, 0xc00, 0xd, 0xc0e, 0xc0f, + 0xc10, 0xc00, 0xc12, 0xc13, 0xc14, 0xc15, 0xc16, 0xc17, 0xc18, 0xc19, 0xc1a, 0x1b, 0xc1b, 0xc1c, 0xc1d, 0xc1e, + 0x20, 0x21, 0xc1f, 0xc20, 0xc21, 0xc22, 0xc23, 0xc24, 0x29, 0x28, 0xc25, 0xc26, 0x2c, 0xc27, 0x2e, 0xc28, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x0, 0xc2a, 0xc2b, 0x3f, + 0xc2c, 0xc2d, 0xc2e, 0xc2f, 0xc30, 0xc31, 0xc32, 0xc33, 0xc00, 0xc35, 0xc36, 0xc37, 0xc38, 0xc39, 0xc00, 0xc3d, + 0xc3e, 0xc3f, 0xc40, 0xc41, 0xc42, 0xc43, 0xc44, 0xc00, 0xc46, 0xc47, 0xc48, 0xc00, 0xc4a, 0xc4b, 0xc4c, 0xc4d, + 0xc55, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0xc56, 0xc60, 0xc61, 0xc62, 0xc63 + }, + { + 0x627, 0x622, 0x628, 0x67b, 0x680, 0x67e, 0x6a6, 0x62a, 0x6c2, 0x67f, 0xa, 0x679, 0x67d, 0xd, 0x67a, 0x67c, + 0x62b, 0x62c, 0x681, 0x684, 0x683, 0x685, 0x686, 0x687, 0x62d, 0x62e, 0x62f, 0x1b, 0x68c, 0x688, 0x689, 0x68a, + 0x20, 0x21, 0x68f, 0x68d, 0x630, 0x631, 0x691, 0x693, 0x29, 0x28, 0x699, 0x632, 0x2c, 0x696, 0x2e, 0x698, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x69a, 0x633, 0x634, 0x3f, + 0x635, 0x636, 0x637, 0x638, 0x639, 0x641, 0x642, 0x6a9, 0x6aa, 0x6ab, 0x6af, 0x6b3, 0x6b1, 0x644, 0x645, 0x646, + 0x6ba, 0x6bb, 0x6bc, 0x648, 0x6c4, 0x6d5, 0x6c1, 0x6be, 0x621, 0x6cc, 0x6d0, 0x6d2, 0x64d, 0x650, 0x64f, 0x657, + 0x654, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x655, 0x651, 0x653, 0x656, 0x670 + } }; -static const char *const LUT_GSM7_SS[14][128] = { - { - "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\f", "\0", "\0", "\0", "\0", "\0", - "\0", "\0", "\0", "\0", "^", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", - "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "{", "}", "\0", "\0", "\0", "\0", "\0", "\\", - "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "[", "~", "]", "\0", - "|", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", - "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", - "\0", "\0", "\0", "\0", "\0", "\u20ac", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", - "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", - }, - { - "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\f", "\0", "\0", "\0", "\0", "\0", - "\0", "\0", "\0", "\0", "^", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", - "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "{", "}", "\0", "\0", "\0", "\0", "\0", "\\", - "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "[", "~", "]", "\0", - "|", "\0", "\0", "\0", "\0", "\0", "\0", "\u011e", "\0", "\u0130", "\0", "\0", "\0", "\0", "\0", "\0", - "\0", "\0", "\0", "\u015e", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", - "\0", "\0", "\0", "\u00e7", "\0", "\u20ac", "\0", "\u011f", "\0", "\u0131", "\0", "\0", "\0", "\0", "\0", "\0", - "\0", "\0", "\0", "\u015f", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", - }, - { - "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\u00e7", "\f", "\0", "\0", "\0", "\0", "\0", - "\0", "\0", "\0", "\0", "^", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", - "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "{", "}", "\0", "\0", "\0", "\0", "\0", "\\", - "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "[", "~", "]", "\0", - "|", "\u00c1", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\u00cd", "\0", "\0", "\0", "\0", "\0", "\u00d3", - "\0", "\0", "\0", "\0", "\0", "\u00da", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", - "\0", "\u00e1", "\0", "\0", "\0", "\u20ac", "\0", "\0", "\0", "\u00ed", "\0", "\0", "\0", "\0", "\0", "\u00f3", - "\0", "\0", "\0", "\0", "\0", "\u00fa", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", - }, - { - "\0", "\0", "\0", "\0", "\0", "\u00ea", "\0", "\0", "\0", "\u00e7", "\f", "\u00d4", "\u00f4", "\0", "\u00c1", "\u00e1", - "\0", "\0", "\u03a6", "\u0393", "^", "\u03a9", "\u03a0", "\u03a8", "\u03a3", "\u0398", "\0", "\0", "\0", "\0", "\0", "\u00ca", - "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "{", "}", "\0", "\0", "\0", "\0", "\0", "\\", - "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "[", "~", "]", "\0", - "|", "\u00c0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\u00cd", "\0", "\0", "\0", "\0", "\0", "\u00d3", - "\0", "\0", "\0", "\0", "\0", "\u00da", "\0", "\0", "\0", "\0", "\0", "\u00c3", "\u00d5", "\0", "\0", "\0", - "\0", "\u00c2", "\0", "\0", "\0", "\u20ac", "\0", "\0", "\0", "\u00ed", "\0", "\0", "\0", "\0", "\0", "\u00f3", - "\0", "\0", "\0", "\0", "\0", "\u00fa", "\0", "\0", "\0", "\0", "\0", "\u00e3", "\u00f5", "\0", "\0", "\u00e2", - }, - { - "@", "\u00a3", "$", "\u00a5", "\u00bf", "\"", "\u00a4", "%", "&", "\'", "\f", "*", "+", "\0", "-", "/", - "<", "=", ">", "\u00a1", "^", "\u00a1", "_", "#", "*", "\u09e6", "\u09e7", "\0", "\u09e8", "\u09e9", "\u09ea", "\u09eb", - "\u09ec", "\u09ed", "\u09ee", "\u09ef", "\u09df", "\u09e0", "\u09e1", "\u09e2", "{", "}", "\u09e3", "\u09f2", "\u09f3", "\u09f4", "\u09f5", "\\", - "\u09f6", "\u09f7", "\u09f8", "\u09f9", "\u09fa", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "[", "~", "]", "\0", - "|", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", - "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "\0", "\0", "\0", "\0", "\0", - "\0", "\0", "\0", "\0", "\0", "\u20ac", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", - "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", - }, - { - "@", "\u00a3", "$", "\u00a5", "\u00bf", "\"", "\u00a4", "%", "&", "\'", "\f", "*", "+", "\0", "-", "/", - "<", "=", ">", "\u00a1", "^", "\u00a1", "_", "#", "*", "\u0964", "\u0965", "\0", "\u0ae6", "\u0ae7", "\u0ae8", "\u0ae9", - "\u0aea", "\u0aeb", "\u0aec", "\u0aed", "\u0aee", "\u0aef", "\0", "\0", "{", "}", "\0", "\0", "\0", "\0", "\0", "\\", - "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "[", "~", "]", "\0", - "|", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", - "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "\0", "\0", "\0", "\0", "\0", - "\0", "\0", "\0", "\0", "\0", "\u20ac", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", - "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", - }, - { - "@", "\u00a3", "$", "\u00a5", "\u00bf", "\"", "\u00a4", "%", "&", "\'", "\f", "*", "+", "\0", "-", "/", - "<", "=", ">", "\u00a1", "^", "\u00a1", "_", "#", "*", "\u0964", "\u0965", "\0", "\u0966", "\u0967", "\u0968", "\u0969", - "\u096a", "\u096b", "\u096c", "\u096d", "\u096e", "\u096f", "\u0951", "\u0952", "{", "}", "\u0953", "\u0954", "\u0958", "\u0959", "\u095a", "\\", - "\u095b", "\u095c", "\u095d", "\u095e", "\u095f", "\u0960", "\u0961", "\u0962", "\u0963", "\u0970", "\u0971", "\0", "[", "~", "]", "\0", - "|", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", - "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "\0", "\0", "\0", "\0", "\0", - "\0", "\0", "\0", "\0", "\0", "\u20ac", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", - "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", - }, - { - "@", "\u00a3", "$", "\u00a5", "\u00bf", "\"", "\u00a4", "%", "&", "\'", "\f", "*", "+", "\0", "-", "/", - "<", "=", ">", "\u00a1", "^", "\u00a1", "_", "#", "*", "\u0964", "\u0965", "\0", "\u0ce6", "\u0ce7", "\u0ce8", "\u0ce9", - "\u0cea", "\u0ceb", "\u0cec", "\u0ced", "\u0cee", "\u0cef", "\u0cde", "\u0cf1", "{", "}", "\u0cf2", "\0", "\0", "\0", "\0", "\\", - "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "[", "~", "]", "\0", - "|", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", - "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "\0", "\0", "\0", "\0", "\0", - "\0", "\0", "\0", "\0", "\0", "\u20ac", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", - "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", - }, - { - "@", "\u00a3", "$", "\u00a5", "\u00bf", "\"", "\u00a4", "%", "&", "\'", "\f", "*", "+", "\0", "-", "/", - "<", "=", ">", "\u00a1", "^", "\u00a1", "_", "#", "*", "\u0964", "\u0965", "\0", "\u0d66", "\u0d67", "\u0d68", "\u0d69", - "\u0d6a", "\u0d6b", "\u0d6c", "\u0d6d", "\u0d6e", "\u0d6f", "\u0d70", "\u0d71", "{", "}", "\u0d72", "\u0d73", "\u0d74", "\u0d75", "\u0d7a", "\\", - "\u0d7b", "\u0d7c", "\u0d7d", "\u0d7e", "\u0d7f", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "[", "~", "]", "\0", - "|", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", - "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "\0", "\0", "\0", "\0", "\0", - "\0", "\0", "\0", "\0", "\0", "\u20ac", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", - "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", - }, - { - "@", "\u00a3", "$", "\u00a5", "\u00bf", "\"", "\u00a4", "%", "&", "\'", "\f", "*", "+", "\0", "-", "/", - "<", "=", ">", "\u00a1", "^", "\u00a1", "_", "#", "*", "\u0964", "\u0965", "\0", "\u0b66", "\u0b67", "\u0b68", "\u0b69", - "\u0b6a", "\u0b6b", "\u0b6c", "\u0b6d", "\u0b6e", "\u0b6f", "\u0b5c", "\u0b5d", "{", "}", "\u0b5f", "\u0b70", "\u0b71", "\0", "\0", "\\", - "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "[", "~", "]", "\0", - "|", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", - "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "\0", "\0", "\0", "\0", "\0", - "\0", "\0", "\0", "\0", "\0", "\u20ac", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", - "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", - }, - { - "@", "\u00a3", "$", "\u00a5", "\u00bf", "\"", "\u00a4", "%", "&", "\'", "\f", "*", "+", "\0", "-", "/", - "<", "=", ">", "\u00a1", "^", "\u00a1", "_", "#", "*", "\u0964", "\u0965", "\0", "\u0a66", "\u0a67", "\u0a68", "\u0a69", - "\u0a6a", "\u0a6b", "\u0a6c", "\u0a6d", "\u0a6e", "\u0a6f", "\u0a59", "\u0a5a", "{", "}", "\u0a5b", "\u0a5c", "\u0a5e", "\u0a75", "\0", "\\", - "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "[", "~", "]", "\0", - "|", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", - "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "\0", "\0", "\0", "\0", "\0", - "\0", "\0", "\0", "\0", "\0", "\u20ac", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", - "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", - }, - { - "@", "\u00a3", "$", "\u00a5", "\u00bf", "\"", "\u00a4", "%", "&", "\'", "\f", "*", "+", "\0", "-", "/", - "<", "=", ">", "\u00a1", "^", "\u00a1", "_", "#", "*", "\u0964", "\u0965", "\0", "\u0be6", "\u0be7", "\u0be8", "\u0be9", - "\u0bea", "\u0beb", "\u0bec", "\u0bed", "\u0bee", "\u0bef", "\u0bf3", "\u0bf4", "{", "}", "\u0bf5", "\u0bf6", "\u0bf7", "\u0bf8", "\u0bfa", "\\", - "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "[", "~", "]", "\0", - "|", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", - "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "\0", "\0", "\0", "\0", "\0", - "\0", "\0", "\0", "\0", "\0", "\u20ac", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", - "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", - }, - { - "@", "\u00a3", "$", "\u00a5", "\u00bf", "\"", "\u00a4", "%", "&", "\'", "\f", "*", "+", "\0", "-", "/", - "<", "=", ">", "\u00a1", "^", "\u00a1", "_", "#", "*", "\0", "\0", "\0", "\u0c66", "\u0c67", "\u0c68", "\u0c69", - "\u0c6a", "\u0c6b", "\u0c6c", "\u0c6d", "\u0c6e", "\u0c6f", "\u0c58", "\u0c59", "{", "}", "\u0c78", "\u0c79", "\u0c7a", "\u0c7b", "\u0c7c", "\\", - "\u0c7d", "\u0c7e", "\u0c7f", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "[", "~", "]", "\0", - "|", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", - "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "\0", "\0", "\0", "\0", "\0", - "\0", "\0", "\0", "\0", "\0", "\u20ac", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", - "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", - }, - { - "@", "\u00a3", "$", "\u00a5", "\u00bf", "\"", "\u00a4", "%", "&", "\'", "\f", "*", "+", "\0", "-", "/", - "<", "=", ">", "\u00a1", "^", "\u00a1", "_", "#", "*", "\u0600", "\u0601", "\0", "\u06f0", "\u06f1", "\u06f2", "\u06f3", - "\u06f4", "\u06f5", "\u06f6", "\u06f7", "\u06f8", "\u06f9", "\u060c", "\u060d", "{", "}", "\u060e", "\u060f", "\u0610", "\u0611", "\u0612", "\\", - "\u0613", "\u0614", "\u061b", "\u061f", "\u0640", "\u0652", "\u0658", "\u066b", "\u066c", "\u0672", "\u0673", "\u06cd", "[", "~", "]", "\u06d4", - "|", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", - "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "\0", "\0", "\0", "\0", "\0", - "\0", "\0", "\0", "\0", "\0", "\u20ac", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", - "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", "\0", - }, +static const uint16_t LUT_GSM7_SS16[14][128] = { + { + 0x600, 0x600, 0x600, 0x600, 0x600, 0x600, 0x600, 0x600, 0x600, 0x600, 0xc, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x5e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7b, 0x7d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5c, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5b, 0x7e, 0x5d, 0x0, + 0x7c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x20ac, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, + 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000 + }, + { + 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0xc, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x5e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7b, 0x7d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5c, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5b, 0x7e, 0x5d, 0x0, + 0x7c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x11e, 0x100, 0x130, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, + 0x100, 0x100, 0x100, 0x15e, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, + 0x100, 0x100, 0x100, 0xe7, 0x0, 0x20ac, 0x2000, 0x11f, 0x100, 0x131, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, + 0x100, 0x100, 0x100, 0x15f, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100 + }, + { + 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0xe7, 0xc, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x5e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7b, 0x7d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5c, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5b, 0x7e, 0x5d, 0x0, + 0x7c, 0xc1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xcd, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd3, + 0x0, 0x0, 0x0, 0x0, 0x0, 0xda, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0xe1, 0x0, 0x0, 0x0, 0x20ac, 0x2000, 0x2000, 0x2000, 0xed, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf3, + 0x0, 0x0, 0x0, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 + }, + { + 0x0, 0x0, 0x0, 0x0, 0x0, 0xea, 0x0, 0x0, 0x0, 0xe7, 0xc, 0xd4, 0xf4, 0x0, 0xc1, 0xe1, + 0x0, 0x0, 0x3a6, 0x393, 0x5e, 0x3a9, 0x3a0, 0x3a8, 0x3a3, 0x398, 0x300, 0x300, 0x300, 0x300, 0x300, 0xca, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7b, 0x7d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5c, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5b, 0x7e, 0x5d, 0x0, + 0x7c, 0xc0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xcd, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd3, + 0x0, 0x0, 0x0, 0x0, 0x0, 0xda, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc3, 0xd5, 0x0, 0x0, 0x0, + 0x0, 0xc2, 0x0, 0x0, 0x0, 0x20ac, 0x2000, 0x2000, 0x2000, 0xed, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf3, + 0x0, 0x0, 0x0, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe3, 0xf5, 0x0, 0x0, 0xe2 + }, + { + 0x40, 0xa3, 0x24, 0xa5, 0xbf, 0x22, 0xa4, 0x25, 0x26, 0x27, 0xc, 0x2a, 0x2b, 0x0, 0x2d, 0x2f, + 0x3c, 0x3d, 0x3e, 0xa1, 0x5e, 0xa1, 0x5f, 0x23, 0x2a, 0x9e6, 0x9e7, 0x900, 0x9e8, 0x9e9, 0x9ea, 0x9eb, + 0x9ec, 0x9ed, 0x9ee, 0x9ef, 0x9df, 0x9e0, 0x9e1, 0x9e2, 0x7b, 0x7d, 0x9e3, 0x9f2, 0x9f3, 0x9f4, 0x9f5, 0x5c, + 0x9f6, 0x9f7, 0x9f8, 0x9f9, 0x9fa, 0x900, 0x900, 0x900, 0x900, 0x900, 0x900, 0x900, 0x5b, 0x7e, 0x5d, 0x0, + 0x7c, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x20ac, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, + 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000 + }, + { + 0x40, 0xa3, 0x24, 0xa5, 0xbf, 0x22, 0xa4, 0x25, 0x26, 0x27, 0xc, 0x2a, 0x2b, 0x0, 0x2d, 0x2f, + 0x3c, 0x3d, 0x3e, 0xa1, 0x5e, 0xa1, 0x5f, 0x23, 0x2a, 0x964, 0x965, 0x900, 0xae6, 0xae7, 0xae8, 0xae9, + 0xaea, 0xaeb, 0xaec, 0xaed, 0xaee, 0xaef, 0xa00, 0xa00, 0x7b, 0x7d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5c, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5b, 0x7e, 0x5d, 0x0, + 0x7c, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x20ac, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, + 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000 + }, + { + 0x40, 0xa3, 0x24, 0xa5, 0xbf, 0x22, 0xa4, 0x25, 0x26, 0x27, 0xc, 0x2a, 0x2b, 0x0, 0x2d, 0x2f, + 0x3c, 0x3d, 0x3e, 0xa1, 0x5e, 0xa1, 0x5f, 0x23, 0x2a, 0x964, 0x965, 0x900, 0x966, 0x967, 0x968, 0x969, + 0x96a, 0x96b, 0x96c, 0x96d, 0x96e, 0x96f, 0x951, 0x952, 0x7b, 0x7d, 0x953, 0x954, 0x958, 0x959, 0x95a, 0x5c, + 0x95b, 0x95c, 0x95d, 0x95e, 0x95f, 0x960, 0x961, 0x962, 0x963, 0x970, 0x971, 0x900, 0x5b, 0x7e, 0x5d, 0x0, + 0x7c, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x20ac, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, + 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000 + }, + { + 0x40, 0xa3, 0x24, 0xa5, 0xbf, 0x22, 0xa4, 0x25, 0x26, 0x27, 0xc, 0x2a, 0x2b, 0x0, 0x2d, 0x2f, + 0x3c, 0x3d, 0x3e, 0xa1, 0x5e, 0xa1, 0x5f, 0x23, 0x2a, 0x964, 0x965, 0x900, 0xce6, 0xce7, 0xce8, 0xce9, + 0xcea, 0xceb, 0xcec, 0xced, 0xcee, 0xcef, 0xcde, 0xcf1, 0x7b, 0x7d, 0xcf2, 0xc00, 0xc00, 0xc00, 0xc00, 0x5c, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5b, 0x7e, 0x5d, 0x0, + 0x7c, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x20ac, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, + 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000 + }, + { + 0x40, 0xa3, 0x24, 0xa5, 0xbf, 0x22, 0xa4, 0x25, 0x26, 0x27, 0xc, 0x2a, 0x2b, 0x0, 0x2d, 0x2f, + 0x3c, 0x3d, 0x3e, 0xa1, 0x5e, 0xa1, 0x5f, 0x23, 0x2a, 0x964, 0x965, 0x900, 0xd66, 0xd67, 0xd68, 0xd69, + 0xd6a, 0xd6b, 0xd6c, 0xd6d, 0xd6e, 0xd6f, 0xd70, 0xd71, 0x7b, 0x7d, 0xd72, 0xd73, 0xd74, 0xd75, 0xd7a, 0x5c, + 0xd7b, 0xd7c, 0xd7d, 0xd7e, 0xd7f, 0xd00, 0xd00, 0xd00, 0xd00, 0xd00, 0xd00, 0xd00, 0x5b, 0x7e, 0x5d, 0x0, + 0x7c, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x20ac, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, + 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000 + }, + { + 0x40, 0xa3, 0x24, 0xa5, 0xbf, 0x22, 0xa4, 0x25, 0x26, 0x27, 0xc, 0x2a, 0x2b, 0x0, 0x2d, 0x2f, + 0x3c, 0x3d, 0x3e, 0xa1, 0x5e, 0xa1, 0x5f, 0x23, 0x2a, 0x964, 0x965, 0x900, 0xb66, 0xb67, 0xb68, 0xb69, + 0xb6a, 0xb6b, 0xb6c, 0xb6d, 0xb6e, 0xb6f, 0xb5c, 0xb5d, 0x7b, 0x7d, 0xb5f, 0xb70, 0xb71, 0xb00, 0xb00, 0x5c, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5b, 0x7e, 0x5d, 0x0, + 0x7c, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x20ac, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, + 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000 + }, + { + 0x40, 0xa3, 0x24, 0xa5, 0xbf, 0x22, 0xa4, 0x25, 0x26, 0x27, 0xc, 0x2a, 0x2b, 0x0, 0x2d, 0x2f, + 0x3c, 0x3d, 0x3e, 0xa1, 0x5e, 0xa1, 0x5f, 0x23, 0x2a, 0x964, 0x965, 0x900, 0xa66, 0xa67, 0xa68, 0xa69, + 0xa6a, 0xa6b, 0xa6c, 0xa6d, 0xa6e, 0xa6f, 0xa59, 0xa5a, 0x7b, 0x7d, 0xa5b, 0xa5c, 0xa5e, 0xa75, 0xa00, 0x5c, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5b, 0x7e, 0x5d, 0x0, + 0x7c, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x20ac, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, + 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000 + }, + { + 0x40, 0xa3, 0x24, 0xa5, 0xbf, 0x22, 0xa4, 0x25, 0x26, 0x27, 0xc, 0x2a, 0x2b, 0x0, 0x2d, 0x2f, + 0x3c, 0x3d, 0x3e, 0xa1, 0x5e, 0xa1, 0x5f, 0x23, 0x2a, 0x964, 0x965, 0x900, 0xbe6, 0xbe7, 0xbe8, 0xbe9, + 0xbea, 0xbeb, 0xbec, 0xbed, 0xbee, 0xbef, 0xbf3, 0xbf4, 0x7b, 0x7d, 0xbf5, 0xbf6, 0xbf7, 0xbf8, 0xbfa, 0x5c, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5b, 0x7e, 0x5d, 0x0, + 0x7c, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x20ac, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, + 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000 + }, + { + 0x40, 0xa3, 0x24, 0xa5, 0xbf, 0x22, 0xa4, 0x25, 0x26, 0x27, 0xc, 0x2a, 0x2b, 0x0, 0x2d, 0x2f, + 0x3c, 0x3d, 0x3e, 0xa1, 0x5e, 0xa1, 0x5f, 0x23, 0x2a, 0x0, 0x0, 0x0, 0xc66, 0xc67, 0xc68, 0xc69, + 0xc6a, 0xc6b, 0xc6c, 0xc6d, 0xc6e, 0xc6f, 0xc58, 0xc59, 0x7b, 0x7d, 0xc78, 0xc79, 0xc7a, 0xc7b, 0xc7c, 0x5c, + 0xc7d, 0xc7e, 0xc7f, 0xc00, 0xc00, 0xc00, 0xc00, 0xc00, 0xc00, 0xc00, 0xc00, 0xc00, 0x5b, 0x7e, 0x5d, 0x0, + 0x7c, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x20ac, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, + 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000 + }, + { + 0x40, 0xa3, 0x24, 0xa5, 0xbf, 0x22, 0xa4, 0x25, 0x26, 0x27, 0xc, 0x2a, 0x2b, 0x0, 0x2d, 0x2f, + 0x3c, 0x3d, 0x3e, 0xa1, 0x5e, 0xa1, 0x5f, 0x23, 0x2a, 0x600, 0x601, 0x600, 0x6f0, 0x6f1, 0x6f2, 0x6f3, + 0x6f4, 0x6f5, 0x6f6, 0x6f7, 0x6f8, 0x6f9, 0x60c, 0x60d, 0x7b, 0x7d, 0x60e, 0x60f, 0x610, 0x611, 0x612, 0x5c, + 0x613, 0x614, 0x61b, 0x61f, 0x640, 0x652, 0x658, 0x66b, 0x66c, 0x672, 0x673, 0x6cd, 0x5b, 0x7e, 0x5d, 0x6d4, + 0x7c, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x20ac, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, + 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000 + } }; static const int LUT_GSM7_REV1[] = { diff --git a/helpers.c b/helpers.c index 758aa358..d8a5afc3 100644 --- a/helpers.c +++ b/helpers.c @@ -17,23 +17,33 @@ #include "helpers.h" #include "chan_dongle.h" /* devices */ #include "at_command.h" -#include "pdu.h" /* pdu_digit2code() */ +#include "error.h" +// #include "pdu.h" /* pdu_digit2code() */ static int is_valid_ussd_string(const char* number) { - for(; *number; number++) - if(pdu_digit2code(*number) == 0) - return 0; - + for (; *number; number++) { + if (*number >= '0' && *number <= '9' || *number == '*' || *number == '#') { + continue; + } + return 0; + } return 1; } #/* */ -EXPORT_DEF int is_valid_phone_number(const char* number) +EXPORT_DEF int is_valid_phone_number(const char *number) { - if(number[0] == '+') + if (number[0] == '+') { number++; - return is_valid_ussd_string(number); + } + for (; *number; number++) { + if (*number >= '0' && *number <= '9') { + continue; + } + return 0; + } + return 1; } @@ -77,123 +87,118 @@ EXPORT_DEF int get_at_clir_value (struct pvt* pvt, int clir) return res; } -typedef int (*at_cmd_f)(struct cpvt*, const char*, const char*, unsigned, int, void **); +typedef int (*at_cmd_f)(struct cpvt*, const char*, const char*, unsigned, int, const char*, size_t); -#/* */ -static const char* send2(const char* dev_name, int * status, int online, const char* emsg, const char* okmsg, at_cmd_f func, const char* arg1, const char * arg2, unsigned arg3, int arg4, void ** arg5) +void free_pvt(struct pvt *pvt) { - struct pvt* pvt; - const char* msg; - - if(status) - *status = 0; - pvt = find_device_ext(dev_name, &msg); - if(pvt) - { - if(pvt->connected && (!online || (pvt->initialized && pvt->gsm_registered))) - { - if((*func) (&pvt->sys_chan, arg1, arg2, arg3, arg4, arg5)) - { - msg = emsg; - ast_log (LOG_ERROR, "[%s] %s\n", PVT_ID(pvt), emsg); - } - else - { - msg = okmsg; - if(status) - *status = 1; - } + ast_mutex_unlock(&pvt->lock); +} +struct pvt *get_pvt(const char *dev_name, int online) +{ + struct pvt *pvt; + pvt = find_device_ext(dev_name); + if (pvt) { + if (pvt->connected && (!online || (pvt->initialized && pvt->gsm_registered))) { + return pvt; } - else - msg = "Device not connected / initialized / registered"; - ast_mutex_unlock (&pvt->lock); + free_pvt(pvt); } - return msg; + chan_dongle_err = E_DEVICE_DISCONNECTED; + return NULL; } #/* */ -EXPORT_DEF const char* send_ussd(const char* dev_name, const char* ussd, int * status, void ** id) +EXPORT_DEF int send_ussd(const char *dev_name, const char *ussd) { - if(is_valid_ussd_string(ussd)) - return send2(dev_name, status, 1, "Error adding USSD command to queue", "USSD queued for send", at_enqueue_ussd, ussd, 0, 0, 0, id); - if(status) - *status = 0; - return "Invalid USSD"; + if (!is_valid_ussd_string(ussd)) { + chan_dongle_err = E_INVALID_USSD; + return -1; + } + + struct pvt *pvt = get_pvt(dev_name, 1); + if (!pvt) { + return -1; + } + int res = at_enqueue_ussd(&pvt->sys_chan, ussd); + free_pvt(pvt); + return res; } #/* */ -EXPORT_DEF const char * send_sms(const char * dev_name, const char * number, const char * message, const char * validity, const char * report, int * status, void ** id) +EXPORT_DEF int send_sms(const char *dev_name, const char *number, const char *message, const char *validity, const char *report, const char *payload, size_t payload_len) { - if(is_valid_phone_number(number)) - { - int val = 0; - int srr = 0; - - if(validity) - { - val = strtol (validity, NULL, 10); - if(val <= 0) - val = 0; - } - - if(report) - srr = ast_true (report); + if (!is_valid_phone_number(number)) { + chan_dongle_err = E_INVALID_PHONE_NUMBER; + return -1; + } - return send2(dev_name, status, 1, "Error adding SMS commands to queue", "SMS queued for send", at_enqueue_sms, number, message, val, srr, id); + int val = 0; + if (validity) { + val = strtol(validity, NULL, 10); + val = val <= 0 ? 0 : val; } - if(status) - *status = 0; - return "Invalid destination number"; -} -#/* */ -EXPORT_DEF const char * send_pdu(const char * dev_name, const char * pdu, int * status, void ** id) -{ - return send2(dev_name, status, 1, "Error adding SMS commands to queue", "SMS queued for send", at_enqueue_pdu, pdu, NULL, 0, 0, id); + int srr = !report ? 0 : ast_true(report); + + struct pvt *pvt = get_pvt(dev_name, 1); + if (!pvt) { + return -1; + } + int res = at_enqueue_sms(&pvt->sys_chan, number, message, val, srr, payload, payload_len); + free_pvt(pvt); + return res; } #/* */ -EXPORT_DEF const char* send_reset(const char* dev_name, int * status) +EXPORT_DEF int send_reset(const char *dev_name) { - return send2(dev_name, status, 0, "Error adding reset command to queue", "Reset command queued for execute", at_enqueue_reset, 0, 0, 0, 0, NULL); + struct pvt *pvt = get_pvt(dev_name, 0); + if (!pvt) { + return -1; + } + int res = at_enqueue_reset(&pvt->sys_chan); + free_pvt(pvt); + return res; } #/* */ -EXPORT_DEF const char* send_ccwa_set(const char* dev_name, call_waiting_t enable, int * status) +EXPORT_DEF int send_ccwa_set(const char *dev_name, call_waiting_t enable) { - return send2(dev_name, status, 1, "Error adding CCWA commands to queue", "Call-Waiting commands queued for execute", at_enqueue_set_ccwa, 0, 0, enable, 0, NULL); + struct pvt *pvt = get_pvt(dev_name, 1); + if (!pvt) { + return -1; + } + int res = at_enqueue_set_ccwa(&pvt->sys_chan, enable); + free_pvt(pvt); + return res; } #/* */ -EXPORT_DEF const char* send_at_command(const char* dev_name, const char* command) +EXPORT_DEF int send_at_command(const char *dev_name, const char *command) { - return send2(dev_name, NULL, 0, "Error adding command", "Command queued for execute", at_enqueue_user_cmd, command, NULL, 0, 0, NULL); + struct pvt *pvt = get_pvt(dev_name, 0); + if (!pvt) { + return -1; + } + int res = at_enqueue_user_cmd(&pvt->sys_chan, command); + free_pvt(pvt); + return res; } -EXPORT_DEF const char* schedule_restart_event(dev_state_t event, restate_time_t when, const char* dev_name, int * status) +EXPORT_DEF int schedule_restart_event(dev_state_t event, restate_time_t when, const char *dev_name) { - const char * msg; - struct pvt * pvt = find_device(dev_name); + struct pvt *pvt = find_device(dev_name); - if (pvt) - { + if (pvt) { pvt->desired_state = event; pvt->restart_time = when; pvt_try_restate(pvt); - ast_mutex_unlock (&pvt->lock); - - msg = dev_state2str_msg(event); - - if(status) - *status = 1; - } - else - { - msg = "Device not found"; - if(status) - *status = 0; + ast_mutex_unlock(&pvt->lock); + } else { + chan_dongle_err = E_DEVICE_NOT_FOUND; + return -1; } - return msg; + return 0; } diff --git a/helpers.h b/helpers.h index 293739cc..ce06b5f0 100644 --- a/helpers.h +++ b/helpers.h @@ -11,13 +11,12 @@ EXPORT_DECL int get_at_clir_value (struct pvt* pvt, int clir); /* return status string of sending, status arg is optional */ -EXPORT_DECL const char * send_ussd(const char * dev_name, const char* ussd, int * status, void ** id); -EXPORT_DECL const char * send_sms(const char * dev_name, const char* number, const char* message, const char * validity, const char * report, int * status, void ** id); -EXPORT_DECL const char * send_pdu(const char * dev_name, const char * pdu, int * status, void ** id); -EXPORT_DECL const char * send_reset(const char * dev_name, int * status); -EXPORT_DECL const char * send_ccwa_set(const char * dev_name, call_waiting_t enable, int * status); -EXPORT_DECL const char * send_at_command(const char * dev_name, const char* command); -EXPORT_DECL const char * schedule_restart_event(dev_state_t event, restate_time_t when, const char * dev_name, int * status); +EXPORT_DECL int send_ussd(const char *dev_name, const char *ussd); +EXPORT_DECL int send_sms(const char *dev_name, const char *number, const char *message, const char *validity, const char *report, const char *payload, size_t payload_len); +EXPORT_DECL int send_reset(const char *dev_name); +EXPORT_DECL int send_ccwa_set(const char *dev_name, call_waiting_t enable); +EXPORT_DECL int send_at_command(const char *dev_name, const char *command); +EXPORT_DECL int schedule_restart_event(dev_state_t event, restate_time_t when, const char *dev_name); EXPORT_DECL int is_valid_phone_number(const char * number); #endif /* CHAN_DONGLE_HELPERS_H_INCLUDED */ diff --git a/manager.c b/manager.c index de1de269..40158270 100644 --- a/manager.c +++ b/manager.c @@ -23,6 +23,7 @@ #include "manager.h" #include "chan_dongle.h" /* devices */ #include "helpers.h" /* ITEMS_OF() send_ccwa_set() send_reset() send_sms() send_ussd() */ +#include "error.h" static char * espace_newlines(const char * text); @@ -89,8 +90,6 @@ static int manager_show_devices (struct mansession* s, const struct message* m) astman_append (s, "SubscriberNumber: %s\r\n", pvt->subscriber_number); astman_append (s, "SMSServiceCenter: %s\r\n", pvt->sms_scenter); astman_append (s, "UseUCS2Encoding: %s\r\n", pvt->use_ucs2_encoding ? "Yes" : "No"); - astman_append (s, "USSDUse7BitEncoding: %s\r\n", pvt->cusd_use_7bit_encoding ? "Yes" : "No"); - astman_append (s, "USSDUseUCS2Decoding: %s\r\n", pvt->cusd_use_ucs2_decoding ? "Yes" : "No"); astman_append (s, "TasksInQueue: %u\r\n", PVT_STATE(pvt, at_tasks)); astman_append (s, "CommandsInQueue: %u\r\n", PVT_STATE(pvt, at_cmds)); astman_append (s, "CallWaitingState: %s\r\n", pvt->has_call_waiting ? "Enabled" : "Disabled"); @@ -133,9 +132,6 @@ static int manager_send_ussd (struct mansession* s, const struct message* m) const char* ussd = astman_get_header (m, "USSD"); char buf[256]; - const char* msg; - int status; - void * msgid = NULL; if (ast_strlen_zero (device)) { @@ -149,16 +145,9 @@ static int manager_send_ussd (struct mansession* s, const struct message* m) return 0; } - msg = send_ussd(device, ussd, &status, &msgid); - snprintf(buf, sizeof (buf), "[%s] %s\r\nID: %p", device, msg, msgid); - if(status) - { - astman_send_ack(s, m, buf); - } - else - { - astman_send_error(s, m, buf); - } + int res = send_ussd(device, ussd); + snprintf(buf, sizeof (buf), "[%s] %s", device, res < 0 ? error2str(chan_dongle_err) : "USSD queued for send"); + (res == 0 ? astman_send_ack : astman_send_error)(s, m, buf); return 0; } @@ -170,11 +159,9 @@ static int manager_send_sms (struct mansession* s, const struct message* m) const char* message = astman_get_header (m, "Message"); const char* validity= astman_get_header (m, "Validity"); const char* report = astman_get_header (m, "Report"); + const char* payload = astman_get_header (m, "Payload"); char buf[256]; - const char* msg; - int status; - void * msgid; if (ast_strlen_zero (device)) { @@ -194,69 +181,30 @@ static int manager_send_sms (struct mansession* s, const struct message* m) return 0; } - msg = send_sms(device, number, message, validity, report, &status, &msgid); - snprintf (buf, sizeof (buf), "[%s] %s\r\nID: %p", device, msg, msgid); - if(status) - { - astman_send_ack(s, m, buf); - } - else - { - astman_send_error(s, m, buf); - } - - return 0; -} - -static int manager_send_pdu (struct mansession* s, const struct message* m) -{ - const char* device = astman_get_header (m, "Device"); - const char* pdu = astman_get_header (m, "PDU"); - - char buf[256]; - const char* msg; - int status; - void * msgid; - - if (ast_strlen_zero (device)) - { - astman_send_error (s, m, "Device not specified"); - return 0; - } - - if (ast_strlen_zero (pdu)) - { - astman_send_error (s, m, "PDU not specified"); - return 0; - } - - msg = send_pdu(device, pdu, &status, &msgid); - snprintf (buf, sizeof (buf), "[%s] %s\r\nID: %p", device, msg, msgid); - if(status) - { - astman_send_ack(s, m, buf); - } - else - { - astman_send_error(s, m, buf); - } + int res = send_sms(device, number, message, validity, report, payload, strlen(payload) + 1); + snprintf(buf, sizeof (buf), "[%s] %s", device, res < 0 ? error2str(chan_dongle_err) : "SMS queued for send"); + (res == 0 ? astman_send_ack : astman_send_error)(s, m, buf); return 0; } #/* */ -EXPORT_DEF void manager_event_sent_notify(const char * devname, const char * type, const void * id, const char * result) +EXPORT_DEF void manager_event_report(const char * devname, const char *payload, size_t payload_len, const char *scts, const char *dt, int success, int type, const char *report_str) { - char buf[40]; - snprintf(buf, sizeof(buf), "Dongle%sStatus", type); - - manager_event (EVENT_FLAG_CALL, buf, + manager_event (EVENT_FLAG_CALL, "DongleReport", "Device: %s\r\n" - "ID: %p\r\n" - "Status: %s\r\n", + "Payload: %.*s\r\n" + "SCTS: %s\r\n" + "DT: %s\r\n" + "Success: %d\r\n" + "Type: %d\r\n" + "Report: %s\r\n", devname, - id, - result + payload_len, payload, + scts, dt, + success, + type, + report_str ); } @@ -453,8 +401,6 @@ static int manager_ccwa_set (struct mansession* s, const struct message* m) // const char* id = astman_get_header (m, "ActionID"); char buf[256]; - const char* msg; - int status; call_waiting_t enable; if (ast_strlen_zero (device)) @@ -473,9 +419,9 @@ static int manager_ccwa_set (struct mansession* s, const struct message* m) return 0; } - msg = send_ccwa_set(device, enable, &status); - snprintf (buf, sizeof (buf), "[%s] %s", device, msg); - (status ? astman_send_ack : astman_send_error)(s, m, buf); + int res = send_ccwa_set(device, enable); + snprintf (buf, sizeof (buf), "[%s] %s", device, res < 0 ? error2str(chan_dongle_err) : "Call-Waiting commands queued for execute"); + (res == 0 ? astman_send_ack : astman_send_error)(s, m, buf); // if(!ast_strlen_zero(id)) // astman_append (s, "ActionID: %s\r\n", id); @@ -489,8 +435,6 @@ static int manager_reset (struct mansession* s, const struct message* m) // const char* id = astman_get_header (m, "ActionID"); char buf[256]; - const char* msg; - int status; if (ast_strlen_zero (device)) { @@ -498,9 +442,9 @@ static int manager_reset (struct mansession* s, const struct message* m) return 0; } - msg = send_reset(device, &status); - snprintf (buf, sizeof (buf), "[%s] %s", device, msg); - (status ? astman_send_ack : astman_send_error)(s, m, buf); + int res = send_reset(device); + snprintf (buf, sizeof (buf), "[%s] %s", device, res < 0 ? error2str(chan_dongle_err) : "Reset command queued for execute"); + (res == 0 ? astman_send_ack : astman_send_error)(s, m, buf); // if(!ast_strlen_zero(id)) // astman_append (s, "ActionID: %s\r\n", id); @@ -518,8 +462,7 @@ static int manager_restart_action(struct mansession * s, const struct message * // const char * id = astman_get_header (m, "ActionID"); char buf[256]; - const char * msg; - int status; + int res; unsigned i; if (ast_strlen_zero (device)) @@ -532,9 +475,9 @@ static int manager_restart_action(struct mansession * s, const struct message * { if(event == DEV_STATE_STARTED || strcasecmp(when, b_choices[i]) == 0) { - msg = schedule_restart_event(event, i, device, &status); - snprintf (buf, sizeof (buf), "[%s] %s", device, msg); - (status ? astman_send_ack : astman_send_error)(s, m, buf); + res = schedule_restart_event(event, i, device); + snprintf(buf, sizeof (buf), "[%s] %s", device, res < 0 ? error2str(chan_dongle_err) : dev_state2str_msg(event)); + (res == 0 ? astman_send_ack : astman_send_error)(s, m, buf); // if(!ast_strlen_zero(id)) // astman_append (s, "ActionID: %s\r\n", id); return 0; @@ -625,8 +568,8 @@ static const struct dongle_manager "Description: Send a ussd message to a dongle.\n\n" "Variables: (Names marked with * are required)\n" " ActionID: Action ID for this transaction. Will be returned.\n" - " *Device: The dongle to which the ussd code will be send.\n" - " *USSD: The ussd code that will be send to the device.\n" + " *Device: The dongle to which the ussd code will be sent.\n" + " *USSD: The ussd code that will be sent to the device.\n" }, { manager_send_sms, @@ -637,19 +580,11 @@ static const struct dongle_manager "Variables: (Names marked with * are required)\n" " ActionID: Action ID for this transaction. Will be returned.\n" " *Device: The dongle to which the SMS be send.\n" - " *Number: The phone number to which the SMS will be send.\n" - " *Message: The SMS message that will be send.\n" - }, - { - manager_send_pdu, - EVENT_FLAG_CALL, - "DongleSendPDU", - "Send a PDU of message.", - "Description: Send a PDU of message from a dongle.\n\n" - "Variables: (Names marked with * are required)\n" - " ActionID: Action ID for this transaction. Will be returned.\n" - " *Device: The dongle to which the PDU be send.\n" - " *PDU: The PDU of SMS.\n" + " *Number: The phone number to which the SMS will be sent.\n" + " *Message: The SMS message that will be sent.\n" + " *Validity: Validity period in minutes.\n" + " *Report: Boolean flag for report request.\n" + " *Payload: Unstructured data that will be included in delivery report.\n" }, { manager_ccwa_set, diff --git a/manager.h b/manager.h index 27942e96..83ff9aba 100644 --- a/manager.h +++ b/manager.h @@ -20,7 +20,7 @@ EXPORT_DECL void manager_event_new_sms_base64 (const char * devname, char * numb EXPORT_DECL void manager_event_cend(const char * devname, int call_index, int duration, int end_status, int cc_cause); EXPORT_DECL void manager_event_call_state_change(const char * devname, int call_index, const char * newstate); EXPORT_DECL void manager_event_device_status(const char * devname, const char * newstatus); -EXPORT_DECL void manager_event_sent_notify(const char * devname, const char * type, const void * id, const char * result); +EXPORT_DECL void manager_event_report(const char * devname, const char *payload, size_t payload_len, const char *scts, const char *dt, int success, int type, const char *report_str); #else /* BUILD_MANAGER */ @@ -35,7 +35,7 @@ EXPORT_DECL void manager_event_sent_notify(const char * devname, const char * ty #define manager_event_cend(devname, call_index, duration, end_status, cc_cause) #define manager_event_call_state_change(devname, call_index, newstate) #define manager_event_device_status(devname, newstatus) -#define manager_event_sent_notify(devname, type, id, result) +#define manager_event_report(devname, payload, payload_len, scts, dt, success, type, report_str) #endif /* BUILD_MANAGER */ diff --git a/pdu.c b/pdu.c index 4f46d01b..481634b6 100644 --- a/pdu.c +++ b/pdu.c @@ -10,6 +10,7 @@ #include "helpers.h" /* dial_digit_code() */ #include "char_conv.h" /* utf8_to_hexstr_ucs2() */ #include "gsm7_luts.h" +#include "error.h" /* SMS-SUBMIT format SCA 1..12 octet(s) Service Center Address information element @@ -215,19 +216,6 @@ #define NUMBER_TYPE_NETWORKSHORT (TP_A_EXT_NOEXT | TP_A_TON_NETSPECIFIC | TP_A_NPI_PRIVATENUM) /* 0xB9 */ #define NUMBER_TYPE_UNKNOWN (TP_A_EXT_NOEXT | TP_A_TON_UNKNOWN | TP_A_NPI_TEL_E164_E163) /* 0x81 */ -/* Message Type Indicator Parameter */ -#define PDUTYPE_MTI_SHIFT 0 -#define PDUTYPE_MTI_SMS_DELIVER (0x00 << PDUTYPE_MTI_SHIFT) -#define PDUTYPE_MTI_SMS_DELIVER_REPORT (0x00 << PDUTYPE_MTI_SHIFT) -#define PDUTYPE_MTI_SMS_SUBMIT (0x01 << PDUTYPE_MTI_SHIFT) -#define PDUTYPE_MTI_SMS_SUBMIT_REPORT (0x01 << PDUTYPE_MTI_SHIFT) -#define PDUTYPE_MTI_SMS_STATUS_REPORT (0x02 << PDUTYPE_MTI_SHIFT) -#define PDUTYPE_MTI_SMS_COMMAND (0x02 << PDUTYPE_MTI_SHIFT) -#define PDUTYPE_MTI_RESERVED (0x03 << PDUTYPE_MTI_SHIFT) - -#define PDUTYPE_MTI_MASK (0x03 << PDUTYPE_MTI_SHIFT) -#define PDUTYPE_MTI(pdutype) ((pdutype) & PDUTYPE_MTI_MASK) - /* Reject Duplicate */ #define PDUTYPE_RD_SHIFT 2 #define PDUTYPE_RD_ACCEPT (0x00 << PDUTYPE_RD_SHIFT) @@ -276,22 +264,22 @@ #define CSMS_GSM7_MAX_LEN 153 #define SMS_GSM7_MAX_LEN 160 -#define CSMS_UCS2_MAX_LEN 66 +#define CSMS_UCS2_MAX_LEN 67 #define SMS_UCS2_MAX_LEN 70 -#define PDU_LENGTH 176 EXPORT_DEF void pdu_udh_init(pdu_udh_t *udh) { udh->ref = 0; udh->parts = 1; + udh->order = 0; udh->ss = 0; udh->ls = 0; } #/* get digit code, 0 if invalid */ -EXPORT_DEF char pdu_digit2code(char digit) +static uint8_t pdu_digit2code(char digit) { - switch(digit) { + switch (digit) { case '0': case '1': case '2': @@ -302,74 +290,54 @@ EXPORT_DEF char pdu_digit2code(char digit) case '7': case '8': case '9': - break; + return digit - '0'; case '*': - digit = 'A'; - break; + return 0xa; case '#': - digit = 'B'; - break; + return 0xb; case 'a': case 'A': - digit = 'C'; + return 0xc; break; case 'b': case 'B': - digit = 'D'; - break; + return 0xd; case 'c': case 'C': - digit = 'E'; - break; + return 0xe; default: - return 0; + return 255; } - return digit; } #/* */ -static char pdu_code2digit(char code) +static char pdu_code2digit(uint8_t code) { - switch(code) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - break; - case 'a': - case 'A': - code = '*'; - break; - case 'b': - case 'B': - code = '#'; - break; - case 'c': - case 'C': - code = 'A'; - break; - case 'd': - case 'D': - code = 'B'; - break; - case 'e': - case 'E': - code = 'C'; - break; - case 'f': - case 'F': - code = 0; - break; + switch (code) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + return code + '0'; + case 0xa: + return '*'; + case 0xb: + return '#'; + case 0xc: + return 'A'; + case 0xd: + return 'B'; + case 0xe: + return 'C'; default: - return -1; + return 0; } - return code; } #/* convert minutes to relative VP value */ @@ -397,31 +365,6 @@ static int pdu_relative_validity(unsigned minutes) #undef DIV_UP } -#/* convert 2 hex digits of PDU to byte, return < 0 on error */ -static int pdu_parse_byte(char ** digits2hex, size_t * length) -{ - int res = -1; - int res2; - - if(*length >= 2) - { - res = parse_hexdigit(*digits2hex[0]); - if(res >= 0) - { - (*digits2hex)++; - (*length)--; - res2 = parse_hexdigit(*digits2hex[0]); - if(res2 >= 0) - { - (*digits2hex)++; - (*length)--; - return (res << 4) | res2; - } - } - } - return res; -} - /*! * \brief Store number in PDU * \param buffer -- pointer to place where number will be stored, CALLER MUST be provide length + 2 bytes of buffer @@ -429,255 +372,203 @@ static int pdu_parse_byte(char ** digits2hex, size_t * length) * \param length -- length of number * \return number of bytes written to buffer */ -static int pdu_store_number(char* buffer, const char* number, unsigned length) +static int pdu_store_number(uint8_t* buffer, int toa, const char *number, unsigned length) { - int i; - for(i = 0; length > 1; length -=2, i +=2) - { - buffer[i] = pdu_digit2code(number[i + 1]); - buffer[i + 1] = pdu_digit2code(number[i]); + int i = 0, res; + buffer[i++] = toa; + + int j; + for (j = 0; j + 1 < length; j += 2) { + uint8_t a = pdu_digit2code(number[j]); + uint8_t b = pdu_digit2code(number[j + 1]); + if (a == 255 || b == 255) { + return -1; + } + buffer[i++] = a | b << 4; } - if(length) - { - buffer[i] = 'F'; - buffer[i+1] = pdu_digit2code(number[i]); - i += 2; + if (j != length) { + uint8_t a = pdu_digit2code(number[j]); + if (a == 255) { + return -1; + } + buffer[i++] = a | 0xf0; } return i; } -/* -failed parse 07 91 97 62 02 00 01 F9 44 14 D0 F7 FB DD D5 2E 9F C3 E6 B7 1B 0008117050815073618C0500037A020100680066006C0067006800200066006800670020006800640066006A006C006700680066006400680067000A002F00200415043604350434043D04350432043D044B04390020043B04380447043D044B043900200433043E0440043E0441043A043E043F003A0020002A003500300035002300360023002000200028003300200440002F0441 - - ^^ not a international format -failed parse 07 91 97 30 07 11 11 F1 04 14 D0 D9B09B5CC637DFEE721E0008117020616444617E041A043E04340020043F043E04340442043204350440043604340435043D0438044F003A00200036003900320037002E0020041D0438043A043E043C04430020043D043500200441043E043E043104490430043904420435002C002004320432043504340438044204350020043D0430002004410430043904420435002E - ^^ not a international format -*/ #/* reverse of pdu_store_number() */ -static int pdu_parse_number(char ** pdu, size_t * pdu_length, unsigned digits, int * toa, char * number, size_t num_len) +static int pdu_parse_number(uint8_t *pdu, size_t pdu_length, unsigned digits, char *number, size_t num_len) { - const char * begin; - unsigned syms; - char digit; - - if (num_len < digits + 1) { + if (num_len < digits + 2) { return -ENOMEM; } + int toa; - begin = *pdu; - *toa = pdu_parse_byte(pdu, pdu_length); - if (*toa < 0) { + int i = 0, res; + toa = pdu[i++]; + unsigned syms = ROUND_UP2(digits); + if (syms > pdu_length - i) { return -EINVAL; } - syms = ROUND_UP2(digits); - if (syms > *pdu_length) { - return -EINVAL; - } - - if ((*toa & TP_A_TON) == TP_A_TON_ALPHANUMERIC) { - /* NPI should be TP_A_NPI_UNKNOWN but has also been - * seen as TP_A_NPI_TEL_E164_E163 */ - for(; syms > 0; syms--, *pdu += 1, *pdu_length -= 1) - *number++ = pdu[0][0]; - return *pdu - begin; - } - - if ((*toa & TP_A_TON) == TP_A_TON_INTERNATIONAL) { - *number++ = '+'; - } - for (; syms > 0; syms -= 2, *pdu += 2, *pdu_length -= 2) { - digit = pdu_code2digit(pdu[0][1]); - if (digit <= 0) { - return -1; + if ((toa & TP_A_TON) == TP_A_TON_ALPHANUMERIC) { + uint16_t number16tmp[num_len]; + res = gsm7_unpack_decode(pdu + i, syms, number16tmp, num_len, 0, 0, 0); + if (res < 0) return -EINVAL; + res = ucs2_to_utf8(number16tmp, res, number, num_len); + i += syms / 2; + number += res; + } else { + if ((toa & TP_A_TON) == TP_A_TON_INTERNATIONAL) { + *number++ = '+'; } - *number++ = digit; - - digit = pdu_code2digit(pdu[0][0]); - if ((signed char)digit < 0 || (digit == 0 && (syms != 2 || (digits & 0x1) == 0))) { - return -1; + for (int j = 0; j < syms / 2; ++j) { + int c = pdu[i]; + *number++ = pdu_code2digit(c & 0xf); + char o = c >> 4; + if (o != 0xf) *number++ = pdu_code2digit(o); + ++i; } - - *number++ = digit; - } - if ((digits & 0x1) == 0) { - *number = 0; } + *number = '\0'; - return *pdu - begin; + return i; } - -#/* return bytes (not octets!) of pdu occupied by SCA or <0 on errors */ -EXPORT_DEF int pdu_parse_sca(char ** pdu, size_t * length) +#/* */ +static int pdu_parse_timestamp(uint8_t *pdu, size_t length, char *out) { - /* get length of SCA field */ - int sca_len = pdu_parse_byte(pdu, length); - - if(sca_len >= 0) - { - sca_len *= 2; - if((size_t)sca_len <= *length) - { - *pdu += sca_len; - *length -= sca_len; - - /* TODO: Parse SCA Address */ - return sca_len + 2; - } - } - return -EINVAL; + int d, m, y, h, i, s, o, os; + if (length >= 7) { + y = (10 * (pdu[0] & 15) + (pdu[0] >> 4)) + 2000; + m = 10 * (pdu[1] & 15) + (pdu[1] >> 4); + d = 10 * (pdu[2] & 15) + (pdu[2] >> 4); + h = 10 * (pdu[3] & 15) + (pdu[3] >> 4); + i = 10 * (pdu[4] & 15) + (pdu[4] >> 4); + s = 10 * (pdu[5] & 15) + (pdu[5] >> 4); + o = (pdu[6] >> 4) + 10 * (pdu[6] & 7); + os = pdu[6] & 0x8; + + sprintf(out, "%02d-%02d-%02d %02d:%02d:%02d %c%02d:%02d", y, m, d, h, i, s, os ? '-' : '+', o / 4, (o % 4) * 15); + + return 7; + } + return -1; } -#/* TODO: implement */ -static int pdu_parse_timestamp(char ** pdu, size_t * length) +EXPORT_DEF int pdu_build_mult(pdu_part_t *pdus, const char *sca, const char *dst, const uint16_t* msg, size_t msg_len, unsigned valid_minutes, int srr, uint8_t csmsref) { - if(*length >= 14) - { - *pdu += 14; - *length -= 14; - return 14; - } - return -EINVAL; -} - -EXPORT_DEF int pdu_build_mult(int (*cb)(const char *buf, unsigned len, void *s), const char* sca, const char* dst, const char* msg, unsigned valid_minutes, int srr, unsigned csmsref, void *s) -{ - unsigned length = 2048; - char buffer[length]; - unsigned msg_len, utf16_len; - - msg_len = strlen(msg); - utf16_len = msg_len * 2 + 4; - uint16_t msg_utf16[utf16_len]; // TODO: is this allowed on the stack? I'm a c++ guy... - utf16_len = convert_string(msg, msg_len, (char*)msg_utf16, utf16_len, "UTF-8", "UTF-16BE") / 2; - uint16_t msg_gsm7[utf16_len * 2]; - - // TODO: Check for other tables - int is_gsm7 = 1; - unsigned gsm7_len = 0; - const uint8_t *escenc = get_char_gsm7_encoding(0x1B00); - for (unsigned i = 0; i < utf16_len; ++i) { - const uint8_t *enc = get_char_gsm7_encoding(msg_utf16[i]); - uint8_t c = enc[0]; - if (c == GSM7_INVALID) { - is_gsm7 = 0; - break; - } - if (c > 127) { - gsm7_len += 2; - msg_gsm7[i] = escenc[0] << 8 | (c - 128); - } else { - ++gsm7_len; - msg_gsm7[i] = c; - } - } - msg_gsm7[gsm7_len] = '\0'; + uint16_t msg_gsm7[msg_len]; + int gsm7_len = gsm7_encode(msg, msg_len, msg_gsm7); unsigned split = 0; - unsigned cnt = 0, cur = 1, off = 0; - int res; - if (is_gsm7) { + unsigned cnt = 0, i = 0, off = 0; + if (gsm7_len >= 0) { if (gsm7_len > SMS_GSM7_MAX_LEN) { split = CSMS_GSM7_MAX_LEN; } else { split = SMS_GSM7_MAX_LEN; } - while (off < utf16_len) { + while (off < msg_len) { unsigned septets = 0, n; - for (n = 0; off + n < utf16_len; ++n) { + for (n = 0; off + n < msg_len; ++n) { unsigned req = msg_gsm7[off + n] > 255 ? 2 : 1; - if (septets + req >= split) break; + if (septets + req >= split) { + break; + } septets += req; } ++cnt; off += n; } if (cnt > 255) { - cnt = -E2BIG; - goto CLEAN; + chan_dongle_err = E_2BIG; + return -1; } off = 0; - while (off < utf16_len) { + while (off < msg_len) { unsigned septets = 0, n; - for (n = 0; off + n < utf16_len; ++n) { + for (n = 0; off + n < msg_len; ++n) { unsigned req = msg_gsm7[off + n] > 255 ? 2 : 1; - if (septets + req >= split) break; + if (septets + req >= split) { + break; + } septets += req; } pdu_udh_t udh; udh.ref = csmsref; - udh.order = cur++; + udh.order = i + 1; udh.parts = cnt; - unsigned curlen = pdu_build16(buffer, length, sca, dst, PDU_DCS_ALPHABET_7BIT, msg_gsm7 + off, n, septets, valid_minutes, srr, &udh); - res = cb(buffer, curlen, s); - if (res < 0) { - cnt = res; - goto CLEAN; + ssize_t curlen = pdu_build(pdus[i].buffer, PDU_LENGTH, &pdus[i].tpdu_length, sca, dst, PDU_DCS_ALPHABET_7BIT, msg_gsm7 + off, n, septets, valid_minutes, srr, &udh); + if (curlen < 0) { + /* pdu_build sets chan_dongle_err */ + return -1; } + pdus[i].length = curlen; off += n; + ++i; } } else { - if (utf16_len > SMS_UCS2_MAX_LEN) { + if (msg_len > SMS_UCS2_MAX_LEN) { split = CSMS_UCS2_MAX_LEN; } else { split = SMS_UCS2_MAX_LEN; } - cnt = (utf16_len + split - 1) / split; + cnt = (msg_len + split - 1) / split; if (cnt > 255) { - cnt = -E2BIG; - goto CLEAN; + chan_dongle_err = E_2BIG; + return -1; } - while (off < utf16_len) { - unsigned r = utf16_len - off; + while (off < msg_len) { + unsigned r = msg_len - off; unsigned n = r < split ? r : split; pdu_udh_t udh; udh.ref = csmsref; - udh.order = cur++; + udh.order = i + 1; udh.parts = cnt; - unsigned curlen = pdu_build16(buffer, length, sca, dst, PDU_DCS_ALPHABET_UCS2, msg_utf16 + off, n, n * 2, valid_minutes, srr, &udh); - res = cb(buffer, curlen, s); - if (res < 0) { - cnt = res; - goto CLEAN; + ssize_t curlen = pdu_build(pdus[i].buffer, PDU_LENGTH, &pdus[i].tpdu_length, sca, dst, PDU_DCS_ALPHABET_UCS2, msg + off, n, n * 2, valid_minutes, srr, &udh); + if (curlen < 0) { + /* pdu_build sets chan_dongle_err */ + return -1; } + pdus[i].length = curlen; off += n; + ++i; } } -CLEAN: - return 0; + return i; } -EXPORT_DEF int pdu_build16(char* buffer, size_t length, const char* sca, const char* dst, int dcs, const uint16_t* msg, unsigned msg_len, unsigned msg_bytes, unsigned valid_minutes, int srr, const pdu_udh_t *udh) +EXPORT_DEF ssize_t pdu_build(uint8_t *buffer, size_t length, size_t *tpdulen, const char *sca, const char *dst, int dcs, const uint16_t *msg, unsigned msg_reallen, unsigned msg_len, unsigned valid_minutes, int srr, const pdu_udh_t *udh) { int len = 0; - int data_len; int sca_toa = NUMBER_TYPE_INTERNATIONAL; - int dst_toa = NUMBER_TYPE_INTERNATIONAL; + int dst_toa; int pdutype = PDUTYPE_MTI_SMS_SUBMIT | PDUTYPE_RD_ACCEPT | PDUTYPE_VPF_RELATIVE | PDUTYPE_SRR_NOT_REQUESTED | PDUTYPE_UDHI_NO_HEADER | PDUTYPE_RP_IS_NOT_SET; int use_udh = udh->parts > 1; + int res; if (use_udh) pdutype |= PDUTYPE_UDHI_HAS_HEADER; unsigned dst_len; unsigned sca_len; - if(sca[0] == '+') - sca++; - - if(dst[0] == '+') - { - dst++; + if (sca[0] == '+') { + ++sca; } - else - { - if(strlen(dst) < 6) - dst_toa=NUMBER_TYPE_NETWORKSHORT; //0xB9 - else - dst_toa=NUMBER_TYPE_UNKNOWN; //0X81 + + if (dst[0] == '+') { + dst_toa = NUMBER_TYPE_INTERNATIONAL; + ++dst; + } else { + if (strlen(dst) < 6) { + dst_toa = NUMBER_TYPE_NETWORKSHORT; + } else { + dst_toa = NUMBER_TYPE_UNKNOWN; + } } /* count length of strings */ @@ -687,15 +578,16 @@ EXPORT_DEF int pdu_build16(char* buffer, size_t length, const char* sca, const c /* SCA Length */ /* Type-of-address of the SMSC */ /* Address of SMSC */ - if(sca_len) - { - len += snprintf(buffer + len, length - len, "%02X%02X", 1 + DIV2UP(sca_len), sca_toa); - len += pdu_store_number(buffer + len, sca, sca_len); - } - else - { - buffer[len++] = '0'; - buffer[len++] = '0'; + if (sca_len) { + buffer[len++] = 1 + DIV2UP(sca_len); + res = pdu_store_number(buffer + len, sca_toa, sca, sca_len); + if (res < 0) { + chan_dongle_err = E_BUILD_SCA; + return -1; + } + len += res; + } else { + buffer[len++] = 0; } sca_len = len; @@ -703,159 +595,144 @@ EXPORT_DEF int pdu_build16(char* buffer, size_t length, const char* sca, const c pdutype |= PDUTYPE_SRR_REQUESTED; /* PDU-type */ - /* TP-Message-Reference. The "00" value here lets the phone set the message reference number itself */ + /* TP-Message-Reference. Value will be ignored. The phone will set the number itself. */ /* Address-Length */ /* Type-of-address of the sender number */ - len += snprintf(buffer + len, length - len, "%02X%02X%02X%02X", pdutype, PDU_MESSAGE_REFERENCE, dst_len, dst_toa); + buffer[len++] = pdutype; + buffer[len++] = PDU_MESSAGE_REFERENCE; + buffer[len++] = dst_len; /* Destination address */ - len += pdu_store_number(buffer + len, dst, dst_len); + res = pdu_store_number(buffer + len, dst_toa, dst, dst_len); + if (res < 0) { + chan_dongle_err = E_BUILD_PHONE_NUMBER; + return -1; + } + len += res; /* TP-PID. Protocol identifier */ /* TP-DCS. Data coding scheme */ /* TP-Validity-Period */ /* TP-User-Data-Length */ -// printf("%d\n\n", pdu_relative_validity(valid_minutes)); - len += snprintf(buffer + len, length - len, "%02X%02X%02X%02X", PDU_PID_SMS, dcs, pdu_relative_validity(valid_minutes), msg_bytes + (!use_udh ? 0 : dcs == PDU_DCS_ALPHABET_UCS2 ? 7 : 8)); // UDH LEN + buffer[len++] = PDU_PID_SMS; + buffer[len++] = dcs; + buffer[len++] = pdu_relative_validity(valid_minutes); + buffer[len++] = msg_len + (!use_udh ? 0 : dcs == PDU_DCS_ALPHABET_UCS2 ? 6 : 7); /* encode UDH */ + int msg_padding = 0; if (use_udh) { - len += snprintf(buffer + len, length - len, "060804%04X%02X%02X", udh->ref, udh->parts, udh->order); - // 7 * 8 % 7 == 0 ==> No padding required for gsm 1 + buffer[len++] = 5; + buffer[len++] = 0; + buffer[len++] = 3; + buffer[len++] = udh->ref; + buffer[len++] = udh->parts; + buffer[len++] = udh->order; + msg_padding = 1; } /* TP-User-Data */ - data_len = str_encode16((dcs == PDU_DCS_ALPHABET_UCS2 ? STR_ENCODING_UCS2_HEX : STR_ENCODING_GSM7_HEX_PAD_0), msg, msg_len, buffer + len, length - len - 1); // TODO: y 3? - if(data_len < 0) - { - return -EINVAL; - } - else if(data_len > 160 * 2) - { - return -E2BIG; + if (dcs == PDU_DCS_ALPHABET_UCS2) { + memcpy(buffer + len, (const char*)msg, msg_len); + len += msg_len; + } else { + len += (gsm7_pack(msg, msg_reallen, buffer + len, length - len - 1, msg_padding) + 1) / 2; } - len += data_len; - buffer[len] = '\0'; - - /* also check message limit in 178 octets of TPDU (w/o SCA) TODO: it's 176, isn't it? */ - if(len - sca_len > PDU_LENGTH * 2) - { - return -E2BIG; + /* also check message limit in 176 octets of TPDU */ + *tpdulen = len - sca_len; + if (*tpdulen > TPDU_LENGTH) { + chan_dongle_err = E_2BIG; + return -1; } - - return len; -} - - -#/* */ -static str_encoding_t pdu_dcs_alphabet2encoding(int alphabet) -{ - str_encoding_t rv = STR_ENCODING_UNKNOWN; - - alphabet >>= PDU_DCS_ALPHABET_SHIFT; - switch(alphabet) - { - case (PDU_DCS_ALPHABET_7BIT >> PDU_DCS_ALPHABET_SHIFT): - rv = STR_ENCODING_GSM7_HEX_PAD_0; - break; - case (PDU_DCS_ALPHABET_8BIT >> PDU_DCS_ALPHABET_SHIFT): - rv = STR_ENCODING_8BIT_HEX; - break; - case (PDU_DCS_ALPHABET_UCS2 >> PDU_DCS_ALPHABET_SHIFT): - rv = STR_ENCODING_UCS2_HEX; - break; + if (len > PDU_LENGTH) { + chan_dongle_err = E_2BIG; + return -1; } - return rv; + return len; } /*! * \brief Parse PDU * \param pdu -- SCA + TPDU * \param tpdu_length -- length of TPDU in octets + * \param timestamp -- 25 bytes for timestamp string * \return 0 on success */ -/* TODO: split long function */ -EXPORT_DEF const char * pdu_parse(char ** pdu, size_t tpdu_length, char * oa, size_t oa_len, str_encoding_t * oa_enc, char ** msg, str_encoding_t * msg_enc, pdu_udh_t *udh) +EXPORT_DEF int pdu_parse_sca(uint8_t *pdu, size_t pdu_length, char *sca, size_t sca_len) { - size_t pdu_length = strlen(*pdu); - int field_len, pdu_type, oa_digits, oa_toa, pid, dcs, alphabet, ts, udl, udhl; - - /* set msg as NULL until the end */ - *msg = NULL; - udh->ref = -1; - - /* decode SCA */ - field_len = pdu_parse_sca(pdu, &pdu_length); + int i = 0; + int sca_digits = (pdu[i++] - 1) * 2; + int field_len = pdu_parse_number(pdu + i, pdu_length - i, sca_digits, sca, sca_len); if (field_len <= 0) { - return "Can't parse SCA"; + chan_dongle_err = E_INVALID_SCA; + return -1; } - - if (tpdu_length * 2 > pdu_length) { - return "TPDU length not matched with actual length"; + i += field_len; + return i; +} +EXPORT_DEF int tpdu_parse_type(uint8_t *pdu, size_t pdu_length, int *type) +{ + if (pdu_length < 1) { + chan_dongle_err = E_INVALID_TPDU_TYPE; + return -1; } - - /* update length, if any */ - (*pdu)[pdu_length = (tpdu_length * 2)] = 0; - - pdu_type = pdu_parse_byte(pdu, &pdu_length); - if (pdu_type < 0) { - return "Can't parse PDU Type"; + *type = *pdu; + return 1; +} +EXPORT_DEF int tpdu_parse_status_report(uint8_t *pdu, size_t pdu_length, int *mr, char *ra, size_t ra_len, char *scts, char *dt, int *st) +{ + int i = 0, field_len; + const char *ret = NULL; + if (i + 2 > pdu_length) { + chan_dongle_err = E_UNKNOWN; + return -1; } - - /* TODO: also handle PDUTYPE_MTI_SMS_SUBMIT_REPORT and PDUTYPE_MTI_SMS_STATUS_REPORT */ - if (PDUTYPE_MTI(pdu_type) == PDUTYPE_MTI_SMS_STATUS_REPORT) - { - const char *ret = NULL; - int reference = pdu_parse_byte(pdu, &pdu_length); - /* Skip over 8 bytes TP-DA */ - if (reference >= 0 && pdu_length >= 8) { - (*pdu) += 8; - pdu_length -= 8; - /* Skip over 7 bytes timestamp TP-SCTS */ - if (pdu_parse_timestamp(pdu, &pdu_length) >= 0 && - /* Skip over 7 bytes timestamp TP-DT */ - pdu_parse_timestamp(pdu, &pdu_length) >= 0) { - int tp_status = pdu_parse_byte(pdu, &pdu_length); - if ((tp_status & 0xf) == 0) { - ret = (void*)0x1; /* HACK! */ - *msg = (char*)(ssize_t)reference; /* HACK! */ - } else { - ret = "Good report, but delivery failed"; - } - } else { - ret = "FIXME error 1"; - } - } else { - ret = "FIXME error 2"; - } - return ret; + *mr = pdu[i++]; + int ra_digits = pdu[i++]; + field_len = pdu_parse_number(pdu + i, pdu_length - i, ra_digits, ra, ra_len); + if (field_len < 0) { + chan_dongle_err = E_INVALID_PHONE_NUMBER; + return -1; } - if (PDUTYPE_MTI(pdu_type) != PDUTYPE_MTI_SMS_DELIVER) { - *pdu -= 2; - return "Unhandled PDU Type MTI only SMS-DELIVER/SMS-STATUS-REPORT supported"; + i += field_len; + + if (i + 14 + 1 > pdu_length) { + chan_dongle_err = E_INVALID_TIMESTAMP; + return -1; } + i += pdu_parse_timestamp(pdu + i, pdu_length - i, scts); + i += pdu_parse_timestamp(pdu + i, pdu_length - i, dt); + *st = pdu[i++]; + return 0; +} +EXPORT_DEF int tpdu_parse_deliver(uint8_t *pdu, size_t pdu_length, int tpdu_type, char *oa, size_t oa_len, char *scts, uint16_t *msg, pdu_udh_t *udh) +{ + int i = 0, field_len, oa_digits, pid, dcs, alphabet, ts, udl, udhl, msg_padding = 0; - oa_digits = pdu_parse_byte(pdu, &pdu_length); - if (oa_digits <= 0) { - return "Can't parse length of OA"; + if (i + 1 > pdu_length) { + chan_dongle_err = E_UNKNOWN; + return -1; } + oa_digits = pdu[i++]; - field_len = pdu_parse_number(pdu, &pdu_length, oa_digits, &oa_toa, oa, oa_len); + field_len = pdu_parse_number(pdu + i, pdu_length - i, oa_digits, oa, oa_len); if (field_len < 0) { - return "Can't parse OA"; + chan_dongle_err = E_INVALID_PHONE_NUMBER; + return -1; } + i += field_len; - pid = pdu_parse_byte(pdu, &pdu_length); - *oa_enc = STR_ENCODING_ASCII; - if ((oa_toa & TP_A_TON) == TP_A_TON_ALPHANUMERIC) { - *oa_enc = STR_ENCODING_GSM7_HEX_PAD_0; + if (i + 2 + 7 + 1 > pdu_length) { + chan_dongle_err = E_UNKNOWN; + return -1; } - if (pid < 0) { - return "Can't parse PID"; - } + pid = pdu[i++]; + dcs = pdu[i++]; + i += pdu_parse_timestamp(pdu + i, pdu_length - i, scts); + udl = pdu[i++]; if (pid != PDU_PID_SMS && !(0x41 <= pid && pid <= 0x47) /* PDU_PID_SMS_REPLACE_MASK */) { /* 3GPP TSS 23.040 v14.0.0 (2017-013) */ @@ -877,10 +754,6 @@ EXPORT_DEF const char * pdu_parse(char ** pdu, size_t tpdu_length, char * oa, si (unsigned char)pid); } - dcs = pdu_parse_byte(pdu, &pdu_length); - if (dcs < 0) { - return "Can't parse DSC"; - } /* http://www.etsi.org/deliver/etsi_gts/03/0338/05.00.00_60/gsmts_0338v050000p.pdf */ /* The TP-Data-Coding-Scheme field, defined in GSM 03.40, * indicates the data coding scheme of the TP-UD field, and may @@ -918,7 +791,8 @@ EXPORT_DEF const char * pdu_parse(char ** pdu, size_t tpdu_length, char * oa, si case 0x3: /* HIGH 0011: Compressed regular with class */ case 0x6: /* HIGH 0110: Compressed, marked for self-destruct */ case 0x7: /* HIGH 0111: Compressed, marked for self-destruct with class */ - return "Compression not implemented"; + chan_dongle_err = E_UNKNOWN; + return -1; case 0xC: /* HIGH 1100: "Discard" MWI */ case 0xD: /* HIGH 1101: "Store" MWI */ /* if 0xC then the recipient may discard message @@ -928,138 +802,133 @@ EXPORT_DEF const char * pdu_parse(char ** pdu, size_t tpdu_length, char * oa, si /* (dsc_lo & 3): {VM, Fax, E-mail, Other} */ break; default: + chan_dongle_err = E_UNKNOWN; reserved = 1; break; } if (reserved) { - *pdu -= 2; - return "Reserved DCS value"; + chan_dongle_err = E_UNKNOWN; + return -1; + } + if (alphabet == -1) { + chan_dongle_err = E_UNKNOWN; + return -1; } } - if (alphabet == -1) { - *pdu -= 2; - return "Unsupported DCS value"; - } - - ts = pdu_parse_timestamp(pdu, &pdu_length); - *msg_enc = pdu_dcs_alphabet2encoding(alphabet); - if (ts < 0) { - return "Can't parse Timestamp"; - } - - udl = pdu_parse_byte(pdu, &pdu_length); - if (udl < 0) { - return "Can't parse UDL"; + if (alphabet == PDU_DCS_ALPHABET_8BIT) { + // TODO: What to do with binary messages? Are there any? + // Return an error as it is dangerous to forward the raw binary data as text + chan_dongle_err = E_INVALID_CHARSET; + return -1; } /* calculate number of octets in UD */ + int udl_nibbles; + int udl_bytes = udl; if (alphabet == PDU_DCS_ALPHABET_7BIT) { - udl = ((udl + 1) * 7) >> 3; - } - if ((size_t)udl * 2 != pdu_length) { - *pdu -= 2; - return "UDL not match with UD length"; + udl_nibbles = (udl * 7 + 3) / 4; + udl_bytes = (udl_nibbles + 1) / 2; } - - if (PDUTYPE_UDHI(pdu_type) != PDUTYPE_UDHI_HAS_HEADER) { - /* save message */ - *msg = *pdu; - return NULL; + if (udl_bytes != pdu_length - i) { + chan_dongle_err = E_UNKNOWN; + return -1; } - udhl = pdu_parse_byte(pdu, &pdu_length); - if (udhl < 0) { - return "Can't parse UDHL"; - } + if (PDUTYPE_UDHI(tpdu_type) == PDUTYPE_UDHI_HAS_HEADER) { + if (i + 1 > pdu_length) { + chan_dongle_err = E_UNKNOWN; + return -1; + } + udhl = pdu[i++]; - /* adjust 7-bit padding */ - if (*msg_enc == STR_ENCODING_GSM7_HEX_PAD_0) { - switch (6 - (udhl % 7)) { - case 1: - *msg_enc = STR_ENCODING_GSM7_HEX_PAD_1; - break; - case 2: - *msg_enc = STR_ENCODING_GSM7_HEX_PAD_2; - break; - case 3: - *msg_enc = STR_ENCODING_GSM7_HEX_PAD_3; - break; - case 4: - *msg_enc = STR_ENCODING_GSM7_HEX_PAD_4; - break; - case 5: - *msg_enc = STR_ENCODING_GSM7_HEX_PAD_5; - break; - case 6: - *msg_enc = STR_ENCODING_GSM7_HEX_PAD_6; - break; - default: - /* no change */ - break; + /* adjust 7-bit padding */ + if (alphabet == PDU_DCS_ALPHABET_7BIT) { + msg_padding = 6 - (udhl % 7); + udl_nibbles -= (udhl + 1) * 2; } - } - /* NOTE: UDHL count octets no need calculation */ - if (pdu_length < (size_t)(udhl * 2)) { - return "Invalid UDH"; - } + /* NOTE: UDHL count octets no need calculation */ + if (pdu_length - i < (size_t)udhl) { + chan_dongle_err = E_UNKNOWN; + return -1; + } - while (udhl >= 2) { - int iei_type, iei_len; - - /* get type byte */ - iei_type = pdu_parse_byte(pdu, &pdu_length); - - /* get length byte */ - iei_len = pdu_parse_byte(pdu, &pdu_length); - - /* subtract bytes */ - udhl -= 2; - - /* skip data, if any */ - if (iei_len >= 0 && iei_len <= udhl) { - switch (iei_type) { - case 0x00: /* Concatenated */ - if (iei_len != 3) return "Invalid IEI len for concatenated SMS"; - udh->ref = pdu_parse_byte(pdu, &pdu_length); - udh->parts = pdu_parse_byte(pdu, &pdu_length); - udh->order = pdu_parse_byte(pdu, &pdu_length); - udhl -= 3; - break; - case 0x08: /* Concatenated, 16 bit ref */ // TODO: Test this - if (iei_len != 4) return "Invalid IEI len for concatenated SMS with 16 bit reference"; - udh->ref = (pdu_parse_byte(pdu, &pdu_length) << 8) | pdu_parse_byte(pdu, &pdu_length); - udh->parts = pdu_parse_byte(pdu, &pdu_length); - udh->order = pdu_parse_byte(pdu, &pdu_length); - udhl -= 4; - break; - case 0x24: /* National Language Single Shift */ - if (iei_len != 1) return "Invalid IEI len for single shift language"; - udh->ss = pdu_parse_byte(pdu, &pdu_length); - break; - case 0x25: /* National Language Single Shift */ - if (iei_len != 1) return "Invalid IEI len for locking shift language"; - udh->ls = pdu_parse_byte(pdu, &pdu_length); - break; - default: - /* skip rest of IEI */ - *pdu += iei_len * 2; - pdu_length -= iei_len * 2; - udhl -= iei_len; + while (udhl >= 2) { + int iei_type, iei_len; + + /* get type byte */ + iei_type = pdu[i++]; + + /* get length byte */ + iei_len = pdu[i++]; + + /* subtract bytes */ + udhl -= 2; + + if (iei_len >= 0 && iei_len <= udhl) { + switch (iei_type) { + case 0x00: /* Concatenated */ + if (iei_len != 3) { + chan_dongle_err = E_UNKNOWN; + return -1; + } + udh->ref = pdu[i++]; + udh->parts = pdu[i++]; + udh->order = pdu[i++]; + udhl -= 3; + break; + case 0x08: /* Concatenated, 16 bit ref */ + if (iei_len != 4) { + chan_dongle_err = E_UNKNOWN; + return -1; + } + udh->ref = (pdu[i++] << 8) | pdu[i++]; + udh->parts = pdu[i++]; + udh->order = pdu[i++]; + udhl -= 4; + break; + case 0x24: /* National Language Single Shift */ + if (iei_len != 1) { + chan_dongle_err = E_UNKNOWN; + return -1; + } + udh->ss = pdu[i++]; + break; + case 0x25: /* National Language Single Shift */ + if (iei_len != 1) { + chan_dongle_err = E_UNKNOWN; + return -1; + } + udh->ls = pdu[i++]; + break; + default: + /* skip rest of IEI */ + i += iei_len; + udhl -= iei_len; + } + } else { + chan_dongle_err = E_UNKNOWN; + return -1; } } - else - { - return "Invalid IEI len"; - } - } - /* skip rest of UDH, if any */ - *pdu += udhl * 2; - pdu_length -= udhl * 2; + /* skip rest of UDH, if any */ + i += udhl; + } - /* save message */ - *msg = *pdu; + int msg_len = pdu_length - i, out_len; + if (alphabet == PDU_DCS_ALPHABET_7BIT) { + out_len = gsm7_unpack_decode(pdu + i, udl_nibbles, msg, 1024 /* assume enough memory, as SMS messages are limited in size */, msg_padding, udh->ls, udh->ss); + if (out_len < 0) { + chan_dongle_err = E_DECODE_GSM7; + return -1; + } + } else { + out_len = msg_len / 2; + memcpy((char*)msg, pdu + i, msg_len); + msg[out_len] = '\0'; + } + msg[out_len] = '\0'; - return NULL; + return out_len; } diff --git a/pdu.h b/pdu.h index 81cdbe39..1af838fb 100644 --- a/pdu.h +++ b/pdu.h @@ -8,17 +8,41 @@ #include "export.h" /* EXPORT_DECL EXPORT_DEF */ #include "char_conv.h" /* str_encoding_t */ -typedef struct pdu_udh { - uint16_t ref; +/* Message Type Indicator Parameter */ +#define PDUTYPE_MTI_SHIFT 0 +#define PDUTYPE_MTI_SMS_DELIVER (0x00 << PDUTYPE_MTI_SHIFT) +#define PDUTYPE_MTI_SMS_DELIVER_REPORT (0x00 << PDUTYPE_MTI_SHIFT) +#define PDUTYPE_MTI_SMS_SUBMIT (0x01 << PDUTYPE_MTI_SHIFT) +#define PDUTYPE_MTI_SMS_SUBMIT_REPORT (0x01 << PDUTYPE_MTI_SHIFT) +#define PDUTYPE_MTI_SMS_STATUS_REPORT (0x02 << PDUTYPE_MTI_SHIFT) +#define PDUTYPE_MTI_SMS_COMMAND (0x02 << PDUTYPE_MTI_SHIFT) +#define PDUTYPE_MTI_RESERVED (0x03 << PDUTYPE_MTI_SHIFT) + +#define PDUTYPE_MTI_MASK (0x03 << PDUTYPE_MTI_SHIFT) +#define PDUTYPE_MTI(pdutype) ((pdutype) & PDUTYPE_MTI_MASK) + +#define TPDU_LENGTH 176 +#define PDU_LENGTH 256 + +typedef struct pdu_udh +{ + uint8_t ref; uint8_t parts, order; uint8_t ls, ss; } pdu_udh_t; +typedef struct pdu_part +{ + uint8_t buffer[PDU_LENGTH]; + size_t tpdu_length, length; +} pdu_part_t; + EXPORT_DECL void pdu_udh_init(pdu_udh_t *udh); -EXPORT_DECL char pdu_digit2code(char digit); -EXPORT_DECL const char * pdu_parse(char ** pdu, size_t tpdu_length, char * oa, size_t oa_len, str_encoding_t * oa_enc, char ** msg, str_encoding_t * msg_enc, pdu_udh_t *udh); -EXPORT_DECL int pdu_parse_sca(char ** pdu, size_t * length); -EXPORT_DECL int pdu_build_mult(int (*cb)(const char *buf, unsigned len, void *s), const char* sca, const char* dst, const char* msg, unsigned valid_minutes, int srr, unsigned csmsref, void *s); -EXPORT_DECL int pdu_build16(char* buffer, size_t length, const char* sca, const char* dst, int dcs, const uint16_t* msg, unsigned msg_len, unsigned msg_bytes, unsigned valid_minutes, int srr, const pdu_udh_t *udh); +EXPORT_DECL int pdu_build_mult(pdu_part_t *pdus, const char* sca, const char* dst, const uint16_t* msg, size_t msg_len, unsigned valid_minutes, int srr, uint8_t csmsref); +EXPORT_DECL ssize_t pdu_build(uint8_t* buffer, size_t length, size_t *tpdulen, const char* sca, const char* dst, int dcs, const uint16_t* msg, unsigned msg_len, unsigned msg_bytes, unsigned valid_minutes, int srr, const pdu_udh_t *udh); +EXPORT_DECL int pdu_parse_sca(uint8_t *pdu, size_t pdu_length, char *sca, size_t sca_len); +EXPORT_DECL int tpdu_parse_type(uint8_t *pdu, size_t pdu_length, int *type); +EXPORT_DECL int tpdu_parse_status_report(uint8_t *pdu, size_t pdu_length, int *mr, char *ra, size_t ra_len, char *scts, char *dt, int *st); +EXPORT_DECL int tpdu_parse_deliver(uint8_t *pdu, size_t pdu_length, int tpdu_type, char *oa, size_t oa_len, char *scts, uint16_t *msg, pdu_udh_t *udh); #endif /* CHAN_DONGLE_PDU_H_INCLUDED */ diff --git a/smsdb.c b/smsdb.c index 8b4afebb..417cb515 100644 --- a/smsdb.c +++ b/smsdb.c @@ -33,26 +33,36 @@ #define MAX_DB_FIELD 256 AST_MUTEX_DEFINE_STATIC(dblock); -static ast_cond_t dbcond; static sqlite3 *smsdb; -static pthread_t syncthread; -static int doexit; -static int dosync; - -static void db_sync(void); #define DEFINE_SQL_STATEMENT(stmt,sql) static sqlite3_stmt *stmt; \ const char stmt##_sql[] = sql; DEFINE_SQL_STATEMENT(get_full_message_stmt, "SELECT message FROM incoming WHERE key = ? ORDER BY seqorder") -DEFINE_SQL_STATEMENT(put_message_stmt, "INSERT OR REPLACE INTO incoming (key, seqorder, message) VALUES (?, ?, ?)") +DEFINE_SQL_STATEMENT(put_message_stmt, "INSERT OR REPLACE INTO incoming (key, seqorder, expiration, message) VALUES (?, ?, datetime(julianday(CURRENT_TIMESTAMP) + ? / 86400.0), ?)") DEFINE_SQL_STATEMENT(clear_messages_stmt, "DELETE FROM incoming WHERE key = ?") -DEFINE_SQL_STATEMENT(purge_messages_stmt, "DELETE FROM incoming WHERE arrival < CURRENT_TIMESTAMP - ?") +DEFINE_SQL_STATEMENT(purge_messages_stmt, "DELETE FROM incoming WHERE expiration < CURRENT_TIMESTAMP") DEFINE_SQL_STATEMENT(get_cnt_stmt, "SELECT COUNT(seqorder) FROM incoming WHERE key = ?") -DEFINE_SQL_STATEMENT(create_incoming_stmt, "CREATE TABLE IF NOT EXISTS incoming (key VARCHAR(256), seqorder INTEGER, arrival TIMESTAMP DEFAULT CURRENT_TIMESTAMP, message VARCHAR(256), PRIMARY KEY(key, seqorder))") +DEFINE_SQL_STATEMENT(create_incoming_stmt, "CREATE TABLE IF NOT EXISTS incoming (key VARCHAR(256), seqorder INTEGER, expiration TIMESTAMP DEFAULT CURRENT_TIMESTAMP, message VARCHAR(256), PRIMARY KEY(key, seqorder))") DEFINE_SQL_STATEMENT(create_index_stmt, "CREATE INDEX IF NOT EXISTS incoming_key ON incoming(key)") -DEFINE_SQL_STATEMENT(create_outgoing_stmt, "CREATE TABLE IF NOT EXISTS outgoing (key VARCHAR(256), refid INTEGER, PRIMARY KEY(key))") -DEFINE_SQL_STATEMENT(inc_outgoing_stmt, "INSERT INTO outgoing (key, refid) VALUES (?, 0) ON CONFLICT(key) DO UPDATE SET refid = CASE WHEN refid < 65535 THEN refid + 1 ELSE 0 END"); -DEFINE_SQL_STATEMENT(get_outgoing_stmt, "SELECT refid FROM outgoing WHERE key = ?"); +DEFINE_SQL_STATEMENT(create_outgoingref_stmt, "CREATE TABLE IF NOT EXISTS outgoing_ref (key VARCHAR(256), refid INTEGER, PRIMARY KEY(key))") // key: IMSI/DEST_ADDR +DEFINE_SQL_STATEMENT(create_outgoingmsg_stmt, "CREATE TABLE IF NOT EXISTS outgoing_msg (dev VARCHAR(256), dst VARCHAR(255), cnt INTEGER, expiration TIMESTAMP, srr BOOLEAN, payload BLOB)") +DEFINE_SQL_STATEMENT(create_outgoingpart_stmt, "CREATE TABLE IF NOT EXISTS outgoing_part (key VARCHAR(256), msg INTEGER, status INTEGER, PRIMARY KEY(key))") // key: IMSI/DEST_ADDR/MR +DEFINE_SQL_STATEMENT(create_outgoingmsg_index_stmt, "CREATE INDEX IF NOT EXISTS outgoing_part_msg ON outgoing_part(msg)") +DEFINE_SQL_STATEMENT(ins_outgoingref_stmt, "INSERT INTO outgoing_ref (refid, key) VALUES (?, ?)") // This have to be the same order as set_outgoingref_stmt +DEFINE_SQL_STATEMENT(set_outgoingref_stmt, "UPDATE outgoing_ref SET refid = ? WHERE key = ?") +DEFINE_SQL_STATEMENT(get_outgoingref_stmt, "SELECT refid FROM outgoing_ref WHERE key = ?") +DEFINE_SQL_STATEMENT(put_outgoingmsg_stmt, "INSERT INTO outgoing_msg (dev, dst, cnt, expiration, srr, payload) VALUES (?, ?, ?, datetime(julianday(CURRENT_TIMESTAMP) + ? / 86400.0), ?, ?)") +DEFINE_SQL_STATEMENT(put_outgoingpart_stmt, "INSERT INTO outgoing_part (key, msg, status) VALUES (?, ?, NULL)") +DEFINE_SQL_STATEMENT(del_outgoingmsg_stmt, "DELETE FROM outgoing_msg WHERE rowid = ?") +DEFINE_SQL_STATEMENT(del_outgoingpart_stmt, "DELETE FROM outgoing_part WHERE msg = ?") +DEFINE_SQL_STATEMENT(get_outgoingmsg_stmt, "SELECT dev, dst, srr FROM outgoing_msg WHERE rowid = ?") +DEFINE_SQL_STATEMENT(set_outgoingpart_stmt, "UPDATE outgoing_part SET status = ? WHERE rowid = ?") +DEFINE_SQL_STATEMENT(get_outgoingpart_stmt, "SELECT rowid, msg FROM outgoing_part WHERE key = ?") +DEFINE_SQL_STATEMENT(cnt_outgoingpart_stmt, "SELECT m.cnt, (SELECT COUNT(p.rowid) FROM outgoing_part p WHERE p.msg = m.rowid AND (p.status & 64 != 0 OR p.status & 32 = 0)) FROM outgoing_msg m WHERE m.rowid = ?") // count all failed and completed messages; don't count messages without delivery notification and temporary failed ones +DEFINE_SQL_STATEMENT(cnt_all_outgoingpart_stmt, "SELECT m.cnt, (SELECT COUNT(p.rowid) FROM outgoing_part p WHERE p.msg = m.rowid) FROM outgoing_msg m WHERE m.rowid = ?") +DEFINE_SQL_STATEMENT(get_payload_stmt, "SELECT payload, dst FROM outgoing_msg WHERE rowid = ?") +DEFINE_SQL_STATEMENT(get_all_status_stmt, "SELECT status FROM outgoing_part WHERE msg = ? ORDER BY rowid") +DEFINE_SQL_STATEMENT(get_expired_stmt, "SELECT rowid, payload, dst FROM outgoing_msg WHERE expiration < CURRENT_TIMESTAMP LIMIT 1") // only fetch one expired row to balance the load of each transaction static int init_stmt(sqlite3_stmt **stmt, const char *sql, size_t len) { @@ -95,9 +105,25 @@ static void clean_statements(void) clean_stmt(&get_cnt_stmt, get_cnt_stmt_sql); clean_stmt(&create_incoming_stmt, create_incoming_stmt_sql); clean_stmt(&create_index_stmt, create_index_stmt_sql); - clean_stmt(&create_outgoing_stmt, create_outgoing_stmt_sql); - clean_stmt(&inc_outgoing_stmt, inc_outgoing_stmt_sql); - clean_stmt(&get_outgoing_stmt, get_outgoing_stmt_sql); + clean_stmt(&create_outgoingref_stmt, create_outgoingref_stmt_sql); + clean_stmt(&create_outgoingmsg_stmt, create_outgoingmsg_stmt_sql); + clean_stmt(&create_outgoingpart_stmt, create_outgoingpart_stmt_sql); + clean_stmt(&create_outgoingmsg_index_stmt, create_outgoingmsg_index_stmt_sql); + clean_stmt(&ins_outgoingref_stmt, ins_outgoingref_stmt_sql); + clean_stmt(&set_outgoingref_stmt, set_outgoingref_stmt_sql); + clean_stmt(&get_outgoingref_stmt, get_outgoingref_stmt_sql); + clean_stmt(&put_outgoingmsg_stmt, put_outgoingmsg_stmt_sql); + clean_stmt(&put_outgoingpart_stmt, put_outgoingpart_stmt_sql); + clean_stmt(&del_outgoingmsg_stmt, del_outgoingmsg_stmt_sql); + clean_stmt(&del_outgoingpart_stmt, del_outgoingpart_stmt_sql); + clean_stmt(&get_outgoingmsg_stmt, get_outgoingmsg_stmt_sql); + clean_stmt(&set_outgoingpart_stmt, set_outgoingpart_stmt_sql); + clean_stmt(&get_outgoingpart_stmt, get_outgoingpart_stmt_sql); + clean_stmt(&cnt_outgoingpart_stmt, cnt_outgoingpart_stmt_sql); + clean_stmt(&cnt_all_outgoingpart_stmt, cnt_all_outgoingpart_stmt_sql); + clean_stmt(&get_payload_stmt, get_payload_stmt_sql); + clean_stmt(&get_all_status_stmt, get_all_status_stmt_sql); + clean_stmt(&get_expired_stmt, get_expired_stmt_sql); } static int init_statements(void) @@ -109,8 +135,21 @@ static int init_statements(void) || init_stmt(&clear_messages_stmt, clear_messages_stmt_sql, sizeof(clear_messages_stmt_sql)) || init_stmt(&purge_messages_stmt, purge_messages_stmt_sql, sizeof(purge_messages_stmt_sql)) || init_stmt(&get_cnt_stmt, get_cnt_stmt_sql, sizeof(get_cnt_stmt_sql)) - || init_stmt(&inc_outgoing_stmt, inc_outgoing_stmt_sql, sizeof(inc_outgoing_stmt_sql)) - || init_stmt(&get_outgoing_stmt, get_outgoing_stmt_sql, sizeof(get_outgoing_stmt_sql)); + || init_stmt(&ins_outgoingref_stmt, ins_outgoingref_stmt_sql, sizeof(ins_outgoingref_stmt_sql)) + || init_stmt(&set_outgoingref_stmt, set_outgoingref_stmt_sql, sizeof(set_outgoingref_stmt_sql)) + || init_stmt(&get_outgoingref_stmt, get_outgoingref_stmt_sql, sizeof(get_outgoingref_stmt_sql)) + || init_stmt(&put_outgoingmsg_stmt, put_outgoingmsg_stmt_sql, sizeof(put_outgoingmsg_stmt_sql)) + || init_stmt(&put_outgoingpart_stmt, put_outgoingpart_stmt_sql, sizeof(put_outgoingpart_stmt_sql)) + || init_stmt(&del_outgoingmsg_stmt, del_outgoingmsg_stmt_sql, sizeof(del_outgoingmsg_stmt_sql)) + || init_stmt(&del_outgoingpart_stmt, del_outgoingpart_stmt_sql, sizeof(del_outgoingpart_stmt_sql)) + || init_stmt(&get_outgoingmsg_stmt, get_outgoingmsg_stmt_sql, sizeof(get_outgoingmsg_stmt_sql)) + || init_stmt(&get_outgoingpart_stmt, get_outgoingpart_stmt_sql, sizeof(get_outgoingpart_stmt_sql)) + || init_stmt(&set_outgoingpart_stmt, set_outgoingpart_stmt_sql, sizeof(set_outgoingpart_stmt_sql)) + || init_stmt(&cnt_outgoingpart_stmt, cnt_outgoingpart_stmt_sql, sizeof(cnt_outgoingpart_stmt_sql)) + || init_stmt(&cnt_all_outgoingpart_stmt, cnt_all_outgoingpart_stmt_sql, sizeof(cnt_all_outgoingpart_stmt_sql)) + || init_stmt(&get_payload_stmt, get_payload_stmt_sql, sizeof(get_payload_stmt_sql)) + || init_stmt(&get_all_status_stmt, get_all_status_stmt_sql, sizeof(get_all_status_stmt_sql)) + || init_stmt(&get_expired_stmt, get_expired_stmt_sql, sizeof(get_expired_stmt_sql)); } static int db_create_smsdb(void) @@ -139,19 +178,49 @@ static int db_create_smsdb(void) sqlite3_reset(create_index_stmt); ast_mutex_unlock(&dblock); - if (!create_outgoing_stmt) { - init_stmt(&create_outgoing_stmt, create_outgoing_stmt_sql, sizeof(create_outgoing_stmt_sql)); + if (!create_outgoingref_stmt) { + init_stmt(&create_outgoingref_stmt, create_outgoingref_stmt_sql, sizeof(create_outgoingref_stmt_sql)); + } + ast_mutex_lock(&dblock); + if (sqlite3_step(create_outgoingref_stmt) != SQLITE_DONE) { + ast_log(LOG_WARNING, "Couldn't create smsdb outgoing table: %s\n", sqlite3_errmsg(smsdb)); + res = -1; + } + sqlite3_reset(create_outgoingref_stmt); + ast_mutex_unlock(&dblock); + + if (!create_outgoingmsg_stmt) { + init_stmt(&create_outgoingmsg_stmt, create_outgoingmsg_stmt_sql, sizeof(create_outgoingmsg_stmt_sql)); } ast_mutex_lock(&dblock); - if (sqlite3_step(create_outgoing_stmt) != SQLITE_DONE) { + if (sqlite3_step(create_outgoingmsg_stmt) != SQLITE_DONE) { ast_log(LOG_WARNING, "Couldn't create smsdb outgoing table: %s\n", sqlite3_errmsg(smsdb)); res = -1; } - sqlite3_reset(create_outgoing_stmt); + sqlite3_reset(create_outgoingmsg_stmt); + ast_mutex_unlock(&dblock); - db_sync(); + if (!create_outgoingpart_stmt) { + init_stmt(&create_outgoingpart_stmt, create_outgoingpart_stmt_sql, sizeof(create_outgoingpart_stmt_sql)); + } + ast_mutex_lock(&dblock); + if (sqlite3_step(create_outgoingpart_stmt) != SQLITE_DONE) { + ast_log(LOG_WARNING, "Couldn't create smsdb outgoing table: %s\n", sqlite3_errmsg(smsdb)); + res = -1; + } + sqlite3_reset(create_outgoingpart_stmt); ast_mutex_unlock(&dblock); + if (!create_outgoingmsg_index_stmt) { + init_stmt(&create_outgoingmsg_index_stmt, create_outgoingmsg_index_stmt_sql, sizeof(create_outgoingmsg_index_stmt_sql)); + } + ast_mutex_lock(&dblock); + if (sqlite3_step(create_outgoingmsg_index_stmt) != SQLITE_DONE) { + ast_log(LOG_WARNING, "Couldn't create smsdb outgoing index: %s\n", sqlite3_errmsg(smsdb)); + res = -1; + } + sqlite3_reset(create_outgoingmsg_index_stmt); + ast_mutex_unlock(&dblock); return res; } @@ -209,17 +278,23 @@ static int db_execute_sql(const char *sql, int (*callback)(void *, int, char **, static int smsdb_begin_transaction(void) { - return db_execute_sql("BEGIN TRANSACTION", NULL, NULL); + ast_mutex_lock(&dblock); + int res = db_execute_sql("BEGIN TRANSACTION", NULL, NULL); + return res; } static int smsdb_commit_transaction(void) { - return db_execute_sql("COMMIT", NULL, NULL); + int res = db_execute_sql("COMMIT", NULL, NULL); + ast_mutex_unlock(&dblock); + return res; } static int smsdb_rollback_transaction(void) { - return db_execute_sql("ROLLBACK", NULL, NULL); + int res = db_execute_sql("ROLLBACK", NULL, NULL); + ast_mutex_unlock(&dblock); + return res; } @@ -238,25 +313,28 @@ static int smsdb_rollback_transaction(void) EXPORT_DEF int smsdb_put(const char *id, const char *addr, int ref, int parts, int order, const char *msg, char *out) { const char *part; - char fullkey[MAX_DB_FIELD]; - size_t fullkey_len; + char fullkey[MAX_DB_FIELD + 1]; + int fullkey_len; int res = 0; + int ttl = CONF_GLOBAL(csms_ttl); - if (strlen(id) + strlen(addr) + 5 + 3 + 3 >= sizeof(fullkey)) { - ast_log(LOG_WARNING, "Key length must be less than %zu bytes\n", sizeof(fullkey)); + fullkey_len = snprintf(fullkey, sizeof(fullkey), "%s/%s/%d/%d", id, addr, ref, parts); + if (fullkey_len < 0) { + ast_log(LOG_ERROR, "Key length must be less than %zu bytes\n", sizeof(fullkey)); return -1; } - fullkey_len = snprintf(fullkey, sizeof(fullkey), "%s/%s/%d/%d", id, addr, ref, parts); - - ast_mutex_lock(&dblock); + smsdb_begin_transaction(); if (sqlite3_bind_text(put_message_stmt, 1, fullkey, fullkey_len, SQLITE_STATIC) != SQLITE_OK) { ast_log(LOG_WARNING, "Couldn't bind key to stmt: %s\n", sqlite3_errmsg(smsdb)); res = -1; } else if (sqlite3_bind_int(put_message_stmt, 2, order) != SQLITE_OK) { ast_log(LOG_WARNING, "Couldn't bind order to stmt: %s\n", sqlite3_errmsg(smsdb)); res = -1; - } else if (sqlite3_bind_text(put_message_stmt, 3, msg, -1, SQLITE_STATIC) != SQLITE_OK) { + } else if (sqlite3_bind_int(put_message_stmt, 3, ttl) != SQLITE_OK) { + ast_log(LOG_WARNING, "Couldn't bind TTL to stmt: %s\n", sqlite3_errmsg(smsdb)); + res = -1; + } else if (sqlite3_bind_text(put_message_stmt, 4, msg, -1, SQLITE_STATIC) != SQLITE_OK) { ast_log(LOG_WARNING, "Couldn't bind msg to stmt: %s\n", sqlite3_errmsg(smsdb)); res = -1; } else if (sqlite3_step(put_message_stmt) != SQLITE_DONE) { @@ -283,16 +361,18 @@ EXPORT_DEF int smsdb_put(const char *id, const char *addr, int ref, int parts, i res = -1; } else while (sqlite3_step(get_full_message_stmt) == SQLITE_ROW) { part = (const char*)sqlite3_column_text(get_full_message_stmt, 0); + int partlen = sqlite3_column_bytes(get_full_message_stmt, 0); if (!part) { ast_log(LOG_WARNING, "Couldn't get value\n"); res = -1; break; } - out = stpcpy(out, part); + out = stpncpy(out, part, partlen); } + out[0] = '\0'; sqlite3_reset(get_full_message_stmt); - if (res != -1) { + if (res >= 0) { if (sqlite3_bind_text(clear_messages_stmt, 1, fullkey, fullkey_len, SQLITE_STATIC) != SQLITE_OK) { ast_log(LOG_WARNING, "Couldn't bind key to stmt: %s\n", sqlite3_errmsg(smsdb)); res = -1; @@ -303,127 +383,371 @@ EXPORT_DEF int smsdb_put(const char *id, const char *addr, int ref, int parts, i } } - db_sync(); + smsdb_commit_transaction(); - ast_mutex_unlock(&dblock); + return res; +} + +static int smsdb_purge() +{ + int res = 0; + + if (sqlite3_step(purge_messages_stmt) != SQLITE_DONE) { + res = -1; + } + sqlite3_reset(purge_messages_stmt); return res; } -static int smsdb_purge(int ttl) +EXPORT_DEF int smsdb_get_refid(const char *id, const char *addr) { int res = 0; - if (sqlite3_bind_int(purge_messages_stmt, 1, ttl) != SQLITE_OK) { + smsdb_begin_transaction(); + + char fullkey[MAX_DB_FIELD + 1]; + int fullkey_len; + + fullkey_len = snprintf(fullkey, sizeof(fullkey), "%s/%s", id, addr); + if (fullkey_len < 0) { + ast_log(LOG_ERROR, "Key length must be less than %zu bytes\n", sizeof(fullkey)); + return -1; + } + + int use_insert = 0; + if (sqlite3_bind_text(get_outgoingref_stmt, 1, fullkey, fullkey_len, SQLITE_STATIC) != SQLITE_OK) { ast_log(LOG_WARNING, "Couldn't bind key to stmt: %s\n", sqlite3_errmsg(smsdb)); res = -1; - } else if (sqlite3_step(purge_messages_stmt) != SQLITE_DONE) { + } else if (sqlite3_step(get_outgoingref_stmt) != SQLITE_ROW) { + res = 255; + use_insert = 1; + } else { + res = sqlite3_column_int(get_outgoingref_stmt, 0); + } + sqlite3_reset(get_outgoingref_stmt); + + if (res >= 0) { + ++res; + if (res >= 256) res = 0; + sqlite3_stmt *stmt = use_insert ? ins_outgoingref_stmt : set_outgoingref_stmt; + if (sqlite3_bind_int(stmt, 1, res) != SQLITE_OK) { + ast_log(LOG_WARNING, "Couldn't bind refid to stmt: %s\n", sqlite3_errmsg(smsdb)); + res = -1; + } else if (sqlite3_bind_text(stmt, 2, fullkey, fullkey_len, SQLITE_STATIC) != SQLITE_OK) { + ast_log(LOG_WARNING, "Couldn't bind key to stmt: %s\n", sqlite3_errmsg(smsdb)); + res = -1; + } else if (sqlite3_step(stmt) != SQLITE_DONE) { + res = -1; + } + sqlite3_reset(stmt); + } + + smsdb_commit_transaction(); + + return res; +} +EXPORT_DEF int smsdb_outgoing_add(const char *id, const char *addr, int cnt, int ttl, int srr, const char *payload, size_t len) +{ + int res = 0; + + smsdb_begin_transaction(); + + if (sqlite3_bind_text(put_outgoingmsg_stmt, 1, id, strlen(id), SQLITE_STATIC) != SQLITE_OK) { + ast_log(LOG_WARNING, "Couldn't bind dev to stmt: %s\n", sqlite3_errmsg(smsdb)); + res = -1; + } else if (sqlite3_bind_text(put_outgoingmsg_stmt, 2, addr, strlen(addr), SQLITE_STATIC) != SQLITE_OK) { + ast_log(LOG_WARNING, "Couldn't bind destination address to stmt: %s\n", sqlite3_errmsg(smsdb)); + res = -1; + } else if (sqlite3_bind_int(put_outgoingmsg_stmt, 3, cnt) != SQLITE_OK) { + ast_log(LOG_WARNING, "Couldn't bind count to stmt: %s\n", sqlite3_errmsg(smsdb)); + res = -1; + } else if (sqlite3_bind_int(put_outgoingmsg_stmt, 4, ttl) != SQLITE_OK) { + ast_log(LOG_WARNING, "Couldn't bind TTL to stmt: %s\n", sqlite3_errmsg(smsdb)); res = -1; + } else if (sqlite3_bind_int(put_outgoingmsg_stmt, 5, srr) != SQLITE_OK) { + ast_log(LOG_WARNING, "Couldn't bind SRR to stmt: %s\n", sqlite3_errmsg(smsdb)); + res = -1; + } else if (sqlite3_bind_blob(put_outgoingmsg_stmt, 6, payload, len > SMSDB_PAYLOAD_MAX_LEN ? SMSDB_PAYLOAD_MAX_LEN : len, SQLITE_STATIC) != SQLITE_OK) { + ast_log(LOG_WARNING, "Couldn't bind payload to stmt: %s\n", sqlite3_errmsg(smsdb)); + res = -1; + } else if (sqlite3_step(put_outgoingmsg_stmt) != SQLITE_DONE) { + res = -1; + } else { + res = sqlite3_last_insert_rowid(smsdb); } - sqlite3_reset(purge_messages_stmt); + sqlite3_reset(put_outgoingmsg_stmt); + + smsdb_commit_transaction(); return res; } -EXPORT_DEF int smsdb_outgoing_get(const char *id) +static int smsdb_outgoing_clear_nolock(int uid) { int res = 0; - ast_mutex_lock(&dblock); + if (sqlite3_bind_int(del_outgoingmsg_stmt, 1, uid) != SQLITE_OK) { + ast_log(LOG_WARNING, "Couldn't bind UID to stmt: %s\n", sqlite3_errmsg(smsdb)); + res = -1; + } else if (sqlite3_step(del_outgoingmsg_stmt) != SQLITE_DONE) { + res = -1; + } + sqlite3_reset(del_outgoingmsg_stmt); + + if (sqlite3_bind_int(del_outgoingpart_stmt, 1, uid) != SQLITE_OK) { + ast_log(LOG_WARNING, "Couldn't bind UID to stmt: %s\n", sqlite3_errmsg(smsdb)); + res = -1; + } else if (sqlite3_step(del_outgoingpart_stmt) != SQLITE_DONE) { + res = -1; + } + sqlite3_reset(del_outgoingpart_stmt); + + return res; +} +EXPORT_DEF ssize_t smsdb_outgoing_clear(int uid, char *dst, char *payload) +{ + int res = 0; + smsdb_begin_transaction(); - if (sqlite3_bind_text(inc_outgoing_stmt, 1, id, strlen(id), SQLITE_STATIC) != SQLITE_OK) { + if (sqlite3_bind_int(get_payload_stmt, 1, uid) != SQLITE_OK) { ast_log(LOG_WARNING, "Couldn't bind key to stmt: %s\n", sqlite3_errmsg(smsdb)); res = -1; - } else if (sqlite3_step(inc_outgoing_stmt) != SQLITE_DONE) { + } else if (sqlite3_step(get_payload_stmt) != SQLITE_ROW) { res = -1; + } else { + strcpy(dst, (const char*)sqlite3_column_text(get_payload_stmt, 1)); + res = sqlite3_column_bytes(get_payload_stmt, 0); + res = res > SMSDB_PAYLOAD_MAX_LEN ? SMSDB_PAYLOAD_MAX_LEN : res; + memcpy(payload, sqlite3_column_blob(get_payload_stmt, 0), res); } - sqlite3_reset(inc_outgoing_stmt); - db_sync(); + sqlite3_reset(get_payload_stmt); + + if (res != -1 && smsdb_outgoing_clear_nolock(uid) < 0) { + res = -1; + } + + smsdb_commit_transaction(); + + return res; +} +EXPORT_DEF ssize_t smsdb_outgoing_part_put(int uid, int refid, char *dst, char *payload) +{ + int res = 0; + char fullkey[MAX_DB_FIELD + 1]; + int fullkey_len; + int srr = 0, cnt, cur; + + smsdb_begin_transaction(); - if (res != -1) { - if (sqlite3_bind_text(get_outgoing_stmt, 1, id, strlen(id), SQLITE_STATIC) != SQLITE_OK) { + if (sqlite3_bind_int(get_outgoingmsg_stmt, 1, uid) != SQLITE_OK) { + ast_log(LOG_WARNING, "Couldn't bind UID to stmt: %s\n", sqlite3_errmsg(smsdb)); + res = -1; + } else if (sqlite3_step(get_outgoingmsg_stmt) != SQLITE_ROW) { + res = -2; + } else { + const char *dev = (const char*)sqlite3_column_text(get_outgoingmsg_stmt, 0); + const char *dst = (const char*)sqlite3_column_text(get_outgoingmsg_stmt, 1); + srr = sqlite3_column_int(get_outgoingmsg_stmt, 2); + + fullkey_len = snprintf(fullkey, sizeof(fullkey), "%s/%s/%d", dev, dst, refid); + if (fullkey_len < 0) { + ast_log(LOG_ERROR, "Key length must be less than %zu bytes\n", sizeof(fullkey)); + return -1; + } + } + sqlite3_reset(get_outgoingmsg_stmt); + + if (res >= 0) { + if (sqlite3_bind_text(put_outgoingpart_stmt, 1, fullkey, fullkey_len, SQLITE_STATIC) != SQLITE_OK) { ast_log(LOG_WARNING, "Couldn't bind key to stmt: %s\n", sqlite3_errmsg(smsdb)); res = -1; - } else if (sqlite3_step(get_outgoing_stmt) != SQLITE_ROW) { - ast_debug(1, "Unable to find key '%s'\n", id); + } else if (sqlite3_bind_int(put_outgoingpart_stmt, 2, uid) != SQLITE_OK) { + ast_log(LOG_WARNING, "Couldn't bind UID to stmt: %s\n", sqlite3_errmsg(smsdb)); + res = -1; + } else if (sqlite3_step(put_outgoingpart_stmt) != SQLITE_DONE) { res = -1; } - res = sqlite3_column_int(get_outgoing_stmt, 0); - sqlite3_reset(get_outgoing_stmt); + sqlite3_reset(put_outgoingpart_stmt); } - ast_mutex_unlock(&dblock); + if (srr) { + res = -2; + } + + // if no status report is requested, just count successfully inserted parts and return payload if the counter reached the number of parts + if (res >= 0) { + if (sqlite3_bind_int(cnt_all_outgoingpart_stmt, 1, uid) != SQLITE_OK) { + ast_log(LOG_WARNING, "Couldn't bind key to stmt: %s\n", sqlite3_errmsg(smsdb)); + res = -1; + } else if (sqlite3_step(cnt_all_outgoingpart_stmt) != SQLITE_ROW) { + res = -1; + } else { + cur = sqlite3_column_int(cnt_all_outgoingpart_stmt, 0); + cnt = sqlite3_column_int(cnt_all_outgoingpart_stmt, 1); + } + sqlite3_reset(cnt_all_outgoingpart_stmt); + } + + if (res >= 0 && cur != cnt) { + res = -2; + } + + // get payload + if (res >= 0) { + if (sqlite3_bind_int(get_payload_stmt, 1, uid) != SQLITE_OK) { + ast_log(LOG_WARNING, "Couldn't bind key to stmt: %s\n", sqlite3_errmsg(smsdb)); + res = -1; + } else if (sqlite3_step(get_payload_stmt) != SQLITE_ROW) { + res = -1; + } else { + strcpy(dst, (const char*)sqlite3_column_text(get_payload_stmt, 1)); + res = sqlite3_column_bytes(get_payload_stmt, 0); + res = res > SMSDB_PAYLOAD_MAX_LEN ? SMSDB_PAYLOAD_MAX_LEN : res; + memcpy(payload, sqlite3_column_blob(get_payload_stmt, 0), res); + } + sqlite3_reset(get_payload_stmt); + } + + // clear if everything is finished + if (res >= 0 && smsdb_outgoing_clear_nolock(uid) < 0) { + res = -1; + } + + + smsdb_commit_transaction(); return res; } -/*! - * \internal - * \brief Signal the smsdb sync thread to do its thing. - * - * \note dblock is assumed to be held when calling this function. - */ -static void db_sync(void) +EXPORT_DEF ssize_t smsdb_outgoing_part_status(const char *id, const char *addr, int mr, int st, int *status_all, char *payload) { - dosync = 1; - ast_cond_signal(&dbcond); -} + char fullkey[MAX_DB_FIELD + 1]; + int fullkey_len; + int res = 0, partid, uid, cur, cnt; + + fullkey_len = snprintf(fullkey, sizeof(fullkey), "%s/%s/%d", id, addr, mr); + if (fullkey_len < 0) { + ast_log(LOG_ERROR, "Key length must be less than %zu bytes\n", sizeof(fullkey)); + return -1; + } -/*! - * \internal - * \brief smsdb sync thread - * - * This thread is in charge of syncing smsdb to disk after a change. - * By pushing it off to this thread to take care of, this I/O bound operation - * will not block other threads from performing other critical processing. - * If changes happen rapidly, this thread will also ensure that the sync - * operations are rate limited. - */ -static void *db_sync_thread(void *data) -{ - (void)(data); - ast_mutex_lock(&dblock); smsdb_begin_transaction(); - for (;;) { - /* If dosync is set, db_sync() was called during sleep(1), - * and the pending transaction should be committed. - * Otherwise, block until db_sync() is called. - */ - while (!dosync) { - ast_cond_wait(&dbcond, &dblock); + + if (sqlite3_bind_text(get_outgoingpart_stmt, 1, fullkey, fullkey_len, SQLITE_STATIC) != SQLITE_OK) { + ast_log(LOG_WARNING, "Couldn't bind key to stmt: %s\n", sqlite3_errmsg(smsdb)); + res = -1; + } else if (sqlite3_step(get_outgoingpart_stmt) != SQLITE_ROW) { + res = -1; + } else { + partid = sqlite3_column_int(get_outgoingpart_stmt, 0); + uid = sqlite3_column_int(get_outgoingpart_stmt, 1); + } + sqlite3_reset(get_outgoingpart_stmt); + + // set status + if (res >= 0) { + if (sqlite3_bind_int(set_outgoingpart_stmt, 1, st) != SQLITE_OK) { + ast_log(LOG_WARNING, "Couldn't bind status to stmt: %s\n", sqlite3_errmsg(smsdb)); + res = -1; + } else if (sqlite3_bind_int(set_outgoingpart_stmt, 2, partid) != SQLITE_OK) { + ast_log(LOG_WARNING, "Couldn't bind ID to stmt: %s\n", sqlite3_errmsg(smsdb)); + res = -1; + } else if (sqlite3_step(set_outgoingpart_stmt) != SQLITE_DONE) { + res = -1; + } + sqlite3_reset(set_outgoingpart_stmt); + } + + // get count + if (res >= 0) { + if (sqlite3_bind_int(cnt_outgoingpart_stmt, 1, uid) != SQLITE_OK) { + ast_log(LOG_WARNING, "Couldn't bind key to stmt: %s\n", sqlite3_errmsg(smsdb)); + res = -1; + } else if (sqlite3_step(cnt_outgoingpart_stmt) != SQLITE_ROW) { + res = -1; + } else { + cur = sqlite3_column_int(cnt_outgoingpart_stmt, 0); + cnt = sqlite3_column_int(cnt_outgoingpart_stmt, 1); } - dosync = 0; - smsdb_purge(CONF_GLOBAL(csms_ttl)); - if (smsdb_commit_transaction()) { - smsdb_rollback_transaction(); + sqlite3_reset(cnt_outgoingpart_stmt); + } + + if (res != -1 && cur != cnt) { + res = -2; + } + + // get status array + if (res >= 0) { + int i = 0; + if (sqlite3_bind_int(get_all_status_stmt, 1, uid) != SQLITE_OK) { + ast_log(LOG_WARNING, "Couldn't bind key to stmt: %s\n", sqlite3_errmsg(smsdb)); + res = -1; + } else while (sqlite3_step(get_all_status_stmt) == SQLITE_ROW) { + status_all[i++] = sqlite3_column_int(get_all_status_stmt, 0); } - if (doexit) { - ast_mutex_unlock(&dblock); - break; + status_all[i] = -1; + sqlite3_reset(get_all_status_stmt); + } + + // get payload + if (res >= 0) { + if (sqlite3_bind_int(get_payload_stmt, 1, uid) != SQLITE_OK) { + ast_log(LOG_WARNING, "Couldn't bind key to stmt: %s\n", sqlite3_errmsg(smsdb)); + res = -1; + } else if (sqlite3_step(get_payload_stmt) != SQLITE_ROW) { + res = -1; + } else { + res = sqlite3_column_bytes(get_payload_stmt, 0); + res = res > SMSDB_PAYLOAD_MAX_LEN ? SMSDB_PAYLOAD_MAX_LEN : res; + memcpy(payload, sqlite3_column_blob(get_payload_stmt, 0), res); } - smsdb_begin_transaction(); - ast_mutex_unlock(&dblock); - sleep(1); - ast_mutex_lock(&dblock); + sqlite3_reset(get_payload_stmt); + } + + // clear if everything is finished + if (res >= 0 && smsdb_outgoing_clear_nolock(uid) < 0) { + res = -1; } - return NULL; + smsdb_commit_transaction(); + + return res; +} + +EXPORT_DEF ssize_t smsdb_outgoing_purge_one(char *dst, char *payload) +{ + int res = -1, uid; + + smsdb_begin_transaction(); + + if (sqlite3_step(get_expired_stmt) != SQLITE_ROW) { + res = -1; + } else { + uid = sqlite3_column_int(get_expired_stmt, 0); + + strcpy(dst, (const char*)sqlite3_column_text(get_expired_stmt, 2)); + res = sqlite3_column_bytes(get_expired_stmt, 1); + res = res > SMSDB_PAYLOAD_MAX_LEN ? SMSDB_PAYLOAD_MAX_LEN : res; + memcpy(payload, sqlite3_column_blob(get_expired_stmt, 1), res); + } + sqlite3_reset(get_expired_stmt); + + if (res != -1 && smsdb_outgoing_clear_nolock(uid) < 0) { + res = -1; + } + + smsdb_commit_transaction(); + + return res; } /*! * \internal * \brief Clean up resources on Asterisk shutdown */ -static void smsdb_atexit(void) +EXPORT_DEF void smsdb_atexit() { - /* Set doexit to 1 to kill thread. db_sync must be called with - * mutex held. */ - ast_mutex_lock(&dblock); - doexit = 1; - db_sync(); - ast_mutex_unlock(&dblock); - - pthread_join(syncthread, NULL); ast_mutex_lock(&dblock); clean_statements(); if (sqlite3_close(smsdb) == SQLITE_OK) { @@ -432,18 +756,11 @@ static void smsdb_atexit(void) ast_mutex_unlock(&dblock); } -int smsdb_init() +EXPORT_DEF int smsdb_init() { - ast_cond_init(&dbcond, NULL); - if (db_init()) { return -1; } - if (ast_pthread_create_background(&syncthread, NULL, db_sync_thread, NULL)) { - return -1; - } - - ast_register_atexit(smsdb_atexit); return 0; } diff --git a/smsdb.h b/smsdb.h index 52daa35c..3ab3d027 100644 --- a/smsdb.h +++ b/smsdb.h @@ -7,8 +7,17 @@ #include "export.h" /* EXPORT_DECL EXPORT_DEF */ +#define SMSDB_PAYLOAD_MAX_LEN 4096 +#define SMSDB_DST_MAX_LEN 256 + EXPORT_DECL int smsdb_init(); +EXPORT_DECL void smsdb_atexit(); EXPORT_DECL int smsdb_put(const char *id, const char *addr, int ref, int parts, int order, const char *msg, char *out); -EXPORT_DECL int smsdb_outgoing_get(const char *id); +EXPORT_DECL int smsdb_get_refid(const char *id, const char *addr); +EXPORT_DECL int smsdb_outgoing_add(const char *id, const char *addr, int cnt, int ttl, int srr, const char *payload, size_t len); +EXPORT_DECL ssize_t smsdb_outgoing_clear(int uid, char *dst, char *payload); +EXPORT_DECL ssize_t smsdb_outgoing_part_put(int uid, int refid, char *dst, char *payload); +EXPORT_DECL ssize_t smsdb_outgoing_part_status(const char *id, const char *addr, int mr, int st, int *status_all, char *payload); +EXPORT_DECL ssize_t smsdb_outgoing_purge_one(char *dst, char *payload); #endif diff --git a/test/gen.c b/test/gen.c index 52ca6fab..eef37a3e 100644 --- a/test/gen.c +++ b/test/gen.c @@ -1,4 +1,6 @@ #include +#include + #include "pdu.h" #include "gsm7_luts.h" @@ -13,70 +15,73 @@ void ast_log(int level, const char* fmt, ...) (void)fmt; } -static const char *res1 = "0031000B916407281553F800000B021B14"; -static const char *res2[] = { - "0071000B919408103254F600000BA006080400010201C8329BFD7681A8E8F41C949E83C2207939CC66E741ECB7FB0C9A36A72E50920E1ABFDDF470DA3D07B5DFF232888E0EBB41311B0C344687E5E131BD2C9F83C26E32888EAECF41E3B0DBFDA683C46550D93D7E93CB64507D9E769F4161D03CED3EB3CBA06973EA0225E9A0767D4E0789CBA0399C9DA683EA7050DA4D7F83DA75363D0D671740", - "0071000B919408103254F600000B6F06080400010202D3E614444787E9A0B0BC0C1ABFDDE330BDEC0ED3CB64D01D5D7683E8E8721E14969741F2F2B89CB697C92E90B36C2FCBE9E832BB3C9FB34074747A0E9A36A7A0F1DB4D0FA7DD73D0DBCDCE838ED3E60D344687E5E131BD2C9F8700" -}; -static const char *res3 = "0031000B916407281553F800080B1A00680065006C006C006F00200077006F0072006C0064D83DDE0B"; -static const char *res4[] = { - "0071000B916407281553F800080B8B06080400010201003100320033003400350036003700380039003000310032003300340035003600370038003900300031003200330034003500360037003800390030003100320033003400350036003700380039003000310032003300340035003600370038003900300031003200330034003500360037003800390030003100320033003400350036", - "0071000B916407281553F800080B1D06080400010202003700380039003000680065006C006C006FD83DDE0B" -}; - -int idx; - -int pdu_send_cb1(const char *buf, unsigned len, void *s) -{ - if (strcmp(buf, res1)) { - fprintf(stderr, "Check 1 unsuccessful; Expected %s, got %s\n", res1, buf); - ++faults; - } else { - ++ok; - } - return 0; -} -int pdu_send_cb2(const char *buf, unsigned len, void *s) -{ - if (strcmp(buf, res2[idx])) { - fprintf(stderr, "Check 2 unsuccessful; Expected %s, got %s\n", res2[idx], buf); - ++faults; - } else { - ++ok; - } - ++idx; - return 0; -} -int pdu_send_cb3(const char *buf, unsigned len, void *s) -{ - if (strcmp(buf, res3)) { - fprintf(stderr, "Check 3 unsuccessful; Expected %s, got %s\n", res3, buf); - ++faults; - } else { - ++ok; - } - return 0; -} -int pdu_send_cb4(const char *buf, unsigned len, void *s) +typedef struct result { - if (strcmp(buf, res4[idx])) { - fprintf(stderr, "Check 4 unsuccessful; Expected %s, got %s\n", res4[idx], buf); - ++faults; - } else { - ++ok; - } - ++idx; - return 0; -} + const char *dst; + const char *in; + const char *out[16]; +} result_t; void test_pdu_build() { -// printf("%s\n", LUT_GSM7_LS[0][0]); - pdu_build_mult(pdu_send_cb1, "", "+46708251358", "{", 60, 1, 1, NULL); - idx = 0; - pdu_build_mult(pdu_send_cb2, "", "+49800123456", "Hello. This is a really long SMS. It contains more than 160 characters and thus cannot be encoded using a single SMS. It must be split up into multiplé SMS that are concatenated when they are received. Nevertheless, this SMS contains only GSM7 characters!", 60, 1, 1, NULL); - pdu_build_mult(pdu_send_cb3, "", "+46708251358", "hello world😋", 60, 1, 1, NULL); - idx = 0; - pdu_build_mult(pdu_send_cb4, "", "+46708251358", "1234567890123456789012345678901234567890123456789012345678901234567890hello😋", 60, 1, 1, NULL); + result_t res[] = { + { + "+46708251358", + "{", + { + "0031000B916407281553F800000B021B14", + NULL + } + }, { + "+49800123456", + "Hello. This is a really long SMS. It contains more than 160 characters and thus cannot be encoded using a single SMS. It must be split up into multiplé SMS that are concatenated when they are received. Nevertheless, this SMS contains only GSM7 characters!", + { + "0071000B919408103254F600000B9F050003010201906536FBED0251D1E939283D078541F27298CDCE83D86FF719346D4E5DA0241D347EBBE9E1B47B0E6ABFE565101D1D7683623618688C0ECBC3637A593E0785DD64101D5D9F83C661B7FB4D0789CBA0B27BFC2697C9A0FA3CED3E83C2A079DA7D669741D3E6D4054AD241EDFA9C0E12974173383B4D07D5E1A0B49BFE06B5EB6C7A1ACE2E8000", + "0071000B919408103254F600000B6E050003010202A6CD29888E0ED341617919347EBBC7617AD91DA697C9A03BBAEC06D1D1E53C282C2F83E4E571396D2F935D2067D95E96D3D16576793E6781E8E8F41C346D4E41E3B79B1E4EBBE7A0B79B9D071DA7CD1B688C0ECBC3637A593E0F01", + NULL + } + }, { + "+46708251358", + "hello world😋", + { + "0031000B916407281553F800080B1A00680065006C006C006F00200077006F0072006C0064D83DDE0B", + NULL + } + }, { + "+46708251358", + "1234567890123456789012345678901234567890123456789012345678901234567890hello😋", + { + "0071000B916407281553F800080B8C0500030102010031003200330034003500360037003800390030003100320033003400350036003700380039003000310032003300340035003600370038003900300031003200330034003500360037003800390030003100320033003400350036003700380039003000310032003300340035003600370038003900300031003200330034003500360037", + "0071000B916407281553F800080B1A05000301020200380039003000680065006C006C006FD83DDE0B", + NULL + } + } + }; + + uint16_t ucs2[256]; + pdu_part_t pdus[255]; + char hexbuf[PDU_LENGTH * 2 + 1]; + for (int i = 0; i < sizeof(res) / sizeof(result_t); ++i) { + int ret = utf8_to_ucs2(res[i].in, strlen(res[i].in), ucs2, sizeof(ucs2)); + if (ret < 0) { + fprintf(stderr, "Check %d unsuccessful: UTF-8-to-UCS-2 returns failure code %d\n", i, ret); + } + int cnt = pdu_build_mult(pdus, "", res[i].dst, ucs2, ret, 60, 1, 1); + if (cnt <= 0) { + fprintf(stderr, "Check %d unsuccessful: PDU-Build returns failure code %d\n", i, cnt); + ++faults; + continue; + } + for (int j = 0; j < cnt; ++j) { + hexify(pdus[j].buffer, pdus[j].length, hexbuf); + if (res[i].out[j] && strcmp(res[i].out[j], hexbuf) == 0) { + ++ok; + } else { + ++faults; + fprintf(stderr, "Check %d unsuccessful: Expected %s; Got %s\n", i, res[i].out[j], hexbuf); + } + } + if (res[i].out[cnt]) fprintf(stderr, "Check %d unsuccessful: Expected %s; Got %s\n", i, res[i].out[cnt], NULL); + } } diff --git a/test/parse.c b/test/parse.c index c72badb0..db391106 100644 --- a/test/parse.c +++ b/test/parse.c @@ -180,118 +180,113 @@ int safe_strcmp(const char *a, const char *b) } #/* */ +void test_gsm7() +{ + const char *in = "0123456789"; + for (int i = 0; i < 10; ++i) { + for (int j = 0; j < 7; ++j) { + char inin[128]; + strncpy(inin, in, i + 1); + inin[i + 1] = 0; + uint8_t pdu[256]; + uint16_t buf16[256]; + int res = utf8_to_ucs2(inin, strlen(inin), buf16, 256); + res = gsm7_encode(buf16, res, buf16); + int packedsize = gsm7_pack(buf16, res, pdu, res * 2, j); + hexify(pdu, (packedsize + 1) / 2, pdu); + res = unhex(pdu, pdu); + res = gsm7_unpack_decode(pdu, packedsize, buf16, res, j, 0, 0); + char rev[256]; + res = ucs2_to_utf8(buf16, res, rev, 256); + rev[res] = 0; + if (strcmp(rev, inin) == 0) { + ++ok; + } else { + ++faults; + printf("%s != %s %d %d\n", inin, rev, i + 1, res); + } + } + } +} void test_parse_cmgr() { struct result { - const char * res; + char * res; char * str; char * oa; - str_encoding_t oa_enc; char * msg; - str_encoding_t msg_enc; char * msg_utf8; + char * sca; + int tpdu_type; pdu_udh_t udh; + int mr, st; + char scts[256], dt[256]; + size_t msg_len; }; static const struct test_case { const char * input; struct result result; } cases[] = { - { "+CMGR: \"REC READ\",\"+79139131234\",,\"10/12/05,22:00:04+12\"\r\n041F04400438043204350442", - { - NULL, - "\"REC READ\",\"+79139131234", - "+79139131234", - STR_ENCODING_ASCII, - "041F04400438043204350442", - STR_ENCODING_UNKNOWN, - NULL - } - }, - { "+CMGR: \"REC READ\",\"002B00370039003500330037003600310032003000350032\",,\"10/12/05,22:00:04+12\"\r\n041F04400438043204350442", - { - NULL, - "\"REC READ\",\"002B00370039003500330037003600310032003000350032", - "002B00370039003500330037003600310032003000350032", - STR_ENCODING_UNKNOWN, - "041F04400438043204350442", - STR_ENCODING_UNKNOWN, - NULL - } - }, { "+CMGR: 0,,106\r\n07911111111100F3040B911111111111F200000121702214952163B1582C168BC562B1984C2693C96432994C369BCD66B3D96C369BD168341A8D46A3D168B55AAD56ABD56AB59ACD66B3D96C369BCD76BBDD6EB7DBED76BBE170381C0E87C3E170B95C2E97CBE572B91C0C0683C16030180C", { - NULL, + 0, "B1582C168BC562B1984C2693C96432994C369BCD66B3D96C369BD168341A8D46A3D168B55AAD56ABD56AB59ACD66B3D96C369BCD76BBDD6EB7DBED76BBE170381C0E87C3E170B95C2E97CBE572B91C0C0683C16030180C", "+11111111112", - STR_ENCODING_ASCII, "B1582C168BC562B1984C2693C96432994C369BCD66B3D96C369BD168341A8D46A3D168B55AAD56ABD56AB59ACD66B3D96C369BCD76BBDD6EB7DBED76BBE170381C0E87C3E170B95C2E97CBE572B91C0C0683C16030180C", - STR_ENCODING_GSM7_HEX_PAD_0, "111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999000000000" } }, { "+CMGR: 0,,159\r\n07919740430900F3440B912222222220F20008012180004390218C0500030003010031003100310031003100310031003100310031003200320032003200320032003200320032003200330033003300330033003300330033003300330034003400340034003400340034003400340034003500350035003500350035003500350035003500360036003600360036003600360036003600360037003700370037003700370037", { - NULL, + 0, "0031003100310031003100310031003100310031003200320032003200320032003200320032003200330033003300330033003300330033003300330034003400340034003400340034003400340034003500350035003500350035003500350035003500360036003600360036003600360036003600360037003700370037003700370037", "+22222222022", - STR_ENCODING_ASCII, "0031003100310031003100310031003100310031003200320032003200320032003200320032003200330033003300330033003300330033003300330034003400340034003400340034003400340034003500350035003500350035003500350035003500360036003600360036003600360036003600360037003700370037003700370037", - STR_ENCODING_UCS2_HEX, "1111111111222222222233333333334444444444555555555566666666667777777" } }, { "+CMGR: 0,,159\r\n07913306000000F0440B913306000000F0000061011012939280A0050003CA020182E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3", { - NULL, + 0, "82E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3", "+33600000000", - STR_ENCODING_ASCII, "82E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3", - STR_ENCODING_GSM7_HEX_PAD_1, "Aaaaa Aaaaa Aaaaa Aaaaa Aaaaa Aaaaa Aaaaa Aaaaa Aaaaa Aaaaa Aaaaa Aaaaa Aaaaa Aaaaa Aaaaa Aaaaa Aaaaa Aaaaa Aaaaa Aaaaa Aaaaa Aaaaa Aaaaa Aaaaa Aaaaa Aaa" } }, { "+CMGR: 0,,43\r\n07913306000000F0640B913306000000F00000610110129303801B050003CA0202C26150301C0E8741C170381C0605C3E17018", { - NULL, + 0, "C26150301C0E8741C170381C0605C3E17018", "+33600000000", - STR_ENCODING_ASCII, "C26150301C0E8741C170381C0605C3E17018", - STR_ENCODING_GSM7_HEX_PAD_1, "aa Aaaaa Aaaaa Aaaaa" } }, { "+CMGR: 0,,158\r\n07916407970970F6400A912222222222000041903021825180A0050003000301A9E5391D14060941439015240409414290102404094142901024040941429010240409414290106405594142901564055941429012A40429AD4AABD22A7481AC56101264455A915624C80AB282AC20A1D06A0559415610D20A4282AC2024C80AB282AC202BC80AB282AC2E9012B4042D414A90D2055282942E90D20502819420254809528294", { - NULL, + 0, "A9E5391D14060941439015240409414290102404094142901024040941429010240409414290106405594142901564055941429012A40429AD4AABD22A7481AC56101264455A915624C80AB282AC20A1D06A0559415610D20A4282AC2024C80AB282AC202BC80AB282AC2E9012B4042D414A90D2055282942E90D20502819420254809528294", "+2222222222", - STR_ENCODING_ASCII, "A9E5391D14060941439015240409414290102404094142901024040941429010240409414290106405594142901564055941429012A40429AD4AABD22A7481AC56101264455A915624C80AB282AC20A1D06A0559415610D20A4282AC2024C80AB282AC202BC80AB282AC2E9012B4042D414A90D2055282942E90D20502819420254809528294", - STR_ENCODING_GSM7_HEX_PAD_1, "Test a B C V B B B B B B B B B B B B B B B B V V B V V V B J J JVJVJVB. VV H VHVHVH V V V BBVV V V HV H V H V V V V V V V. J K K J J. J J. J. J J J J J" } }, { "+CMGR: 0,,55\r\n07912933035011804409D055F3DB5D060000411120712071022A080701030003990202A09976D7E9E5390B640FB3D364103DCD668364B3562CD692C1623417", { - NULL, + 0, "A09976D7E9E5390B640FB3D364103DCD668364B3562CD692C1623417", "Ufone", /* 55F3DB5D062 */ - STR_ENCODING_GSM7_HEX_PAD_0, "A09976D7E9E5390B640FB3D364103DCD668364B3562CD692C1623417", - STR_ENCODING_GSM7_HEX_PAD_5, "Minutes, valid till 23-11-2014." } }, { "+CMGR: 0,,137\r\n07919333851805320409D034186C360300F0713032810105408849A7F1099A36A720D9EC059BB140319C2E06D38186EF39FD0D1AA3D3E176981E06155D20184B467381926CD0585E26A7E96F1001547481683816ACE60241CB7250DA6D7E83E67550D95E76D3EB61761AF486EBD36F771A14A6D3D3F632A80C12BFDDF539485E9EA7C9F534688C4E87DB61100D968BD95C", { - NULL, + 0, "49A7F1099A36A720D9EC059BB140319C2E06D38186EF39FD0D1AA3D3E176981E06155D20184B467381926CD0585E26A7E96F1001547481683816ACE60241CB7250DA6D7E83E67550D95E76D3EB61761AF486EBD36F771A14A6D3D3F632A80C12BFDDF539485E9EA7C9F534688C4E87DB61100D968BD95C", "40033", /* 09D034186C3603 */ - STR_ENCODING_GSM7_HEX_PAD_0, "49A7F1099A36A720D9EC059BB140319C2E06D38186EF39FD0D1AA3D3E176981E06155D20184B467381926CD0585E26A7E96F1001547481683816ACE60241CB7250DA6D7E83E67550D95E76D3EB61761AF486EBD36F771A14A6D3D3F632A80C12BFDDF539485E9EA7C9F534688C4E87DB61100D968BD95C", - STR_ENCODING_GSM7_HEX_PAD_0, "INFO SMS 23/03, 18:10: Costo chiamata E. 0,24. Il credito è E. 48,05. Per info su eventuali opzioni attive e bonus residui chiama 40916." } }, @@ -300,10 +295,11 @@ void test_parse_cmgr() unsigned idx = 0; char * input; struct result result; - char oa[200]; + char oa[200], sca[200]; const char * msg; result.oa = oa; + result.sca = sca; for (; idx < ITEMS_OF(cases); ++idx) { char buf[4096]; int failidx = 0; @@ -312,33 +308,11 @@ void test_parse_cmgr() fprintf(stderr, "/* %u */ %s(\"%s\")...", idx, "at_parse_cmgr", input); result.res = at_parse_cmgr( - &result.str, strlen(result.str), result.oa, sizeof(oa), &result.oa_enc, - &result.msg, &result.msg_enc, &result.udh); - - /* convert to utf8 representation */ - if (!result.res && result.oa_enc != STR_ENCODING_UNKNOWN) { - char tmp_oa[200]; - if (str_decode(result.oa_enc, - result.oa, strlen(result.oa), - tmp_oa, sizeof(tmp_oa), 0, 0) >= 0) { - strcpy(result.oa, tmp_oa); - } - } - result.msg_utf8 = NULL; - if (!result.res && result.msg_enc != STR_ENCODING_UNKNOWN) { - if (str_decode(result.msg_enc, - result.str, strlen(result.str), - buf, sizeof(buf), 0, 0) >= 0) { - result.msg_utf8 = buf; - } - } + result.str, strlen(result.str), &result.tpdu_type, &result.sca, sizeof(sca), result.oa, sizeof(oa), result.scts, &result.mr, &result.st, result.dt, + result.msg_utf8, &result.msg_len, &result.udh); - if (++failidx && safe_strcmp(result.res, cases[idx].result.res) == 0 && - ++failidx && safe_strcmp(result.str, cases[idx].result.str) == 0 && + if (++failidx && result.res == cases[idx].result.res && ++failidx && safe_strcmp(result.oa, cases[idx].result.oa) == 0 && - ++failidx && result.oa_enc == cases[idx].result.oa_enc && - ++failidx && safe_strcmp(result.msg, cases[idx].result.msg) == 0 && - ++failidx && result.msg_enc == cases[idx].result.msg_enc && ++failidx && safe_strcmp(result.msg_utf8, cases[idx].result.msg_utf8) == 0) { msg = "OK"; @@ -348,9 +322,9 @@ void test_parse_cmgr() msg = "FAIL"; faults++; } - fprintf(stderr, " = '%s' ('%s','%s',%d,'%s',%d) [fail@%d]\n[text=%s]\t%s\n", - result.res, result.str, result.oa, result.oa_enc, - result.msg, result.msg_enc, failidx, result.msg_utf8, msg); + fprintf(stderr, " = '%s' ('%s','%s') [fail@%d]\n[text=%s] %s\n", + result.res, result.oa, + result.msg, failidx, result.msg_utf8, msg); free(input); } fprintf(stderr, "\n"); @@ -502,6 +476,7 @@ int main() test_parse_csca(); test_parse_clcc(); test_parse_ccwa(); + test_gsm7(); fprintf(stderr, "done %d tests: %d OK %d FAILS\n", ok + faults, ok, faults);