From d13f113721f94b8aeb69a381b2a24ffa34cbf3da Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Sat, 5 Sep 2015 16:58:20 +0200 Subject: [PATCH 001/117] Parse a +CMGS notification and hack it to send an event with reference. Author: me, some time in the past. Source: /~dexter/work/asterisk-chan-dongle/patch.patch.txt --- at_command.c | 2 +- at_parse.c | 21 +++++++++++++++++++++ at_parse.h | 1 + at_response.c | 26 +++++++++++++++++++++++--- pdu.c | 27 ++++++++++++++++++++++++++- 5 files changed, 72 insertions(+), 5 deletions(-) diff --git a/at_command.c b/at_command.c index c2515eee..8c2a5a7f 100644 --- a/at_command.c +++ b/at_command.c @@ -133,7 +133,7 @@ EXPORT_DEF int at_enque_initialization(struct cpvt* cpvt, at_cmd_t from_command) static const char cmd19[] = "AT+CSSN=1,1\r"; static const char cmd21[] = "AT+CSCS=\"UCS2\"\r"; - static const char cmd22[] = "AT+CPMS=\"ME\",\"ME\",\"ME\"\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 cmd24[] = "AT+CSQ\r"; diff --git a/at_parse.c b/at_parse.c index e407e7fe..27ef6d74 100644 --- a/at_parse.c +++ b/at_parse.c @@ -385,6 +385,27 @@ EXPORT_DEF const char * at_parse_cmgr(char ** str, size_t len, char * oa, size_t return rv; } +/*! + * \brief Parse a +CMGS notification + * \param str -- string to parse (null terminated) + * \return -1 on error (parse error) or the first integer value found + * \todo FIXME: parse [,] value correctly + */ + +EXPORT_DEF int at_parse_cmgs (const char* str) +{ + int cmgs = -1; + + /* + * parse CMGS info in the following format: + * +CMGS:[,] + * (sscanf is lax about extra spaces) + * TODO: not ignore parse errors ;) + */ + sscanf (str, "+CMGS:%d", &cmgs); + return cmgs; +} + /*! * \brief Parse a CUSD answer * \param str -- string to parse (null terminated) diff --git a/at_parse.h b/at_parse.h index 29a28b82..b2e2ed51 100644 --- a/at_parse.h +++ b/at_parse.h @@ -15,6 +15,7 @@ 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); +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); EXPORT_DECL int at_parse_csq (const char* str, int* rssi); diff --git a/at_response.c b/at_response.c index 15c55316..e66be460 100644 --- a/at_response.c +++ b/at_response.c @@ -1198,8 +1198,16 @@ static int at_response_cmgr (struct pvt* pvt, const char * str, size_t len) 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); - if (err) + err = at_parse_cmgr (&err_pos, len, oa, sizeof(oa), &oa_enc, &msg, &msg_enc); // 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); + return 0; + } + else if (err) { ast_log (LOG_WARNING, "[%s] Error parsing incoming message '%s' at possition %d: %s\n", PVT_ID(pvt), str, (int)(err_pos - cmgr), err); return 0; @@ -1722,11 +1730,23 @@ int at_response (struct pvt* pvt, const struct iovec iov[2], int iovcnt, at_res_ case RES_CSSU: case RES_SRVST: case RES_CVOICE: - case RES_CMGS: case RES_CPMS: case RES_CONF: 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); + } + return 0; + case RES_OK: at_response_ok (pvt, at_res); return 0; diff --git a/pdu.c b/pdu.c index e3dae5b4..5f87db3f 100644 --- a/pdu.c +++ b/pdu.c @@ -797,10 +797,35 @@ EXPORT_DEF const char * pdu_parse(char ** pdu, size_t tpdu_length, char * oa, si err = "Can't parse length of OA"; } } + else if(PDUTYPE_MTI(pdu_type) == PDUTYPE_MTI_SMS_STATUS_REPORT) + { + 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) { + err = (void*)0x1; /* HACK! */ + *msg = (char*)(ssize_t)reference; /* HACK! */ + } else { + err = "Good report, but delivery failed"; + } + } else { + err = "FIXME error 1"; + } + } else { + err = "FIXME error 2"; + } + } else { *pdu -= 2; - err = "Unhandled PDU Type MTI only SMS-DELIVER supported"; + err = "Unhandled PDU Type MTI only SMS-DELIVER/SMS-STATUS-REPORT supported"; } } else From ea62b24513ddcbc5cf7184131d6dc438b96b66a7 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Sat, 5 Sep 2015 16:59:39 +0200 Subject: [PATCH 002/117] Add Asterisk 10 support. Author: me, some time in the past. Source: /~dexter/work/asterisk-chan-dongle/patch2.patch.txt --- channel.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/channel.c b/channel.c index 0c96fa46..cfd926ad 100644 --- a/channel.c +++ b/channel.c @@ -95,18 +95,23 @@ EXPORT_DEF int channels_loop(struct pvt * pvt, const struct ast_channel * reques return 0; } -#if ASTERISK_VERSION_NUM >= 10800 +#if ASTERISK_VERSION_NUM >= 100000 /* 10+ */ + +static struct ast_channel * channel_request (attribute_unused const char * type, struct ast_format_cap * cap, const struct ast_channel *requestor, void * data, int * cause) + +#elif ASTERISK_VERSION_NUM >= 10800 /* 1.8+ */ // TODO: simplify by move common code to functions static struct ast_channel * channel_request (attribute_unused const char * type, format_t format, const struct ast_channel * requestor, void * data, int * cause) -#else /* #if ASTERISK_VERSION_NUM >= 10800 */ +#else /* ASTERISK_VERSION_NUM < 10800 */ /* TODO: add check when request 'holdother' what requestor is not on same device for 1.6 */ static struct ast_channel * channel_request (attribute_unused const char * type, int format, void * data, int * cause) -#endif /* #if ASTERISK_VERSION_NUM >= 10800 */ +#endif /* ASTERISK_VERSION_NUM */ { -#if ASTERISK_VERSION_NUM >= 10800 +#if ASTERISK_VERSION_NUM >= 100000 /* 10+ */ +#elif ASTERISK_VERSION_NUM >= 10800 /* 1.8+ */ format_t oldformat; #else int oldformat; @@ -126,11 +131,18 @@ static struct ast_channel * channel_request (attribute_unused const char * type, return NULL; } +#if ASTERISK_VERSION_NUM >= 100000 /* 10+ */ + if (!(ast_format_cap_has_type(cap, AST_FORMAT_SLINEAR))) +#else oldformat = format; format &= AST_FORMAT_SLINEAR; if (!format) +#endif { -#if ASTERISK_VERSION_NUM >= 10800 +#if ASTERISK_VERSION_NUM >= 100000 /* 10+ */ + char buf[255]; + ast_log (LOG_WARNING, "Asked to get a channel of unsupported format '%s'\n", ast_getformatname_multiple (buf, 255, cap)); +#elif ASTERISK_VERSION_NUM >= 10800 /* 1.8+ */ ast_log (LOG_WARNING, "Asked to get a channel of unsupported format '%s'\n", ast_getformatname (oldformat)); #else ast_log (LOG_WARNING, "Asked to get a channel of unsupported format '%d'\n", oldformat); From aebd07348672cfd8f620a57aaa61e83d2e6e04dd Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Sat, 5 Sep 2015 17:03:52 +0200 Subject: [PATCH 003/117] Add more Asterisk 10 support. Author: me, some time in the past. Source: /~dexter/work/asterisk-chan-dongle/patch3.patch.txt --- chan_dongle.c | 19 +++++++++++++++++++ chan_dongle.h | 3 +++ channel.c | 45 +++++++++++++++++++++++++++++++-------------- channel.h | 2 +- 4 files changed, 54 insertions(+), 15 deletions(-) diff --git a/chan_dongle.c b/chan_dongle.c index e6e99baf..48dc1d36 100644 --- a/chan_dongle.c +++ b/chan_dongle.c @@ -39,6 +39,7 @@ #include ASTERISK_FILE_VERSION(__FILE__, "$Rev: " PACKAGE_REVISION " $") +#include #include /* AST_DECLARE_STRING_FIELDS for asterisk/manager.h */ #include #include @@ -67,6 +68,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Rev: " PACKAGE_REVISION " $") EXPORT_DEF const char * const dev_state_strs[4] = { "stop", "restart", "remove", "start" }; EXPORT_DEF public_state_t * gpublic; +EXPORT_DEF struct ast_format chan_dongle_format; +EXPORT_DEF struct ast_format_cap * chan_dongle_format_cap; static int public_state_init(struct public_state * state); @@ -1659,6 +1662,16 @@ static int public_state_init(struct public_state * state) rv = AST_MODULE_LOAD_FAILURE; if(discovery_restart(state) == 0) { +#if ASTERISK_VERSION_NUM >= 100000 /* 10+ */ + /* set preferred capabilities */ + ast_format_set(&chan_dongle_format, AST_FORMAT_SLINEAR, 0); + if (!(channel_tech.capabilities = ast_format_cap_alloc())) { + return AST_MODULE_LOAD_FAILURE; + } + ast_format_cap_add(channel_tech.capabilities, &chan_dongle_format); + chan_dongle_format_cap = channel_tech.capabilities; +#endif + /* register our channel type */ if(ast_channel_register(&channel_tech) == 0) { @@ -1671,6 +1684,9 @@ static int public_state_init(struct public_state * state) } else { +#if ASTERISK_VERSION_NUM >= 100000 /* 10+ */ + channel_tech.capabilities = ast_format_cap_destroy(channel_tech.capabilities); +#endif ast_log (LOG_ERROR, "Unable to register channel class %s\n", channel_tech.type); } discovery_stop(state); @@ -1698,6 +1714,9 @@ static void public_state_fini(struct public_state * state) { /* First, take us out of the channel loop */ ast_channel_unregister (&channel_tech); +#if ASTERISK_VERSION_NUM >= 100000 /* 10+ */ + channel_tech.capabilities = ast_format_cap_destroy(channel_tech.capabilities); +#endif /* Unregister the CLI & APP & MANAGER */ diff --git a/chan_dongle.h b/chan_dongle.h index 0123e048..4783e5e8 100644 --- a/chan_dongle.h +++ b/chan_dongle.h @@ -36,6 +36,9 @@ INLINE_DECL const char * dev_state2str_msg(dev_state_t state) return enum2str(state, states, ITEMS_OF(states)); } +/* Only linear is allowed */ +EXPORT_DECL struct ast_format chan_dongle_format; +EXPORT_DECL struct ast_format_cap * chan_dongle_format_cap; typedef enum { RESTATE_TIME_NOW = 0, diff --git a/channel.c b/channel.c index cfd926ad..ea759315 100644 --- a/channel.c +++ b/channel.c @@ -103,17 +103,16 @@ static struct ast_channel * channel_request (attribute_unused const char * type, // TODO: simplify by move common code to functions static struct ast_channel * channel_request (attribute_unused const char * type, format_t format, const struct ast_channel * requestor, void * data, int * cause) -#else /* ASTERISK_VERSION_NUM < 10800 */ +#else /* 1.8- */ /* TODO: add check when request 'holdother' what requestor is not on same device for 1.6 */ static struct ast_channel * channel_request (attribute_unused const char * type, int format, void * data, int * cause) -#endif /* ASTERISK_VERSION_NUM */ +#endif { -#if ASTERISK_VERSION_NUM >= 100000 /* 10+ */ -#elif ASTERISK_VERSION_NUM >= 10800 /* 1.8+ */ +#if ASTERISK_VERSION_NUM >= 10800 && ASTERISK_VERSION_NUM < 100000 /* 1.8+ .. 10- */ format_t oldformat; -#else +#elif ASTERISK_VERSION_NUM < 10800 /* 1.8- */ int oldformat; const struct ast_channel * requestor = NULL; #endif @@ -132,7 +131,7 @@ static struct ast_channel * channel_request (attribute_unused const char * type, } #if ASTERISK_VERSION_NUM >= 100000 /* 10+ */ - if (!(ast_format_cap_has_type(cap, AST_FORMAT_SLINEAR))) + if (!ast_format_cap_iscompatible(cap, &chan_dongle_format)) #else oldformat = format; format &= AST_FORMAT_SLINEAR; @@ -583,10 +582,12 @@ static void write_conference(struct pvt * pvt, const char * buffer, size_t lengt } -#if ASTERISK_VERSION_NUM >= 10800 +#if ASTERISK_VERSION_NUM >= 100000 /* 10+ */ +#define subclass_integer subclass.integer +#elif ASTERISK_VERSION_NUM >= 10800 /* 1.8+ */ #define subclass_codec subclass.codec #define subclass_integer subclass.integer -#else +#else /* 1.8- */ #define subclass_codec subclass #define subclass_integer subclass #endif @@ -630,7 +631,11 @@ static struct ast_frame* channel_read (struct ast_channel* channel) memset (&cpvt->a_read_frame, 0, sizeof (cpvt->a_read_frame)); cpvt->a_read_frame.frametype = AST_FRAME_VOICE; - cpvt->a_read_frame.subclass_codec= AST_FORMAT_SLINEAR; +#if ASTERISK_VERSION_NUM >= 100000 /* 10+ */ + ast_format_copy(&cpvt->a_read_frame.subclass.format, &chan_dongle_format); +#else + cpvt->a_read_frame.subclass_codec = AST_FORMAT_SLINEAR; +#endif cpvt->a_read_frame.data.ptr = cpvt->a_read_buf + AST_FRIENDLY_OFFSET; cpvt->a_read_frame.offset = AST_FRIENDLY_OFFSET; cpvt->a_read_frame.src = AST_MODULE; @@ -744,7 +749,11 @@ static int channel_write (struct ast_channel* channel, struct ast_frame* f) size_t count; int gains[2]; +#if ASTERISK_VERSION_NUM >= 100000 /* 10+ */ + if (f->frametype != AST_FRAME_VOICE || f->subclass.format.id != AST_FORMAT_SLINEAR) +#else /* 10- */ if (f->frametype != AST_FRAME_VOICE || f->subclass_codec != AST_FORMAT_SLINEAR) +#endif { return 0; } @@ -1158,9 +1167,15 @@ EXPORT_DEF struct ast_channel* new_channel (struct pvt* pvt, int ast_state, cons channel->tech_pvt = cpvt; channel->tech = &channel_tech; +#if ASTERISK_VERSION_NUM >= 100000 /* 10+ */ + ast_format_cap_add(channel->nativeformats, &chan_dongle_format); + ast_format_copy(&channel->writeformat, &chan_dongle_format); + ast_format_copy(&channel->readformat, &chan_dongle_format); +#else /* 10- */ channel->nativeformats = AST_FORMAT_SLINEAR; channel->writeformat = AST_FORMAT_SLINEAR; channel->readformat = AST_FORMAT_SLINEAR; +#endif if (ast_state == AST_STATE_RING) { @@ -1248,9 +1263,11 @@ EXPORT_DEF void start_local_channel (struct pvt* pvt, const char* exten, const c snprintf (channel_name, sizeof (channel_name), "%s@%s", exten, CONF_SHARED(pvt, context)); -#if ASTERISK_VERSION_NUM >= 10800 +#if ASTERISK_VERSION_NUM >= 100000 /* 10+ */ + channel = ast_request ("Local", chan_dongle_format_cap, NULL, channel_name, &cause); +#elif ASTERISK_VERSION_NUM >= 10800 /* 1.8+ */ channel = ast_request ("Local", AST_FORMAT_AUDIO_MASK, NULL, channel_name, &cause); -#else +#else /* 1.8- */ channel = ast_request ("Local", AST_FORMAT_AUDIO_MASK, channel_name, &cause); #endif if (channel) @@ -1335,7 +1352,6 @@ static int channel_func_read(struct ast_channel* channel, attribute_unused const static int channel_func_write(struct ast_channel* channel, const char* function, char* data, const char* value) { struct cpvt* cpvt = channel->tech_pvt; - struct pvt* pvt; call_state_t newstate, oldstate; int ret = 0; @@ -1344,7 +1360,6 @@ static int channel_func_write(struct ast_channel* channel, const char* function, ast_log (LOG_WARNING, "call on unreferenced %s\n", channel->name); return -1; } - pvt = cpvt->pvt; if (!strcasecmp(data, "callstate")) { @@ -1411,11 +1426,13 @@ static int channel_func_write(struct ast_channel* channel, const char* function, return ret; } -EXPORT_DEF const struct ast_channel_tech channel_tech = +EXPORT_DEF struct ast_channel_tech channel_tech = { .type = "Dongle", .description = MODULE_DESCRIPTION, +#if ASTERISK_VERSION_NUM < 100000 /* 10- */ .capabilities = AST_FORMAT_SLINEAR, +#endif .requester = channel_request, .call = channel_call, .hangup = channel_hangup, diff --git a/channel.h b/channel.h index d4ab5be4..e62bc9d8 100644 --- a/channel.h +++ b/channel.h @@ -19,7 +19,7 @@ typedef struct channel_var struct pvt; struct cpvt; -EXPORT_DECL const struct ast_channel_tech channel_tech; +EXPORT_DECL struct ast_channel_tech channel_tech; EXPORT_DECL struct ast_channel* new_channel (struct pvt * pvt, int ast_state, const char * cid_num, int call_idx, unsigned dir, unsigned state, const char * exten, const struct ast_channel * requestor); EXPORT_DECL int queue_control_channel (struct cpvt * cpvt, enum ast_control_frame_type control); From 9108a966aa146487815f639f653cd0401e2849c5 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Sat, 5 Sep 2015 17:21:25 +0200 Subject: [PATCH 004/117] Add Asterisk 11 support. Author: me, in August 2013. --- app.c | 3 +- ast_compat.h | 56 +++++++++++++++++++ at_response.c | 9 +-- chan_dongle.c | 5 +- chan_dongle.h | 4 ++ channel.c | 148 +++++++++++++++++++++++++++++--------------------- cli.c | 6 +- manager.c | 4 ++ 8 files changed, 166 insertions(+), 69 deletions(-) create mode 100644 ast_compat.h diff --git a/app.c b/app.c index c0965ff1..a46fc8d5 100644 --- a/app.c +++ b/app.c @@ -18,7 +18,8 @@ #include /* AST_DECLARE_APP_ARGS() ... */ #include /* pbx_builtin_setvar_helper() */ #include /* ast_register_application2() ast_unregister_application() */ -#include /* ASTERISK_VERSION_NUM */ + +#include "ast_compat.h" /* asterisk compatibility fixes */ #include "app.h" /* app_register() app_unregister() */ #include "chan_dongle.h" /* struct pvt */ diff --git a/ast_compat.h b/ast_compat.h new file mode 100644 index 00000000..c22b75e3 --- /dev/null +++ b/ast_compat.h @@ -0,0 +1,56 @@ +#ifndef CHAN_DONGLE_AST_COMPAT_H_INCLUDED +#define CHAN_DONGLE_AST_COMPAT_H_INCLUDED + +#ifndef ASTERISK_VERSION_NUM +#error ASTERISK_VERSION_NUM is not set. Please re-run configure, \ + prefixed like this: CPPFLAGS=-DASTERISK_VERSION_NUM=100501 \ + ./configure (where 100501 is asterisk version 10.5.1). +#endif + +#include + +/* Asterisk 11+ channel opaqification */ +#if ASTERISK_VERSION_NUM < 110000 + +/* channel->name */ +static inline const char *ast_channel_name(const struct ast_channel *chan) { return chan->name; } + +/* channel->_state / channel->state */ +static inline enum ast_channel_state ast_channel_state(const struct ast_channel *chan) { return chan->_state; } + +/* channel->fdno */ +static inline int ast_channel_fdno(const struct ast_channel *chan) { return chan->fdno; } + +/* channel->tech */ +static inline const struct ast_channel_tech *ast_channel_tech(const struct ast_channel *chan) { return chan->tech; } +static inline void ast_channel_tech_set(struct ast_channel *chan, const struct ast_channel_tech *value) { chan->tech = value; } + +static inline void *ast_channel_tech_pvt(const struct ast_channel *chan) { return chan->tech_pvt; } +static inline void ast_channel_tech_pvt_set(struct ast_channel *chan, void *value) { chan->tech_pvt = value; } + +/* channel->rings */ +static inline int ast_channel_rings(const struct ast_channel *chan) { return chan->rings; } +static inline void ast_channel_rings_set(struct ast_channel *chan, int value) { chan->rings = value; } + +/* ast_string_field_set(channel, language, ...) */ +static inline void ast_channel_language_set(struct ast_channel *chan, const char *value) { ast_string_field_set(chan, language, value); } + +/* channel->connected */ +static inline struct ast_party_connected_line *ast_channel_connected(struct ast_channel *chan) { return &chan->connected; } + +/* channel->linkedid */ +static inline const char *ast_channel_linkedid(const struct ast_channel *chan) { return chan->linkedid; } + +/* channel->hangupcause */ +static inline void ast_channel_hangupcause_set(struct ast_channel *chan, int value) { chan->hangupcause = value; } + +#if ASTERISK_VERSION_NUM >= 100000 +/* channel->*format* */ +static inline struct ast_format_cap *ast_channel_nativeformats(const struct ast_channel *chan) { return chan->nativeformats; } +static inline struct ast_format ast_channel_readformat(const struct ast_channel *chan) { return chan->readformat; } +static inline struct ast_format ast_channel_writeformat(const struct ast_channel *chan) { return chan->writeformat; } +#endif + +#endif + +#endif /* CHAN_DONGLE_AST_COMPAT_H_INCLUDED */ diff --git a/at_response.c b/at_response.c index e66be460..ad3e0565 100644 --- a/at_response.c +++ b/at_response.c @@ -16,6 +16,8 @@ #include /* ast_debug() */ #include /* ast_pbx_start() */ +#include "ast_compat.h" /* asterisk compatibility fixes */ + #include "at_response.h" #include "mutils.h" /* STRLEN() */ #include "at_queue.h" @@ -851,14 +853,14 @@ static int start_pbx(struct pvt* pvt, const char * number, int call_idx, call_st return -1; } - cpvt = channel->tech_pvt; + cpvt = ast_channel_tech_pvt(channel); // FIXME: not execute if channel_new() failed CPVT_SET_FLAGS(cpvt, CALL_FLAG_NEED_HANGUP); // ast_pbx_start() usually failed if asterisk.conf minmemfree set too low, try drop buffer cache sync && echo 3 > /proc/sys/vm/drop_caches if (ast_pbx_start (channel)) { - channel->tech_pvt = NULL; + ast_channel_tech_pvt_set(channel, NULL); cpvt_free(cpvt); ast_hangup (channel); @@ -920,8 +922,7 @@ static int at_response_clcc (struct pvt* pvt, char* str) if(cpvt->channel) { /* FIXME: unprotected channel access */ - cpvt->channel->rings += pvt->rings; - pvt->rings = 0; + ast_channel_rings_set(cpvt->channel, ast_channel_rings(cpvt->channel) + pvt->rings); } } if(state != cpvt->state) diff --git a/chan_dongle.c b/chan_dongle.c index 48dc1d36..dba5a49d 100644 --- a/chan_dongle.c +++ b/chan_dongle.c @@ -39,7 +39,6 @@ #include ASTERISK_FILE_VERSION(__FILE__, "$Rev: " PACKAGE_REVISION " $") -#include #include /* AST_DECLARE_STRING_FIELDS for asterisk/manager.h */ #include #include @@ -53,6 +52,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Rev: " PACKAGE_REVISION " $") #include /* O_RDWR O_NOCTTY */ #include /* SIGURG */ +#include "ast_compat.h" /* asterisk compatibility fixes */ + #include "chan_dongle.h" #include "at_response.h" /* at_res_t */ #include "at_queue.h" /* struct at_queue_task_cmd at_queue_head_cmd() */ @@ -68,8 +69,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Rev: " PACKAGE_REVISION " $") EXPORT_DEF const char * const dev_state_strs[4] = { "stop", "restart", "remove", "start" }; EXPORT_DEF public_state_t * gpublic; +#if ASTERISK_VERSION_NUM >= 100000 /* 10+ */ EXPORT_DEF struct ast_format chan_dongle_format; EXPORT_DEF struct ast_format_cap * chan_dongle_format_cap; +#endif static int public_state_init(struct public_state * state); diff --git a/chan_dongle.h b/chan_dongle.h index 4783e5e8..b3adab78 100644 --- a/chan_dongle.h +++ b/chan_dongle.h @@ -16,6 +16,8 @@ #include #include +#include + #include "mixbuffer.h" /* struct mixbuffer */ //#include "ringbuffer.h" /* struct ringbuffer */ #include "cpvt.h" /* struct cpvt */ @@ -36,9 +38,11 @@ INLINE_DECL const char * dev_state2str_msg(dev_state_t state) return enum2str(state, states, ITEMS_OF(states)); } +#if ASTERISK_VERSION_NUM >= 100000 /* 10+ */ /* Only linear is allowed */ EXPORT_DECL struct ast_format chan_dongle_format; EXPORT_DECL struct ast_format_cap * chan_dongle_format_cap; +#endif typedef enum { RESTATE_TIME_NOW = 0, diff --git a/channel.c b/channel.c index ea759315..73e40336 100644 --- a/channel.c +++ b/channel.c @@ -12,6 +12,10 @@ #include #endif /* HAVE_CONFIG_H */ +#ifndef ASTERISK_VERSION_NUM +#error ASTERISK_VERSION_NUM is not set, please supply -D ASTERISK_VERSION_NUM=100501 for version 10.5.1 +#endif + #include #include /* ast_dsp_digitreset() */ #include /* pbx_builtin_setvar_helper() */ @@ -20,7 +24,8 @@ #include /* ast_moh_start() ast_moh_stop() */ #include /* AST_MUTEX_DEFINE_STATIC */ #include /* ast_timer_fd() ast_timer_set_rate() ast_timer_ack() */ -#include /* ASTERISK_VERSION_NUM */ + +#include "ast_compat.h" #include "channel.h" #include "chan_dongle.h" @@ -29,6 +34,7 @@ #include "at_queue.h" /* write_all() TODO: move out */ #include "manager.h" /* manager_event_call_state_change() */ + static char silence_frame[FRAME_SIZE]; #/* */ @@ -90,25 +96,23 @@ EXPORT_DEF int channels_loop(struct pvt * pvt, const struct ast_channel * reques /* FIXME: requestor may be just proxy/masquerade for real channel */ // use ast_bridged_channel(chan) ? // use requestor->tech->get_base_channel() ? - if(requestor && requestor->tech == &channel_tech && requestor->tech_pvt && ((struct cpvt*)requestor->tech_pvt)->pvt == pvt) + struct cpvt *cpvt = ast_channel_tech_pvt(requestor); + if(requestor && ast_channel_tech(requestor) == &channel_tech && cpvt && cpvt->pvt == pvt) return 1; return 0; } -#if ASTERISK_VERSION_NUM >= 100000 /* 10+ */ - +// TODO: simplify by move common code to functions +/* TODO: add check when request 'holdother' what requestor is not on same device for 1.6 */ +#if ASTERISK_VERSION_NUM >= 110000 /* 11+ */ +static struct ast_channel * channel_request (attribute_unused const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, const char *data, int *cause) +#elif ASTERISK_VERSION_NUM >= 100000 /* 10+ */ static struct ast_channel * channel_request (attribute_unused const char * type, struct ast_format_cap * cap, const struct ast_channel *requestor, void * data, int * cause) - #elif ASTERISK_VERSION_NUM >= 10800 /* 1.8+ */ -// TODO: simplify by move common code to functions static struct ast_channel * channel_request (attribute_unused const char * type, format_t format, const struct ast_channel * requestor, void * data, int * cause) - -#else /* 1.8- */ -/* TODO: add check when request 'holdother' what requestor is not on same device for 1.6 */ - +#else /* #if ASTERISK_VERSION_NUM < 10800 */ static struct ast_channel * channel_request (attribute_unused const char * type, int format, void * data, int * cause) - -#endif +#endif /* #if ASTERISK_VERSION_NUM < 10800 */ { #if ASTERISK_VERSION_NUM >= 10800 && ASTERISK_VERSION_NUM < 100000 /* 1.8+ .. 10- */ format_t oldformat; @@ -133,11 +137,11 @@ static struct ast_channel * channel_request (attribute_unused const char * type, #if ASTERISK_VERSION_NUM >= 100000 /* 10+ */ if (!ast_format_cap_iscompatible(cap, &chan_dongle_format)) #else - oldformat = format; - format &= AST_FORMAT_SLINEAR; - if (!format) + oldformat = format; + format &= AST_FORMAT_SLINEAR; + if (!format) #endif - { + { #if ASTERISK_VERSION_NUM >= 100000 /* 10+ */ char buf[255]; ast_log (LOG_WARNING, "Asked to get a channel of unsupported format '%s'\n", ast_getformatname_multiple (buf, 255, cap)); @@ -182,9 +186,13 @@ static struct ast_channel * channel_request (attribute_unused const char * type, } #/* */ +#if ASTERISK_VERSION_NUM >= 110000 /* 11+ */ +static int channel_call (struct ast_channel* channel, const char *dest, attribute_unused int timeout) +#else static int channel_call (struct ast_channel* channel, char* dest, attribute_unused int timeout) +#endif { - struct cpvt* cpvt = channel->tech_pvt; + struct cpvt* cpvt = ast_channel_tech_pvt(channel); struct pvt* pvt; char* dest_dev; const char* dest_num; @@ -193,7 +201,7 @@ static int channel_call (struct ast_channel* channel, char* dest, attribute_unus if(!cpvt || cpvt->channel != channel || !cpvt->pvt) { - ast_log (LOG_WARNING, "call on unreferenced %s\n", channel->name); + ast_log (LOG_WARNING, "call on unreferenced %s\n", ast_channel_name(channel)); return -1; } pvt = cpvt->pvt; @@ -203,9 +211,9 @@ static int channel_call (struct ast_channel* channel, char* dest, attribute_unus if(parse_dial_string(dest_dev, &dest_num, &opts)) return -1; - if ((channel->_state != AST_STATE_DOWN) && (channel->_state != AST_STATE_RESERVED)) + if ((ast_channel_state(channel) != AST_STATE_DOWN) && (ast_channel_state(channel) != AST_STATE_RESERVED)) { - ast_log (LOG_WARNING, "channel_call called on %s, neither down nor reserved\n", channel->name); + ast_log (LOG_WARNING, "channel_call called on %s, neither down nor reserved\n", ast_channel_name(channel)); return -1; } @@ -220,14 +228,14 @@ static int channel_call (struct ast_channel* channel, char* dest, attribute_unus } CPVT_SET_FLAGS(cpvt, opts); - ast_debug (1, "[%s] Calling %s on %s\n", PVT_ID(pvt), dest, channel->name); + ast_debug (1, "[%s] Calling %s on %s\n", PVT_ID(pvt), dest, ast_channel_name(channel)); if (CONF_SHARED(pvt, usecallingpres)) { if (CONF_SHARED(pvt, callingpres) < 0) { #if ASTERISK_VERSION_NUM >= 10800 - clir = channel->connected.id.number.presentation; + clir = ast_channel_connected(channel)->id.number.presentation; #else clir = channel->cid.cid_pres; #endif @@ -339,7 +347,7 @@ static void activate_call(struct cpvt* cpvt) #/* we has 2 case of call this function, when local side want terminate call and when called for cleanup after remote side alreay terminate call, CEND received and cpvt destroyed */ static int channel_hangup (struct ast_channel* channel) { - struct cpvt* cpvt = channel->tech_pvt; + struct cpvt* cpvt = ast_channel_tech_pvt(channel); struct pvt* pvt; /* its possible call with channel w/o tech_pvt */ @@ -368,7 +376,7 @@ static int channel_hangup (struct ast_channel* channel) } /* drop channel -> cpvt reference */ - channel->tech_pvt = NULL; + ast_channel_tech_pvt_set(channel, NULL); ast_module_unref (self_module()); ast_setstate (channel, AST_STATE_DOWN); @@ -379,12 +387,12 @@ static int channel_hangup (struct ast_channel* channel) #/* */ static int channel_answer (struct ast_channel* channel) { - struct cpvt* cpvt = channel->tech_pvt; + struct cpvt* cpvt = ast_channel_tech_pvt(channel); struct pvt* pvt; if(!cpvt || cpvt->channel != channel || !cpvt->pvt) { - ast_log (LOG_WARNING, "call on unreferenced %s\n", channel->name); + ast_log (LOG_WARNING, "call on unreferenced %s\n", ast_channel_name(channel)); return 0; } pvt = cpvt->pvt; @@ -408,13 +416,13 @@ static int channel_answer (struct ast_channel* channel) #/* */ static int channel_digit_begin (struct ast_channel* channel, char digit) { - struct cpvt* cpvt = channel->tech_pvt; + struct cpvt* cpvt = ast_channel_tech_pvt(channel); struct pvt* pvt; int rv; if(!cpvt || cpvt->channel != channel || !cpvt->pvt) { - ast_log (LOG_WARNING, "call on unreferenced %s\n", channel->name); + ast_log (LOG_WARNING, "call on unreferenced %s\n", ast_channel_name(channel)); return -1; } pvt = cpvt->pvt; @@ -582,20 +590,21 @@ static void write_conference(struct pvt * pvt, const char * buffer, size_t lengt } + #if ASTERISK_VERSION_NUM >= 100000 /* 10+ */ -#define subclass_integer subclass.integer +#define subclass_integer subclass.integer #elif ASTERISK_VERSION_NUM >= 10800 /* 1.8+ */ -#define subclass_codec subclass.codec -#define subclass_integer subclass.integer +#define subclass_codec subclass.codec +#define subclass_integer subclass.integer #else /* 1.8- */ -#define subclass_codec subclass -#define subclass_integer subclass +#define subclass_codec subclass +#define subclass_integer subclass #endif #/* */ static struct ast_frame* channel_read (struct ast_channel* channel) { - struct cpvt* cpvt = channel->tech_pvt; + struct cpvt* cpvt = ast_channel_tech_pvt(channel); struct pvt* pvt; struct ast_frame* f = &ast_null_frame; ssize_t res; @@ -619,7 +628,7 @@ static struct ast_frame* channel_read (struct ast_channel* channel) goto e_return; } - if (pvt->a_timer && channel->fdno == 1) + if (pvt->a_timer && ast_channel_fdno(channel) == 1) { ast_timer_ack (pvt->a_timer, 1); timing_write (pvt); @@ -744,7 +753,7 @@ static struct ast_frame* channel_read (struct ast_channel* channel) #/* */ static int channel_write (struct ast_channel* channel, struct ast_frame* f) { - struct cpvt* cpvt = channel->tech_pvt; + struct cpvt* cpvt = ast_channel_tech_pvt(channel); struct pvt* pvt; size_t count; int gains[2]; @@ -760,7 +769,7 @@ static int channel_write (struct ast_channel* channel, struct ast_frame* f) if(!cpvt || cpvt->channel != channel || !cpvt->pvt) { - ast_log (LOG_WARNING, "call on unreferenced %s\n", channel->name); + ast_log (LOG_WARNING, "call on unreferenced %s\n", ast_channel_name(channel)); return 0; } @@ -785,14 +794,16 @@ static int channel_write (struct ast_channel* channel, struct ast_frame* f) if(CPVT_TEST_FLAG(cpvt, CALL_FLAG_MULTIPARTY) && !CPVT_TEST_FLAG(cpvt, CALL_FLAG_BRIDGE_CHECK)) { struct ast_channel* bridged = ast_bridged_channel(channel); + struct cpvt *tmp_cpvt; CPVT_SET_FLAGS(cpvt, CALL_FLAG_BRIDGE_CHECK); - if(bridged && bridged->tech == &channel_tech && bridged->tech_pvt && ((struct cpvt*)bridged->tech_pvt)->pvt == pvt) + tmp_cpvt = ast_channel_tech_pvt(bridged); + if(bridged && ast_channel_tech(bridged) == &channel_tech && tmp_cpvt && tmp_cpvt->pvt == pvt) { CPVT_SET_FLAGS(cpvt, CALL_FLAG_BRIDGE_LOOP); - CPVT_SET_FLAGS((struct cpvt*)bridged->tech_pvt, CALL_FLAG_BRIDGE_LOOP); - ast_log (LOG_WARNING, "[%s] Bridged channels %s and %s working on same device, discard writes to avoid voice loop\n", PVT_ID(pvt), channel->name, bridged->name); + CPVT_SET_FLAGS((struct cpvt*)ast_channel_tech_pvt(bridged), CALL_FLAG_BRIDGE_LOOP); + ast_log(LOG_WARNING, "[%s] Bridged channels %s and %s working on same device, discard writes to avoid voice loop\n", PVT_ID(pvt), ast_channel_name(channel), ast_channel_name(bridged)); goto e_return; } } @@ -915,12 +926,12 @@ static int channel_write (struct ast_channel* channel, struct ast_frame* f) #/* */ static int channel_fixup (struct ast_channel* oldchannel, struct ast_channel* newchannel) { - struct cpvt * cpvt = newchannel->tech_pvt; + struct cpvt * cpvt = ast_channel_tech_pvt(newchannel); struct pvt* pvt; if (!cpvt || !cpvt->pvt) { - ast_log (LOG_WARNING, "call on unreferenced %s\n", newchannel->name); + ast_log (LOG_WARNING, "call on unreferenced %s\n", ast_channel_name(newchannel)); return -1; } pvt = cpvt->pvt; @@ -936,7 +947,11 @@ static int channel_fixup (struct ast_channel* oldchannel, struct ast_channel* ne } #/* FIXME: must modify in conjuction with state on call not whole device? */ +#if ASTERISK_VERSION_NUM >= 110000 /* 11+ */ +static int channel_devicestate (const char *data) +#else static int channel_devicestate (void* data) +#endif { char* device; struct pvt* pvt; @@ -971,7 +986,7 @@ static int channel_indicate (struct ast_channel* channel, int condition, const v { int res = 0; - ast_debug (1, "[%s] Requested indication %d\n", channel->name, condition); + ast_debug (1, "[%s] Requested indication %d\n", ast_channel_name(channel), condition); switch (condition) { @@ -1000,7 +1015,7 @@ static int channel_indicate (struct ast_channel* channel, int condition, const v break; default: - ast_log (LOG_WARNING, "[%s] Don't know how to indicate condition %d\n", channel->name, condition); + ast_log (LOG_WARNING, "[%s] Don't know how to indicate condition %d\n", ast_channel_name(channel), condition); res = -1; break; } @@ -1112,7 +1127,7 @@ EXPORT_DEF void change_channel_state(struct cpvt * cpvt, unsigned newstate, int /* drop channel -> cpvt reference */ - channel->tech_pvt = NULL; + ast_channel_tech_pvt_set(channel, NULL); cpvt_free(cpvt); if (queue_hangup (channel, cause)) { @@ -1139,7 +1154,7 @@ static void set_channel_vars(struct pvt* pvt, struct ast_channel* channel) { "DONGLENUMBER", pvt->subscriber_number }, }; - ast_string_field_set (channel, language, CONF_SHARED(pvt, language)); + ast_channel_language_set(channel, CONF_SHARED(pvt, language)); for(idx = 0; idx < ITEMS_OF(dev_vars); ++idx) pbx_builtin_setvar_helper (channel, dev_vars[idx].name, dev_vars[idx].value); @@ -1156,7 +1171,7 @@ EXPORT_DEF struct ast_channel* new_channel (struct pvt* pvt, int ast_state, cons if (cpvt) { #if ASTERISK_VERSION_NUM >= 10800 - channel = ast_channel_alloc (1, ast_state, cid_num, PVT_ID(pvt), NULL, dnid, CONF_SHARED(pvt, context), requestor ? requestor->linkedid : NULL, 0, "%s/%s-%02u%08lx", channel_tech.type, PVT_ID(pvt), call_idx, pvt->channel_instanse); + channel = ast_channel_alloc (1, ast_state, cid_num, PVT_ID(pvt), NULL, dnid, CONF_SHARED(pvt, context), requestor ? ast_channel_linkedid(requestor) : NULL, 0, "%s/%s-%02u%08lx", channel_tech.type, PVT_ID(pvt), call_idx, pvt->channel_instanse); #else channel = ast_channel_alloc (1, ast_state, cid_num, PVT_ID(pvt), NULL, dnid, CONF_SHARED(pvt, context), 0, "%s/%s-%02u%08lx", channel_tech.type, PVT_ID(pvt), call_idx, pvt->channel_instanse); #endif @@ -1165,12 +1180,18 @@ EXPORT_DEF struct ast_channel* new_channel (struct pvt* pvt, int ast_state, cons cpvt->channel = channel; pvt->channel_instanse++; - channel->tech_pvt = cpvt; - channel->tech = &channel_tech; -#if ASTERISK_VERSION_NUM >= 100000 /* 10+ */ - ast_format_cap_add(channel->nativeformats, &chan_dongle_format); - ast_format_copy(&channel->writeformat, &chan_dongle_format); - ast_format_copy(&channel->readformat, &chan_dongle_format); + ast_channel_tech_pvt_set(channel, cpvt); + ast_channel_tech_set(channel, &channel_tech); + + +#if ASTERISK_VERSION_NUM >= 110000 /* 11+ */ + ast_format_set(ast_channel_readformat(channel), AST_FORMAT_SLINEAR, 0); + ast_format_set(ast_channel_writeformat(channel), AST_FORMAT_SLINEAR, 0); + ast_format_cap_set(ast_channel_nativeformats(channel), ast_channel_writeformat(channel)); +#elif ASTERISK_VERSION_NUM >= 100000 /* 10+ */ + ast_format_set(&channel->readformat, AST_FORMAT_SLINEAR, 0); + ast_format_set(&channel->writeformat, AST_FORMAT_SLINEAR, 0); + ast_format_cap_set(channel->nativeformats, &channel->writeformat); #else /* 10- */ channel->nativeformats = AST_FORMAT_SLINEAR; channel->writeformat = AST_FORMAT_SLINEAR; @@ -1179,7 +1200,7 @@ EXPORT_DEF struct ast_channel* new_channel (struct pvt* pvt, int ast_state, cons if (ast_state == AST_STATE_RING) { - channel->rings = 1; + ast_channel_rings_set(channel, 1); } set_channel_vars(pvt, channel); @@ -1246,8 +1267,9 @@ EXPORT_DEF int queue_hangup(struct ast_channel* channel, int hangupcause) int rv = 0; if(channel) { - if (hangupcause != 0) - channel->hangupcause = hangupcause; + if (hangupcause != 0) { + ast_channel_hangupcause_set(channel, hangupcause); + } rv = ast_queue_hangup (channel); } @@ -1267,7 +1289,7 @@ EXPORT_DEF void start_local_channel (struct pvt* pvt, const char* exten, const c channel = ast_request ("Local", chan_dongle_format_cap, NULL, channel_name, &cause); #elif ASTERISK_VERSION_NUM >= 10800 /* 1.8+ */ channel = ast_request ("Local", AST_FORMAT_AUDIO_MASK, NULL, channel_name, &cause); -#else /* 1.8- */ +#else channel = ast_request ("Local", AST_FORMAT_AUDIO_MASK, channel_name, &cause); #endif if (channel) @@ -1294,13 +1316,13 @@ EXPORT_DEF void start_local_channel (struct pvt* pvt, const char* exten, const c #/* */ static int channel_func_read(struct ast_channel* channel, attribute_unused const char* function, char* data, char* buf, size_t len) { - struct cpvt* cpvt = channel->tech_pvt; + struct cpvt* cpvt = ast_channel_tech_pvt(channel); struct pvt* pvt; int ret = 0; if(!cpvt || !cpvt->pvt) { - ast_log (LOG_WARNING, "call on unreferenced %s\n", channel->name); + ast_log (LOG_WARNING, "call on unreferenced %s\n", ast_channel_name(channel)); return -1; } pvt = cpvt->pvt; @@ -1351,15 +1373,17 @@ static int channel_func_read(struct ast_channel* channel, attribute_unused const #/* */ static int channel_func_write(struct ast_channel* channel, const char* function, char* data, const char* value) { - struct cpvt* cpvt = channel->tech_pvt; + struct cpvt* cpvt = ast_channel_tech_pvt(channel); + struct pvt* pvt; call_state_t newstate, oldstate; int ret = 0; if(!cpvt || !cpvt->pvt) { - ast_log (LOG_WARNING, "call on unreferenced %s\n", channel->name); + ast_log (LOG_WARNING, "call on unreferenced %s\n", ast_channel_name(channel)); return -1; } + pvt = cpvt->pvt; if (!strcasecmp(data, "callstate")) { @@ -1431,7 +1455,7 @@ EXPORT_DEF struct ast_channel_tech channel_tech = .type = "Dongle", .description = MODULE_DESCRIPTION, #if ASTERISK_VERSION_NUM < 100000 /* 10- */ - .capabilities = AST_FORMAT_SLINEAR, + .capabilities = AST_FORMAT_SLINEAR, #endif .requester = channel_request, .call = channel_call, diff --git a/cli.c b/cli.c index d0b1918e..4c255625 100644 --- a/cli.c +++ b/cli.c @@ -12,10 +12,14 @@ #include #endif /* HAVE_CONFIG_H */ +#ifndef ASTERISK_VERSION_NUM +#error ASTERISK_VERSION_NUM is not set, please supply \ + -D ASTERISK_VERSION_NUM=100501 for version 10.5.1 +#endif + #include #include /* struct ast_cli_entry; struct ast_cli_args */ #include /* ast_describe_caller_presentation() */ -#include /* ASTERISK_VERSION_NUM */ #include "cli.h" #include "chan_dongle.h" /* devices */ diff --git a/manager.c b/manager.c index ceda3a2c..4f9e9620 100644 --- a/manager.c +++ b/manager.c @@ -733,7 +733,11 @@ EXPORT_DEF void manager_register() unsigned i; for(i = 0; i < ITEMS_OF(dcm); i++) { +#if ASTERISK_VERSION_NUM >= 110000 + ast_manager_register2 (dcm[i].name, dcm[i].authority, dcm[i].func, NULL, dcm[i].brief, dcm[i].desc); +#else ast_manager_register2 (dcm[i].name, dcm[i].authority, dcm[i].func, dcm[i].brief, dcm[i].desc); +#endif } } From 46cc1dd054c533e48cdba68cbde2935e4365e4e4 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Sat, 5 Sep 2015 17:40:51 +0200 Subject: [PATCH 005/117] Add .gitignore. --- .gitignore | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..9170b951 --- /dev/null +++ b/.gitignore @@ -0,0 +1,20 @@ +*.swp + +*.o +*.o.d + +/chan_dongle.so + +/Makefile +/aclocal.m4 +/autom4te.cache +/compile +/config.guess +/config.h +/config.log +/config.status +/config.sub +/configure +/install-sh +/missing +/stamp-h1 From 50110f333c0f10d1237e4c6947f9dd11f476caf3 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Sat, 5 Sep 2015 22:53:17 +0200 Subject: [PATCH 006/117] Add Asterisk 13 support. Author: novaktmp, 2014-12-01 17:42:52 Source: https://forum.openwrt.org/viewtopic.php?id=54343 Slight adaptations by me. --- at_response.c | 13 +++++- chan_dongle.c | 33 ++++++++++++--- chan_dongle.h | 3 +- channel.c | 114 ++++++++++++++++++++++++++++++++++---------------- channel.h | 4 ++ manager.c | 9 +++- pdu.c | 2 +- 7 files changed, 133 insertions(+), 45 deletions(-) diff --git a/at_response.c b/at_response.c index ad3e0565..e8a35b6a 100644 --- a/at_response.c +++ b/at_response.c @@ -840,7 +840,11 @@ static int start_pbx(struct pvt* pvt, const char * number, int call_idx, call_st struct cpvt* cpvt; /* TODO: pass also Subscriber number or other DID info for exten */ +#if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ + struct ast_channel * channel = new_channel (pvt, AST_STATE_RING, number, call_idx, CALL_DIR_INCOMING, state, pvt->has_subscriber_number ? pvt->subscriber_number : CONF_SHARED(pvt, exten), NULL, NULL); +#else struct ast_channel * channel = new_channel (pvt, AST_STATE_RING, number, call_idx, CALL_DIR_INCOMING, state, pvt->has_subscriber_number ? pvt->subscriber_number : CONF_SHARED(pvt, exten), NULL); +#endif if (!channel) { @@ -853,11 +857,15 @@ static int start_pbx(struct pvt* pvt, const char * number, int call_idx, call_st return -1; } + cpvt = ast_channel_tech_pvt(channel); // FIXME: not execute if channel_new() failed CPVT_SET_FLAGS(cpvt, CALL_FLAG_NEED_HANGUP); // ast_pbx_start() usually failed if asterisk.conf minmemfree set too low, try drop buffer cache sync && echo 3 > /proc/sys/vm/drop_caches +#if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ + ast_channel_unlock(channel); +#endif if (ast_pbx_start (channel)) { ast_channel_tech_pvt_set(channel, NULL); @@ -922,7 +930,10 @@ static int at_response_clcc (struct pvt* pvt, char* str) if(cpvt->channel) { /* FIXME: unprotected channel access */ - ast_channel_rings_set(cpvt->channel, ast_channel_rings(cpvt->channel) + pvt->rings); + int rings = ast_channel_rings(cpvt->channel); + rings += pvt->rings; + ast_channel_rings_set(cpvt->channel, rings); + pvt->rings = 0; } } if(state != cpvt->state) diff --git a/chan_dongle.c b/chan_dongle.c index dba5a49d..84eef5ca 100644 --- a/chan_dongle.c +++ b/chan_dongle.c @@ -45,6 +45,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Rev: " PACKAGE_REVISION " $") #include #include /* AST_MODULE_LOAD_DECLINE ... */ #include /* ast_timer_open() ast_timer_fd() */ +#if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ +#include +#include +#endif #include /* S_IRUSR | S_IRGRP | S_IROTH */ #include /* struct termios tcgetattr() tcsetattr() */ @@ -69,12 +73,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Rev: " PACKAGE_REVISION " $") EXPORT_DEF const char * const dev_state_strs[4] = { "stop", "restart", "remove", "start" }; EXPORT_DEF public_state_t * gpublic; -#if ASTERISK_VERSION_NUM >= 100000 /* 10+ */ +#if ASTERISK_VERSION_NUM >= 100000 && ASTERISK_VERSION_NUM < 130000 /* 10+..13- */ EXPORT_DEF struct ast_format chan_dongle_format; EXPORT_DEF struct ast_format_cap * chan_dongle_format_cap; #endif - static int public_state_init(struct public_state * state); @@ -1665,8 +1668,15 @@ static int public_state_init(struct public_state * state) rv = AST_MODULE_LOAD_FAILURE; if(discovery_restart(state) == 0) { -#if ASTERISK_VERSION_NUM >= 100000 /* 10+ */ + /* set preferred capabilities */ +#if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ + if (!(channel_tech.capabilities = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) { + return AST_MODULE_LOAD_FAILURE; + } + ast_format_cap_append(channel_tech.capabilities, ast_format_slin, 0); + //chan_dongle_format_cap = channel_tech.capabilities; +#elif ASTERISK_VERSION_NUM >= 100000 /* 10-13 */ ast_format_set(&chan_dongle_format, AST_FORMAT_SLINEAR, 0); if (!(channel_tech.capabilities = ast_format_cap_alloc())) { return AST_MODULE_LOAD_FAILURE; @@ -1687,7 +1697,10 @@ static int public_state_init(struct public_state * state) } else { -#if ASTERISK_VERSION_NUM >= 100000 /* 10+ */ +#if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ + ao2_cleanup(channel_tech.capabilities); + channel_tech.capabilities = NULL; +#elif ASTERISK_VERSION_NUM >= 100000 /* 10-13 */ channel_tech.capabilities = ast_format_cap_destroy(channel_tech.capabilities); #endif ast_log (LOG_ERROR, "Unable to register channel class %s\n", channel_tech.type); @@ -1717,7 +1730,11 @@ static void public_state_fini(struct public_state * state) { /* First, take us out of the channel loop */ ast_channel_unregister (&channel_tech); -#if ASTERISK_VERSION_NUM >= 100000 /* 10+ */ +#if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ + ao2_cleanup(channel_tech.capabilities); + channel_tech.capabilities = NULL; + +#elif ASTERISK_VERSION_NUM >= 100000 /* 10-13 */ channel_tech.capabilities = ast_format_cap_destroy(channel_tech.capabilities); #endif @@ -1766,9 +1783,15 @@ static int reload_module() } AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, MODULE_DESCRIPTION, +#if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ + .support_level = AST_MODULE_SUPPORT_EXTENDED, +#endif .load = load_module, .unload = unload_module, .reload = reload_module, +#if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ + .load_pri = AST_MODPRI_CHANNEL_DRIVER, +#endif ); //AST_MODULE_INFO_STANDARD (ASTERISK_GPL_KEY, MODULE_DESCRIPTION); diff --git a/chan_dongle.h b/chan_dongle.h index b3adab78..a94cfbb0 100644 --- a/chan_dongle.h +++ b/chan_dongle.h @@ -38,9 +38,10 @@ INLINE_DECL const char * dev_state2str_msg(dev_state_t state) return enum2str(state, states, ITEMS_OF(states)); } -#if ASTERISK_VERSION_NUM >= 100000 /* 10+ */ +#if ASTERISK_VERSION_NUM >= 100000 && ASTERISK_VERSION_NUM < 130000 /* 10+..13- */ /* Only linear is allowed */ EXPORT_DECL struct ast_format chan_dongle_format; +//EXPORT_DEF struct ast_format chan_dongle_format; EXPORT_DECL struct ast_format_cap * chan_dongle_format_cap; #endif diff --git a/channel.c b/channel.c index 73e40336..7c978e62 100644 --- a/channel.c +++ b/channel.c @@ -26,6 +26,10 @@ #include /* ast_timer_fd() ast_timer_set_rate() ast_timer_ack() */ #include "ast_compat.h" +#if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ +#include +#include +#endif #include "channel.h" #include "chan_dongle.h" @@ -96,23 +100,27 @@ EXPORT_DEF int channels_loop(struct pvt * pvt, const struct ast_channel * reques /* FIXME: requestor may be just proxy/masquerade for real channel */ // use ast_bridged_channel(chan) ? // use requestor->tech->get_base_channel() ? - struct cpvt *cpvt = ast_channel_tech_pvt(requestor); - if(requestor && ast_channel_tech(requestor) == &channel_tech && cpvt && cpvt->pvt == pvt) - return 1; - return 0; + return (requestor + && ast_channel_tech(requestor) == &channel_tech + && ast_channel_tech_pvt(requestor) + && ((struct cpvt*) ast_channel_tech_pvt(requestor))->pvt == pvt) + ? 1 + : 0; } -// TODO: simplify by move common code to functions -/* TODO: add check when request 'holdother' what requestor is not on same device for 1.6 */ -#if ASTERISK_VERSION_NUM >= 110000 /* 11+ */ -static struct ast_channel * channel_request (attribute_unused const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, const char *data, int *cause) -#elif ASTERISK_VERSION_NUM >= 100000 /* 10+ */ -static struct ast_channel * channel_request (attribute_unused const char * type, struct ast_format_cap * cap, const struct ast_channel *requestor, void * data, int * cause) +#if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ +static struct ast_channel * channel_request (attribute_unused const char * type, struct ast_format_cap * cap, const struct ast_assigned_ids * assignedids, const struct ast_channel * requestor, const char * data, int * cause) +#elif ASTERISK_VERSION_NUM >= 110000 /* 11+ */ +static struct ast_channel * channel_request (attribute_unused const char * type, struct ast_format_cap * cap, const struct ast_channel * requestor, const char * data, int * cause) +#elif ASTERISK_VERSION_NUM >= 100000 /* 10-13 */ +static struct ast_channel * channel_request (attribute_unused const char * type, struct ast_format_cap * cap, const struct ast_channel * requestor, void * data, int * cause) #elif ASTERISK_VERSION_NUM >= 10800 /* 1.8+ */ +// TODO: simplify by move common code to functions static struct ast_channel * channel_request (attribute_unused const char * type, format_t format, const struct ast_channel * requestor, void * data, int * cause) -#else /* #if ASTERISK_VERSION_NUM < 10800 */ +#else /* 1.8- */ +/* TODO: add check when request 'holdother' what requestor is not on same device for 1.6 */ static struct ast_channel * channel_request (attribute_unused const char * type, int format, void * data, int * cause) -#endif /* #if ASTERISK_VERSION_NUM < 10800 */ +#endif { #if ASTERISK_VERSION_NUM >= 10800 && ASTERISK_VERSION_NUM < 100000 /* 1.8+ .. 10- */ format_t oldformat; @@ -134,15 +142,20 @@ static struct ast_channel * channel_request (attribute_unused const char * type, return NULL; } -#if ASTERISK_VERSION_NUM >= 100000 /* 10+ */ +#if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ + if (ast_format_cap_iscompatible_format(cap, ast_format_slin) != AST_FORMAT_CMP_EQUAL) +#elif ASTERISK_VERSION_NUM >= 100000 /* 10-13 */ if (!ast_format_cap_iscompatible(cap, &chan_dongle_format)) #else - oldformat = format; - format &= AST_FORMAT_SLINEAR; - if (!format) + oldformat = format; + format &= AST_FORMAT_SLINEAR; + if (!format) #endif - { -#if ASTERISK_VERSION_NUM >= 100000 /* 10+ */ + { +#if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ + struct ast_str *codec_buf = ast_str_alloca(64); + ast_log (LOG_WARNING, "Asked to get a channel of unsupported format '%s'\n", ast_format_cap_get_names(cap, &codec_buf)); +#elif ASTERISK_VERSION_NUM >= 100000 /* 10-13 */ char buf[255]; ast_log (LOG_WARNING, "Asked to get a channel of unsupported format '%s'\n", ast_getformatname_multiple (buf, 255, cap)); #elif ASTERISK_VERSION_NUM >= 10800 /* 1.8+ */ @@ -167,7 +180,11 @@ static struct ast_channel * channel_request (attribute_unused const char * type, #endif if(pvt) { +#if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ + channel = new_channel (pvt, AST_STATE_DOWN, NULL, pvt_get_pseudo_call_idx(pvt), CALL_DIR_OUTGOING, CALL_STATE_INIT, NULL, assignedids, requestor); +#else channel = new_channel (pvt, AST_STATE_DOWN, NULL, pvt_get_pseudo_call_idx(pvt), CALL_DIR_OUTGOING, CALL_STATE_INIT, NULL, requestor); +#endif ast_mutex_unlock (&pvt->lock); if(!channel) { @@ -592,13 +609,13 @@ static void write_conference(struct pvt * pvt, const char * buffer, size_t lengt #if ASTERISK_VERSION_NUM >= 100000 /* 10+ */ -#define subclass_integer subclass.integer +#define subclass_integer subclass.integer #elif ASTERISK_VERSION_NUM >= 10800 /* 1.8+ */ -#define subclass_codec subclass.codec -#define subclass_integer subclass.integer +#define subclass_codec subclass.codec +#define subclass_integer subclass.integer #else /* 1.8- */ -#define subclass_codec subclass -#define subclass_integer subclass +#define subclass_codec subclass +#define subclass_integer subclass #endif #/* */ @@ -640,7 +657,9 @@ static struct ast_frame* channel_read (struct ast_channel* channel) memset (&cpvt->a_read_frame, 0, sizeof (cpvt->a_read_frame)); cpvt->a_read_frame.frametype = AST_FRAME_VOICE; -#if ASTERISK_VERSION_NUM >= 100000 /* 10+ */ +#if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ + cpvt->a_read_frame.subclass.format = ao2_bump(ast_format_slin); +#elif ASTERISK_VERSION_NUM >= 100000 /* 10-13 */ ast_format_copy(&cpvt->a_read_frame.subclass.format, &chan_dongle_format); #else cpvt->a_read_frame.subclass_codec = AST_FORMAT_SLINEAR; @@ -758,7 +777,9 @@ static int channel_write (struct ast_channel* channel, struct ast_frame* f) size_t count; int gains[2]; -#if ASTERISK_VERSION_NUM >= 100000 /* 10+ */ +#if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ + if (f->frametype != AST_FRAME_VOICE || ast_format_cmp(f->subclass.format, ast_format_slin) != AST_FORMAT_CMP_EQUAL) +#elif ASTERISK_VERSION_NUM >= 100000 /* 10-13 */ if (f->frametype != AST_FRAME_VOICE || f->subclass.format.id != AST_FORMAT_SLINEAR) #else /* 10- */ if (f->frametype != AST_FRAME_VOICE || f->subclass_codec != AST_FORMAT_SLINEAR) @@ -793,13 +814,16 @@ static int channel_write (struct ast_channel* channel, struct ast_frame* f) if(CPVT_TEST_FLAG(cpvt, CALL_FLAG_MULTIPARTY) && !CPVT_TEST_FLAG(cpvt, CALL_FLAG_BRIDGE_CHECK)) { +#if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ + RAII_VAR(struct ast_channel *, bridged, ast_channel_bridge_peer(channel), ast_channel_cleanup); +#else struct ast_channel* bridged = ast_bridged_channel(channel); +#endif struct cpvt *tmp_cpvt; CPVT_SET_FLAGS(cpvt, CALL_FLAG_BRIDGE_CHECK); - tmp_cpvt = ast_channel_tech_pvt(bridged); - if(bridged && ast_channel_tech(bridged) == &channel_tech && tmp_cpvt && tmp_cpvt->pvt == pvt) + if (bridged && ast_channel_tech(bridged) == &channel_tech && (tmp_cpvt = ast_channel_tech_pvt(bridged)) && tmp_cpvt->pvt == pvt) { CPVT_SET_FLAGS(cpvt, CALL_FLAG_BRIDGE_LOOP); CPVT_SET_FLAGS((struct cpvt*)ast_channel_tech_pvt(bridged), CALL_FLAG_BRIDGE_LOOP); @@ -1004,6 +1028,7 @@ static int channel_indicate (struct ast_channel* channel, int condition, const v case AST_CONTROL_PROCEEDING: case AST_CONTROL_VIDUPDATE: case AST_CONTROL_SRCUPDATE: + case AST_CONTROL_PVT_CAUSE_CODE://33 break; case AST_CONTROL_HOLD: @@ -1154,7 +1179,12 @@ static void set_channel_vars(struct pvt* pvt, struct ast_channel* channel) { "DONGLENUMBER", pvt->subscriber_number }, }; +#if ASTERISK_VERSION_NUM >= 110000 /* 11+ */ ast_channel_language_set(channel, CONF_SHARED(pvt, language)); +#else + //TODO uncomment and fix + //ast_string_field_set (channel, language, CONF_SHARED(pvt, language); +#endif for(idx = 0; idx < ITEMS_OF(dev_vars); ++idx) pbx_builtin_setvar_helper (channel, dev_vars[idx].name, dev_vars[idx].value); @@ -1162,16 +1192,24 @@ static void set_channel_vars(struct pvt* pvt, struct ast_channel* channel) } #/* NOTE: called from device and current levels with locked pvt */ +#if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ +EXPORT_DEF struct ast_channel* new_channel (struct pvt* pvt, int ast_state, const char* cid_num, int call_idx, unsigned dir, call_state_t state, const char * dnid, const struct ast_assigned_ids *assignedids, attribute_unused const struct ast_channel * requestor) +#else EXPORT_DEF struct ast_channel* new_channel (struct pvt* pvt, int ast_state, const char* cid_num, int call_idx, unsigned dir, call_state_t state, const char * dnid, attribute_unused const struct ast_channel * requestor) +#endif { +// struct ast_channel * channel = new_channel (pvt, AST_STATE_RING, number, call_idx, CALL_DIR_INCOMING, state, pvt->has_subscriber_number ? pvt->subscriber_number : CONF_SHARED(pvt, exten), $ + struct ast_channel* channel; struct cpvt * cpvt; cpvt = cpvt_alloc(pvt, call_idx, dir, state); if (cpvt) { -#if ASTERISK_VERSION_NUM >= 10800 - channel = ast_channel_alloc (1, ast_state, cid_num, PVT_ID(pvt), NULL, dnid, CONF_SHARED(pvt, context), requestor ? ast_channel_linkedid(requestor) : NULL, 0, "%s/%s-%02u%08lx", channel_tech.type, PVT_ID(pvt), call_idx, pvt->channel_instanse); +#if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ + channel = ast_channel_alloc (1, ast_state, cid_num, PVT_ID(pvt), NULL, dnid, CONF_SHARED(pvt, context), assignedids, requestor, 0, "%s/%s-%02u%08lx", channel_tech.type, PVT_ID(pvt), call_idx, pvt->channel_instanse); +#elif ASTERISK_VERSION_NUM >= 10800 /* 1.8+ */ + channel = ast_channel_alloc (1, ast_state, cid_num, PVT_ID(pvt), NULL, dnid, CONF_SHARED(pvt, context), requestor ? ast_channel_linkedid(requestor): NULL, 0, "%s/%s-%02u%08lx", channel_tech.type, PVT_ID(pvt), call_idx, pvt->channel_instanse); #else channel = ast_channel_alloc (1, ast_state, cid_num, PVT_ID(pvt), NULL, dnid, CONF_SHARED(pvt, context), 0, "%s/%s-%02u%08lx", channel_tech.type, PVT_ID(pvt), call_idx, pvt->channel_instanse); #endif @@ -1183,8 +1221,11 @@ EXPORT_DEF struct ast_channel* new_channel (struct pvt* pvt, int ast_state, cons ast_channel_tech_pvt_set(channel, cpvt); ast_channel_tech_set(channel, &channel_tech); - -#if ASTERISK_VERSION_NUM >= 110000 /* 11+ */ +#if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ + ast_format_cap_append(ast_channel_nativeformats(channel), ast_format_slin, 0); + ast_channel_set_writeformat(channel, ast_format_slin); + ast_channel_set_readformat(channel, ast_format_slin); +#elif ASTERISK_VERSION_NUM >= 110000 /* 11+ */ ast_format_set(ast_channel_readformat(channel), AST_FORMAT_SLINEAR, 0); ast_format_set(ast_channel_writeformat(channel), AST_FORMAT_SLINEAR, 0); ast_format_cap_set(ast_channel_nativeformats(channel), ast_channel_writeformat(channel)); @@ -1285,11 +1326,13 @@ EXPORT_DEF void start_local_channel (struct pvt* pvt, const char* exten, const c snprintf (channel_name, sizeof (channel_name), "%s@%s", exten, CONF_SHARED(pvt, context)); -#if ASTERISK_VERSION_NUM >= 100000 /* 10+ */ +#if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ + channel = ast_request ("Local", channel_tech.capabilities, NULL, NULL, channel_name, &cause); +#elif ASTERISK_VERSION_NUM >= 100000 /* 10-13 */ channel = ast_request ("Local", chan_dongle_format_cap, NULL, channel_name, &cause); #elif ASTERISK_VERSION_NUM >= 10800 /* 1.8+ */ channel = ast_request ("Local", AST_FORMAT_AUDIO_MASK, NULL, channel_name, &cause); -#else +#else /* 1.8- */ channel = ast_request ("Local", AST_FORMAT_AUDIO_MASK, channel_name, &cause); #endif if (channel) @@ -1374,7 +1417,7 @@ static int channel_func_read(struct ast_channel* channel, attribute_unused const static int channel_func_write(struct ast_channel* channel, const char* function, char* data, const char* value) { struct cpvt* cpvt = ast_channel_tech_pvt(channel); - struct pvt* pvt; + struct pvt* pvt = cpvt->pvt; call_state_t newstate, oldstate; int ret = 0; @@ -1383,7 +1426,6 @@ static int channel_func_write(struct ast_channel* channel, const char* function, ast_log (LOG_WARNING, "call on unreferenced %s\n", ast_channel_name(channel)); return -1; } - pvt = cpvt->pvt; if (!strcasecmp(data, "callstate")) { @@ -1455,7 +1497,7 @@ EXPORT_DEF struct ast_channel_tech channel_tech = .type = "Dongle", .description = MODULE_DESCRIPTION, #if ASTERISK_VERSION_NUM < 100000 /* 10- */ - .capabilities = AST_FORMAT_SLINEAR, + .capabilities = AST_FORMAT_SLINEAR, #endif .requester = channel_request, .call = channel_call, diff --git a/channel.h b/channel.h index e62bc9d8..702e3f23 100644 --- a/channel.h +++ b/channel.h @@ -21,7 +21,11 @@ struct cpvt; EXPORT_DECL struct ast_channel_tech channel_tech; +#if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ +EXPORT_DECL struct ast_channel* new_channel (struct pvt * pvt, int ast_state, const char * cid_num, int call_idx, unsigned dir, unsigned state, const char * exten, const struct ast_assigned_ids *assignedids, const struct ast_channel * requestor); +#else EXPORT_DECL struct ast_channel* new_channel (struct pvt * pvt, int ast_state, const char * cid_num, int call_idx, unsigned dir, unsigned state, const char * exten, const struct ast_channel * requestor); +#endif EXPORT_DECL int queue_control_channel (struct cpvt * cpvt, enum ast_control_frame_type control); EXPORT_DECL int queue_hangup (struct ast_channel * channel, int hangupcause); EXPORT_DECL void start_local_channel (struct pvt * pvt, const char * exten, const char * number, channel_var_t * vars); diff --git a/manager.c b/manager.c index 4f9e9620..0f9a0ca6 100644 --- a/manager.c +++ b/manager.c @@ -731,9 +731,16 @@ static const struct dongle_manager EXPORT_DEF void manager_register() { unsigned i; +#if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ + struct ast_module* module = self_module(); +#endif + for(i = 0; i < ITEMS_OF(dcm); i++) { -#if ASTERISK_VERSION_NUM >= 110000 +#if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ + ast_manager_register2 (dcm[i].name, dcm[i].authority, dcm[i].func, + module, dcm[i].brief, dcm[i].desc); +#elif ASTERISK_VERSION_NUM >= 110000 /* 11+ */ ast_manager_register2 (dcm[i].name, dcm[i].authority, dcm[i].func, NULL, dcm[i].brief, dcm[i].desc); #else ast_manager_register2 (dcm[i].name, dcm[i].authority, dcm[i].func, dcm[i].brief, dcm[i].desc); diff --git a/pdu.c b/pdu.c index 5f87db3f..e0bdab78 100644 --- a/pdu.c +++ b/pdu.c @@ -463,7 +463,7 @@ static int pdu_parse_number(char ** pdu, size_t * pdu_length, unsigned digits, i *number++ = digit; digit = pdu_code2digit(pdu[0][0]); - if(digit < 0 || (digit == 0 && (syms != 2 || (digits & 0x1) == 0))) + if((signed char)digit < 0 || (digit == 0 && (syms != 2 || (digits & 0x1) == 0))) return -1; *number++ = digit; From 897b27c273d302c2217319c5ff5de44024682846 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Sat, 5 Sep 2015 23:08:09 +0200 Subject: [PATCH 007/117] Translate NUL to @ in incoming SMS. Author: mio Source: https://github.com/bg111/asterisk-chan-dongle/issues/153 Not using full 3GPP version from https://code.google.com/p/ asterisk-chan-dongle/issues/detail?id=140 because of the issues described there. This should simply fix that you don't get a truncated SMS because of an @ sign. --- char_conv.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/char_conv.c b/char_conv.c index 3167f307..2a4be705 100644 --- a/char_conv.c +++ b/char_conv.c @@ -235,11 +235,20 @@ static ssize_t hexstr_7bit_to_char (const char* in, size_t in_length, char* out, c = (c >> 1) | b; b = ((unsigned char) hexval) >> (8 - s); + if (c == 0 && i + 1 < in_length) { + /* @ is encoded as NUL */ + c = '@'; + } + out[x] = c; x++; s++; if (s == 8) { + if (b == 0 && i + 1 < in_length) { + /* @ is encoded as NUL */ + b = '@'; + } out[x] = b; s = 1; b = 0; x++; From cd03d3a8fc000782eb97293ed874c31973d7b64f Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Sun, 6 Sep 2015 11:25:25 +0200 Subject: [PATCH 008/117] Fix so Asterisk 1.8 compilation works again. --- channel.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/channel.c b/channel.c index 7c978e62..1875748d 100644 --- a/channel.c +++ b/channel.c @@ -1028,7 +1028,9 @@ static int channel_indicate (struct ast_channel* channel, int condition, const v case AST_CONTROL_PROCEEDING: case AST_CONTROL_VIDUPDATE: case AST_CONTROL_SRCUPDATE: - case AST_CONTROL_PVT_CAUSE_CODE://33 +#if ASTERISK_VERSION_NUM >= 110000 /* 11+ */ + case AST_CONTROL_PVT_CAUSE_CODE: +#endif /* ^11+ */ break; case AST_CONTROL_HOLD: From 7eb9b856e3824fc5fb44158dd03b74fdeef637fe Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Sun, 6 Sep 2015 11:25:48 +0200 Subject: [PATCH 009/117] Spaces cleanup. Asterisk version number cleanup. --- app.c | 20 ++--- ast_compat.h | 8 +- at_command.c | 26 +++---- at_parse.c | 35 ++++----- at_queue.c | 20 ++--- at_read.c | 2 +- at_response.c | 175 +++++++++++++++++++++-------------------- chan_dongle.c | 120 +++++++++++++--------------- chan_dongle.h | 10 +-- channel.c | 201 +++++++++++++++++++++++++++-------------------- channel.h | 17 +++- char_conv.c | 4 +- cli.c | 31 ++++---- cpvt.c | 2 +- dc_config.c | 4 +- helpers.c | 6 +- manager.c | 76 +++++++++--------- mixbuffer.c | 2 +- pdiscovery.c | 39 +++++----- pdu.c | 212 +++++++++++++++++++++++++------------------------- ringbuffer.c | 16 ++-- ringbuffer.h | 6 +- test/parse.c | 69 ++++++---------- 23 files changed, 553 insertions(+), 548 deletions(-) diff --git a/app.c b/app.c index a46fc8d5..6ea5e935 100644 --- a/app.c +++ b/app.c @@ -3,12 +3,12 @@ #endif /* HAVE_CONFIG_H */ #ifdef BUILD_APPLICATIONS -/* +/* Copyright (C) 2009 - 2010 - + Artem Makhutov http://www.makhutov.org - + Dmitry Vagin bg @@ -124,7 +124,7 @@ static const struct dongle_application int (*func)(struct ast_channel* channel, const char* data); const char* synopsis; const char* desc; -} dca[] = +} dca[] = { { "DongleStatus", @@ -138,7 +138,7 @@ static const struct dongle_application { "DongleSendSMS", app_send_sms_exec, - "DongleSendSMS(Device,Dest,Message,Validity,Report)", + "DongleSendSMS(Device,Dest,Message,Validity,Report)", "DongleSendSMS(Device,Dest,Message,Validity,Report)\n" " Device - Id of device from dongle.conf\n" " Dest - destination\n" @@ -148,11 +148,11 @@ static const struct dongle_application } }; -#if ASTERISK_VERSION_NUM >= 10800 -typedef int (*app_func_t)(struct ast_channel* channel, const char * data); -#else -typedef int (*app_func_t)(struct ast_channel* channel, void * data); -#endif +#if ASTERISK_VERSION_NUM >= 10800 /* 1.8+ */ +typedef int (*app_func_t)(struct ast_channel *channel, const char *data); +#else /* 1.8- */ +typedef int (*app_func_t)(struct ast_channel *channel, void *data); +#endif /* ^1.8- */ #/* */ EXPORT_DEF void app_register() diff --git a/ast_compat.h b/ast_compat.h index c22b75e3..1ad407aa 100644 --- a/ast_compat.h +++ b/ast_compat.h @@ -10,7 +10,7 @@ #include /* Asterisk 11+ channel opaqification */ -#if ASTERISK_VERSION_NUM < 110000 +#if ASTERISK_VERSION_NUM < 110000 /* 11- */ /* channel->name */ static inline const char *ast_channel_name(const struct ast_channel *chan) { return chan->name; } @@ -44,13 +44,13 @@ static inline const char *ast_channel_linkedid(const struct ast_channel *chan) { /* channel->hangupcause */ static inline void ast_channel_hangupcause_set(struct ast_channel *chan, int value) { chan->hangupcause = value; } -#if ASTERISK_VERSION_NUM >= 100000 +#if ASTERISK_VERSION_NUM >= 100000 /* 10+ */ /* channel->*format* */ static inline struct ast_format_cap *ast_channel_nativeformats(const struct ast_channel *chan) { return chan->nativeformats; } static inline struct ast_format ast_channel_readformat(const struct ast_channel *chan) { return chan->readformat; } static inline struct ast_format ast_channel_writeformat(const struct ast_channel *chan) { return chan->writeformat; } -#endif +#endif /* ^10+ */ -#endif +#endif /* ^11- */ #endif /* CHAN_DONGLE_AST_COMPAT_H_INCLUDED */ diff --git a/at_command.c b/at_command.c index 8c2a5a7f..8ed6c842 100644 --- a/at_command.c +++ b/at_command.c @@ -1,9 +1,9 @@ -/* +/* Copyright (C) 2009 - 2010 - + Artem Makhutov http://www.makhutov.org - + Dmitry Vagin bg @@ -46,7 +46,7 @@ static const char cmd_ddsetex2[] = "AT^DDSETEX=2\r"; static int at_fill_generic_cmd_va (at_queue_cmd_t * cmd, const char * format, va_list ap) { char buf[4096]; - + cmd->length = vsnprintf (buf, sizeof(buf)-1, format, ap); buf[cmd->length] = 0; @@ -255,7 +255,7 @@ EXPORT_DEF int at_enque_pdu(struct cpvt * cpvt, const char * pdu, attribute_unus size_t pdulen = length; int scalen = pdu_parse_sca(&ptr, &pdulen); - + if(scalen < 2 || length % 2 != 0) { return -EINVAL; @@ -263,7 +263,7 @@ EXPORT_DEF int at_enque_pdu(struct cpvt * cpvt, const char * pdu, attribute_unus at_cmd[1].data = ast_malloc(length + 2); if(!at_cmd[1].data) - { + { return -ENOMEM; } @@ -272,15 +272,15 @@ EXPORT_DEF int at_enque_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[0].length = snprintf(buf, sizeof(buf), "AT+CMGS=%d\r", (int)(pdulen / 2)); at_cmd[0].data = ast_strdup(buf); if(!at_cmd[0].data) { ast_free(at_cmd[1].data); - return -ENOMEM; + 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); */ @@ -300,7 +300,7 @@ EXPORT_DEF int at_enque_sms (struct cpvt* cpvt, const char* destination, const c char buf[1024] = "AT+CMGS=\""; char pdu_buf[2048]; pvt_t* pvt = cpvt->pvt; - + at_queue_cmd_t at_cmd[] = { { CMD_AT_CMGS, RES_SMS_PROMPT, ATQ_CMD_FLAG_DEFAULT, { ATQ_CMD_TIMEOUT_2S, 0} , NULL, 0 }, { CMD_AT_SMSTEXT, RES_OK, ATQ_CMD_FLAG_DEFAULT, { ATQ_CMD_TIMEOUT_40S, 0} , NULL, 0 } @@ -655,7 +655,7 @@ EXPORT_DEF int at_enque_activate (struct cpvt* cpvt) if (cpvt->state != CALL_STATE_ONHOLD && cpvt->state != CALL_STATE_WAITING) { - ast_log (LOG_ERROR, "[%s] Imposible activate call idx %d from state '%s'\n", + ast_log (LOG_ERROR, "[%s] Imposible activate call idx %d from state '%s'\n", PVT_ID(cpvt->pvt), cpvt->call_idx, call_state2str(cpvt->state)); return -1; } @@ -811,11 +811,11 @@ EXPORT_DEF int at_enque_hangup (struct cpvt* cpvt, int call_idx) not found yes */ /* - static const struct + static const struct { at_cmd_t cmd; const char *data; - } commands[] = + } commands[] = { { CMD_AT_CHUP, "AT+CHUP\r" }, { CMD_AT_CHLD_1x, "AT+CHLD=1%d\r" } diff --git a/at_parse.c b/at_parse.c index 27ef6d74..85172cb6 100644 --- a/at_parse.c +++ b/at_parse.c @@ -1,9 +1,9 @@ -/* +/* Copyright (C) 2009 - 2010 - + Artem Makhutov http://www.makhutov.org - + Dmitry Vagin bg @@ -27,7 +27,7 @@ static unsigned mark_line(char * line, const char * delimiters, char * pointers[]) { unsigned found = 0; - + for(; line[0] && delimiters[found]; line++) { if(line[0] == delimiters[found]) @@ -89,7 +89,7 @@ EXPORT_DEF char* at_parse_cops (char* str) * parse COPS response in the following format: * +COPS: [,,,] * - * example + * example * +COPS: 0,0,"TELE2",0 */ @@ -265,7 +265,7 @@ EXPORT_DEF int at_parse_cmti (const char* str) /* * parse cmti info in the following format: - * +CMTI: , + * +CMTI: , */ return sscanf (str, "+CMTI: %*[^,],%u", &index) == 1 ? index : -1; @@ -288,7 +288,7 @@ static const char * parse_cmgr_text(char ** str, size_t len, char * oa, size_t o char delimiters[] = ",,,\n"; char * marks[STRLEN(delimiters)]; size_t length; - + unsigned count = mark_line(*str, delimiters, marks); if(count == ITEMS_OF(marks)) { @@ -420,7 +420,7 @@ EXPORT_DEF int at_parse_cusd (char* str, int * type, char** cusd, int * dcs) /* * parse cusd message in the following format: * +CUSD: ,[,] - * + * * examples * +CUSD: 5 * +CUSD: 0,"100,00 EURO, valid till 01.01.2010, you are using tariff "Mega Tariff". More informations *111#.",15 @@ -597,17 +597,12 @@ EXPORT_DEF int at_parse_clcc(char* str, unsigned * call_idx, unsigned * dir, uns if(mark_line(str, delimiters, marks) == ITEMS_OF(marks)) { - if( sscanf(marks[0] + 1, "%u", call_idx) == 1 - && - sscanf(marks[1] + 1, "%u", dir) == 1 - && - sscanf(marks[2] + 1, "%u", state) == 1 - && - sscanf(marks[3] + 1, "%u", mode) == 1 - && - sscanf(marks[4] + 1, "%u", mpty) == 1 - && - sscanf(marks[6] + 1, "%u", toa) == 1) + if(sscanf(marks[0] + 1, "%u", call_idx) == 1 + && sscanf(marks[1] + 1, "%u", dir) == 1 + && sscanf(marks[2] + 1, "%u", state) == 1 + && sscanf(marks[3] + 1, "%u", mode) == 1 + && sscanf(marks[4] + 1, "%u", mpty) == 1 + && sscanf(marks[6] + 1, "%u", toa) == 1) { marks[5]++; if(marks[5][0] == '"') @@ -627,7 +622,7 @@ EXPORT_DEF int at_parse_clcc(char* str, unsigned * call_idx, unsigned * dir, uns #/* */ EXPORT_DEF int at_parse_ccwa(char* str, unsigned * class) { - /* + /* * CCWA may be in form: * in response of AT+CCWA=? * +CCWA: (0,1) diff --git a/at_queue.c b/at_queue.c index def47777..dc480a71 100644 --- a/at_queue.c +++ b/at_queue.c @@ -1,4 +1,4 @@ -/* +/* Copyright (C) 2009 - 2010 Artem Makhutov @@ -28,7 +28,7 @@ static void at_queue_free_data(at_queue_cmd_t * cmd) { if(cmd->data) { - if((cmd->flags & ATQ_CMD_FLAG_STATIC) == 0) + if((cmd->flags & ATQ_CMD_FLAG_STATIC) == 0) { ast_free (cmd->data); cmd->data = NULL; @@ -67,8 +67,8 @@ static void at_queue_remove (struct pvt * pvt) { PVT_STATE(pvt, at_tasks)--; PVT_STATE(pvt, at_cmds) -= task->cmdsno - task->cindex; - ast_debug (4, "[%s] remove task with %u command(s) begin with '%s' expected response '%s' from queue\n", - PVT_ID(pvt), task->cmdsno, at_cmd2str (task->cmds[0].cmd), + ast_debug (4, "[%s] remove task with %u command(s) begin with '%s' expected response '%s' from queue\n", + PVT_ID(pvt), task->cmdsno, at_cmd2str (task->cmds[0].cmd), at_res2str (task->cmds[0].res)); at_queue_free(task); @@ -124,8 +124,8 @@ static at_queue_task_t * at_queue_add (struct cpvt * cpvt, const at_queue_cmd_t PVT_STAT(pvt, at_tasks) ++; PVT_STAT(pvt, at_cmds) += cmdsno; - ast_debug (4, "[%s] insert task with %u commands begin with '%s' expected response '%s' %s of queue\n", - PVT_ID(pvt), e->cmdsno, at_cmd2str (e->cmds[0].cmd), + ast_debug (4, "[%s] insert task with %u commands begin with '%s' expected response '%s' %s of queue\n", + PVT_ID(pvt), e->cmdsno, at_cmd2str (e->cmds[0].cmd), at_res2str (e->cmds[0].res), prio ? "after head" : "at tail"); } } @@ -218,9 +218,9 @@ EXPORT_DEF void at_queue_remove_cmd (struct pvt* pvt, at_res_t res) task->cindex++; PVT_STATE(pvt, at_cmds)--; - ast_debug (4, "[%s] remove command '%s' expected response '%s' real '%s' cmd %u/%u flags 0x%02x from queue\n", - PVT_ID(pvt), at_cmd2str (task->cmds[index].cmd), - at_res2str (task->cmds[index].res), at_res2str (res), + ast_debug (4, "[%s] remove command '%s' expected response '%s' real '%s' cmd %u/%u flags 0x%02x from queue\n", + PVT_ID(pvt), at_cmd2str (task->cmds[index].cmd), + at_res2str (task->cmds[index].res), at_res2str (res), task->cindex, task->cmdsno, task->cmds[index].flags); if((task->cindex >= task->cmdsno) || (task->cmds[index].res != res && (task->cmds[index].flags & ATQ_CMD_FLAG_IGNORE) == 0)) @@ -245,7 +245,7 @@ EXPORT_DEF int at_queue_run (struct pvt * pvt) { if(cmd->length > 0) { - ast_debug (4, "[%s] write command '%s' expected response '%s' length %u\n", + ast_debug (4, "[%s] write command '%s' expected response '%s' length %u\n", PVT_ID(pvt), at_cmd2str (cmd->cmd), at_res2str (cmd->res), cmd->length); fail = at_write(pvt, cmd->data, cmd->length); diff --git a/at_read.c b/at_read.c index 43e2f888..5bf7d74f 100644 --- a/at_read.c +++ b/at_read.c @@ -79,7 +79,7 @@ EXPORT_DEF ssize_t at_read (int fd, const char * dev, struct ringbuffer* rb) { rb_write_upd (rb, n); - ast_debug (5, "[%s] receive %zu byte, used %zu, free %zu, read %zu, write %zu\n", + ast_debug (5, "[%s] receive %zu byte, used %zu, free %zu, read %zu, write %zu\n", dev, n, rb_used (rb), rb_free (rb), rb->read, rb->write); iovcnt = rb_read_all_iov (rb, iov); diff --git a/at_response.c b/at_response.c index e8a35b6a..83e800a7 100644 --- a/at_response.c +++ b/at_response.c @@ -1,9 +1,9 @@ -/* +/* Copyright (C) 2009 - 2010 - + Artem Makhutov http://www.makhutov.org - + Dmitry Vagin bg @@ -215,7 +215,7 @@ static int at_response_ok (struct pvt* pvt, at_res_t res) case CMD_AT_A: case CMD_AT_CHLD_2x: -/* not work, ^CONN: appear before OK for CHLD_ANSWER +/* not work, ^CONN: appear before OK for CHLD_ANSWER task->cpvt->answered = 1; task->cpvt->needhangup = 1; */ @@ -325,7 +325,7 @@ static int at_response_error (struct pvt* pvt, at_res_t res) { switch (ecmd->cmd) { - /* critical errors */ + /* critical errors */ case CMD_AT: case CMD_AT_Z: case CMD_AT_E: @@ -841,10 +841,16 @@ static int start_pbx(struct pvt* pvt, const char * number, int call_idx, call_st /* TODO: pass also Subscriber number or other DID info for exten */ #if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ - struct ast_channel * channel = new_channel (pvt, AST_STATE_RING, number, call_idx, CALL_DIR_INCOMING, state, pvt->has_subscriber_number ? pvt->subscriber_number : CONF_SHARED(pvt, exten), NULL, NULL); -#else - struct ast_channel * channel = new_channel (pvt, AST_STATE_RING, number, call_idx, CALL_DIR_INCOMING, state, pvt->has_subscriber_number ? pvt->subscriber_number : CONF_SHARED(pvt, exten), NULL); -#endif + struct ast_channel * channel = new_channel( + pvt, AST_STATE_RING, number, call_idx, CALL_DIR_INCOMING, state, + pvt->has_subscriber_number ? pvt->subscriber_number : CONF_SHARED(pvt, exten), + NULL, NULL); +#else /* 13- */ + struct ast_channel * channel = new_channel( + pvt, AST_STATE_RING, number, call_idx, CALL_DIR_INCOMING, state, + pvt->has_subscriber_number ? pvt->subscriber_number : CONF_SHARED(pvt, exten), + NULL); +#endif /* ^13- */ if (!channel) { @@ -865,7 +871,7 @@ static int start_pbx(struct pvt* pvt, const char * number, int call_idx, call_st // ast_pbx_start() usually failed if asterisk.conf minmemfree set too low, try drop buffer cache sync && echo 3 > /proc/sys/vm/drop_caches #if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ ast_channel_unlock(channel); -#endif +#endif /* ^13+ */ if (ast_pbx_start (channel)) { ast_channel_tech_pvt_set(channel, NULL); @@ -1050,7 +1056,7 @@ static int at_response_ccwa(struct pvt* pvt, char* str) int status, n; unsigned class; - /* + /* * CCWA may be in form: * in response of AT+CCWA=? * +CCWA: (0,1) @@ -1161,7 +1167,9 @@ static int at_response_cmti (struct pvt* pvt, const char* str) return -1; } else - pvt->incoming_sms = 1; + { + pvt->incoming_sms = 1; + } } return 0; @@ -1203,82 +1211,82 @@ static int at_response_cmgr (struct pvt* pvt, const char * str, size_t len) manager_event_message("DongleNewCMGR", PVT_ID(pvt), str); 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); // YYY - if (err == (void*)0x1) /* HACK! */ + if (ecmd->res == RES_CMGR || ecmd->cmd == CMD_USER) { - 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); - return 0; - } - else if (err) - { - ast_log (LOG_WARNING, "[%s] Error parsing incoming message '%s' at possition %d: %s\n", PVT_ID(pvt), str, (int)(err_pos - cmgr), err); - return 0; - } + at_queue_handle_result (pvt, RES_CMGR); + pvt->incoming_sms = 0; + pvt_try_restate(pvt); - ast_debug (1, "[%s] Successfully read SMS message\n", PVT_ID(pvt)); + cmgr = err_pos = ast_strdupa (str); + err = at_parse_cmgr (&err_pos, len, oa, sizeof(oa), &oa_enc, &msg, &msg_enc); // 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); + return 0; + } + else if (err) + { + ast_log (LOG_WARNING, "[%s] Error parsing incoming message '%s' at possition %d: %s\n", PVT_ID(pvt), str, (int)(err_pos - cmgr), err); + return 0; + } - /* last chance to define encodings */ - if (oa_enc == STR_ENCODING_UNKNOWN) - oa_enc = pvt->use_ucs2_encoding ? STR_ENCODING_UCS2_HEX : STR_ENCODING_7BIT; + ast_debug (1, "[%s] Successfully read SMS message\n", PVT_ID(pvt)); - if (msg_enc == STR_ENCODING_UNKNOWN) - msg_enc = pvt->use_ucs2_encoding ? STR_ENCODING_UCS2_HEX : STR_ENCODING_7BIT; + /* last chance to define encodings */ + if (oa_enc == STR_ENCODING_UNKNOWN) + oa_enc = pvt->use_ucs2_encoding ? STR_ENCODING_UCS2_HEX : STR_ENCODING_7BIT; - /* decode number and message */ - res = str_recode (RECODE_DECODE, oa_enc, oa, strlen(oa), from_number_utf8_str, sizeof (from_number_utf8_str)); - 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; + if (msg_enc == STR_ENCODING_UNKNOWN) + msg_enc = pvt->use_ucs2_encoding ? STR_ENCODING_UCS2_HEX : STR_ENCODING_7BIT; - msg_len = strlen(msg); - res = str_recode (RECODE_DECODE, msg_enc, msg, msg_len, sms_utf8_str, sizeof (sms_utf8_str)); - 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; - } + /* decode number and message */ + res = str_recode (RECODE_DECODE, oa_enc, oa, strlen(oa), from_number_utf8_str, sizeof (from_number_utf8_str)); + 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_recode (RECODE_DECODE, msg_enc, msg, msg_len, sms_utf8_str, sizeof (sms_utf8_str)); + 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 from %s: '%s'\n", PVT_ID(pvt), number, msg); - ast_base64encode (text_base64, (unsigned char*)msg, msg_len, sizeof(text_base64)); + ast_verb (1, "[%s] Got SMS from %s: '%s'\n", PVT_ID(pvt), number, msg); + ast_base64encode (text_base64, (unsigned char*)msg, msg_len, sizeof(text_base64)); - manager_event_new_sms(PVT_ID(pvt), number, msg); - manager_event_new_sms_base64(PVT_ID(pvt), number, text_base64); - { - channel_var_t vars[] = + manager_event_new_sms(PVT_ID(pvt), number, msg); + manager_event_new_sms_base64(PVT_ID(pvt), number, text_base64); { - { "SMS", msg } , - { "SMS_BASE64", text_base64 }, - { "CMGR", (char *)str }, - { NULL, NULL }, - }; - start_local_channel (pvt, "sms", number, vars); + channel_var_t vars[] = + { + { "SMS", msg } , + { "SMS_BASE64", text_base64 }, + { "CMGR", (char *)str }, + { NULL, NULL }, + }; + start_local_channel (pvt, "sms", number, vars); + } + } + else + { + ast_log (LOG_ERROR, "[%s] Received '+CMGR' when expecting '%s' response to '%s', ignoring\n", PVT_ID(pvt), + at_res2str (ecmd->res), at_cmd2str (ecmd->cmd)); } - } - else - { - ast_log (LOG_ERROR, "[%s] Received '+CMGR' when expecting '%s' response to '%s', ignoring\n", PVT_ID(pvt), - at_res2str (ecmd->res), at_cmd2str (ecmd->cmd)); - } } else { @@ -1387,7 +1395,7 @@ static int at_response_cusd (struct pvt * pvt, char * str, size_t len) manager_event_message("DongleNewUSSDBase64", PVT_ID(pvt), text_base64); { - channel_var_t vars[] = + channel_var_t vars[] = { { "USSD_TYPE", typebuf }, { "USSD_TYPE_STR", ast_strdupa(typestr) }, @@ -1714,13 +1722,8 @@ int at_response (struct pvt* pvt, const struct iovec iov[2], int iovcnt, at_res_ ast_debug (5, "[%s] iovcnt == 2\n", PVT_ID(pvt)); str = alloca(len + 1); - if (!str) - { - ast_debug (1, "[%s] buffer overflow\n", PVT_ID(pvt)); - return -1; - } - memcpy (str, iov[0].iov_base, iov[0].iov_len); - memcpy (str + iov[0].iov_len, iov[1].iov_base, iov[1].iov_len); + memcpy(str, iov[0].iov_base, iov[0].iov_len); + memcpy(str + iov[0].iov_len, iov[1].iov_base, iov[1].iov_len); } else { diff --git a/chan_dongle.c b/chan_dongle.c index 84eef5ca..01d17f7a 100644 --- a/chan_dongle.c +++ b/chan_dongle.c @@ -35,7 +35,6 @@ #include #endif /* HAVE_CONFIG_H */ - #include ASTERISK_FILE_VERSION(__FILE__, "$Rev: " PACKAGE_REVISION " $") @@ -45,10 +44,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Rev: " PACKAGE_REVISION " $") #include #include /* AST_MODULE_LOAD_DECLINE ... */ #include /* ast_timer_open() ast_timer_fd() */ -#if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ -#include -#include -#endif #include /* S_IRUSR | S_IRGRP | S_IROTH */ #include /* struct termios tcgetattr() tcsetattr() */ @@ -58,6 +53,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Rev: " PACKAGE_REVISION " $") #include "ast_compat.h" /* asterisk compatibility fixes */ +#if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ +#include +#include +#endif /* ^13+ */ + #include "chan_dongle.h" #include "at_response.h" /* at_res_t */ #include "at_queue.h" /* struct at_queue_task_cmd at_queue_head_cmd() */ @@ -73,10 +73,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Rev: " PACKAGE_REVISION " $") EXPORT_DEF const char * const dev_state_strs[4] = { "stop", "restart", "remove", "start" }; EXPORT_DEF public_state_t * gpublic; -#if ASTERISK_VERSION_NUM >= 100000 && ASTERISK_VERSION_NUM < 130000 /* 10+..13- */ +#if ASTERISK_VERSION_NUM >= 100000 && ASTERISK_VERSION_NUM < 130000 /* 10-13 */ EXPORT_DEF struct ast_format chan_dongle_format; EXPORT_DEF struct ast_format_cap * chan_dongle_format_cap; -#endif +#endif /* ^10-13 */ static int public_state_init(struct public_state * state); @@ -355,7 +355,7 @@ EXPORT_DEF void clean_read_data(const char * devname, int fd) struct ringbuffer rb; int iovcnt; int t; - + rb_init (&rb, buf, sizeof (buf)); for (t = 0; at_wait(fd, &t); t = 0) { @@ -399,7 +399,7 @@ static void* do_monitor_phone (void* data) ast_copy_string(dev, PVT_ID(pvt), sizeof(dev)); clean_read_data(dev, fd); - + /* schedule dongle initilization */ if (at_enque_initialization (&pvt->sys_chan, CMD_AT)) { @@ -535,39 +535,39 @@ static int pvt_discovery(struct pvt * pvt) ast_copy_string(imei, CONF_UNIQ(pvt, imei), sizeof(imei)); ast_copy_string(imsi, CONF_UNIQ(pvt, imsi), sizeof(imsi)); - ast_debug(3, "[%s] Trying ports discovery for%s%s%s%s\n", - PVT_ID(pvt), - imei[0] == 0 ? "" : " IMEI ", - imei, - imsi[0] == 0 ? "" : " IMSI ", + ast_debug(3, "[%s] Trying ports discovery for%s%s%s%s\n", + PVT_ID(pvt), + imei[0] == 0 ? "" : " IMEI ", + imei, + imsi[0] == 0 ? "" : " IMSI ", imsi ); ast_mutex_unlock (&pvt->lock); //sleep(10); // test resolved = pdiscovery_lookup(devname, imei, imsi, &data_tty, &audio_tty); ast_mutex_lock (&pvt->lock); - + if(resolved) { ast_copy_string (PVT_STATE(pvt, data_tty), data_tty, sizeof (PVT_STATE(pvt, data_tty))); ast_copy_string (PVT_STATE(pvt, audio_tty), audio_tty, sizeof (PVT_STATE(pvt, audio_tty))); ast_free(audio_tty); ast_free(data_tty); - ast_verb (3, "[%s]%s%s%s%s found on data_tty=%s audio_tty=%s\n", - PVT_ID(pvt), - imei[0] == 0 ? "" : " IMEI ", - imei, - imsi[0] == 0 ? "" : " IMSI ", + ast_verb (3, "[%s]%s%s%s%s found on data_tty=%s audio_tty=%s\n", + PVT_ID(pvt), + imei[0] == 0 ? "" : " IMEI ", + imei, + imsi[0] == 0 ? "" : " IMSI ", imsi, - PVT_STATE(pvt, data_tty), + PVT_STATE(pvt, data_tty), PVT_STATE(pvt, audio_tty) ); } else { - ast_debug(3, "[%s] Not found ports for%s%s%s%s\n", - PVT_ID(pvt), - imei[0] == 0 ? "" : " IMEI ", - imei, - imsi[0] == 0 ? "" : " IMSI ", + ast_debug(3, "[%s] Not found ports for%s%s%s%s\n", + PVT_ID(pvt), + imei[0] == 0 ? "" : " IMEI ", + imei, + imsi[0] == 0 ? "" : " IMSI ", imsi ); } @@ -860,17 +860,13 @@ EXPORT_DECL int pvt_enabled(const struct pvt * pvt) #/* */ EXPORT_DEF int ready4voice_call(const struct pvt* pvt, const struct cpvt * current_cpvt, int opts) { - if(!pvt->connected - || - !pvt->initialized - || - !pvt->has_voice - || - !pvt->gsm_registered - || - !pvt_enabled(pvt) - ) + if(!pvt->connected + || !pvt->initialized + || !pvt->has_voice + || !pvt->gsm_registered + || !pvt_enabled(pvt)) { return 0; + } return is_dial_possible2(pvt, opts, current_cpvt); } @@ -1468,29 +1464,20 @@ static int pvt_reconfigure(struct pvt * pvt, const pvt_config_t * settings, rest /* check what changes require starting or stopping */ if(pvt->desired_state != SCONFIG(settings, initstate)) { pvt->desired_state = SCONFIG(settings, initstate); - + rv = pvt_time4restate(pvt); pvt->restart_time = rv ? RESTATE_TIME_NOW : when; } /* check what config changes require restaring */ - else if( - strcmp(UCONFIG(settings, audio_tty), CONF_UNIQ(pvt, audio_tty)) - || - strcmp(UCONFIG(settings, data_tty), CONF_UNIQ(pvt, data_tty)) - || - strcmp(UCONFIG(settings, imei), CONF_UNIQ(pvt, imei)) - || - strcmp(UCONFIG(settings, imsi), CONF_UNIQ(pvt, imsi)) - || - SCONFIG(settings, u2diag) != CONF_SHARED(pvt, u2diag) - || - SCONFIG(settings, resetdongle) != CONF_SHARED(pvt, resetdongle) - || - SCONFIG(settings, smsaspdu) != CONF_SHARED(pvt, smsaspdu) - || - SCONFIG(settings, callwaiting) != CONF_SHARED(pvt, callwaiting) - ) + else if(strcmp(UCONFIG(settings, audio_tty), CONF_UNIQ(pvt, audio_tty)) + || strcmp(UCONFIG(settings, data_tty), CONF_UNIQ(pvt, data_tty)) + || strcmp(UCONFIG(settings, imei), CONF_UNIQ(pvt, imei)) + || strcmp(UCONFIG(settings, imsi), CONF_UNIQ(pvt, imsi)) + || SCONFIG(settings, u2diag) != CONF_SHARED(pvt, u2diag) + || SCONFIG(settings, resetdongle) != CONF_SHARED(pvt, resetdongle) + || SCONFIG(settings, smsaspdu) != CONF_SHARED(pvt, smsaspdu) + || SCONFIG(settings, callwaiting) != CONF_SHARED(pvt, callwaiting)) { /* TODO: schedule restart */ pvt->desired_state = DEV_STATE_RESTARTED; @@ -1656,7 +1643,7 @@ static int load_module() static int public_state_init(struct public_state * state) { int rv = AST_MODULE_LOAD_DECLINE; - + AST_RWLIST_HEAD_INIT(&state->devices); ast_mutex_init(&state->discovery_lock); @@ -1683,7 +1670,7 @@ static int public_state_init(struct public_state * state) } ast_format_cap_add(channel_tech.capabilities, &chan_dongle_format); chan_dongle_format_cap = channel_tech.capabilities; -#endif +#endif /* ^10-13 */ /* register our channel type */ if(ast_channel_register(&channel_tech) == 0) @@ -1698,11 +1685,11 @@ static int public_state_init(struct public_state * state) else { #if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ - ao2_cleanup(channel_tech.capabilities); - channel_tech.capabilities = NULL; + ao2_cleanup(channel_tech.capabilities); + channel_tech.capabilities = NULL; #elif ASTERISK_VERSION_NUM >= 100000 /* 10-13 */ channel_tech.capabilities = ast_format_cap_destroy(channel_tech.capabilities); -#endif +#endif /* ^10-13 */ ast_log (LOG_ERROR, "Unable to register channel class %s\n", channel_tech.type); } discovery_stop(state); @@ -1731,12 +1718,11 @@ static void public_state_fini(struct public_state * state) /* First, take us out of the channel loop */ ast_channel_unregister (&channel_tech); #if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ - ao2_cleanup(channel_tech.capabilities); - channel_tech.capabilities = NULL; - + ao2_cleanup(channel_tech.capabilities); + channel_tech.capabilities = NULL; #elif ASTERISK_VERSION_NUM >= 100000 /* 10-13 */ channel_tech.capabilities = ast_format_cap_destroy(channel_tech.capabilities); -#endif +#endif /* ^10-13 */ /* Unregister the CLI & APP & MANAGER */ @@ -1748,7 +1734,7 @@ static void public_state_fini(struct public_state * state) discovery_stop(state); devices_destroy(state); - + // ast_mutex_destroy(&state->round_robin_mtx); ast_mutex_destroy(&state->discovery_lock); AST_RWLIST_HEAD_DESTROY(&state->devices); @@ -1759,7 +1745,7 @@ static int unload_module() public_state_fini(gpublic); pdiscovery_fini(); - + ast_free(gpublic); gpublic = NULL; return 0; @@ -1785,13 +1771,13 @@ static int reload_module() AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, MODULE_DESCRIPTION, #if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ .support_level = AST_MODULE_SUPPORT_EXTENDED, -#endif +#endif /* ^13+ */ .load = load_module, .unload = unload_module, .reload = reload_module, #if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ .load_pri = AST_MODPRI_CHANNEL_DRIVER, -#endif +#endif /* ^13+ */ ); //AST_MODULE_INFO_STANDARD (ASTERISK_GPL_KEY, MODULE_DESCRIPTION); diff --git a/chan_dongle.h b/chan_dongle.h index a94cfbb0..b6e0436c 100644 --- a/chan_dongle.h +++ b/chan_dongle.h @@ -16,7 +16,7 @@ #include #include -#include +#include "ast_compat.h" /* asterisk compatibility fixes */ #include "mixbuffer.h" /* struct mixbuffer */ //#include "ringbuffer.h" /* struct ringbuffer */ @@ -38,12 +38,12 @@ INLINE_DECL const char * dev_state2str_msg(dev_state_t state) return enum2str(state, states, ITEMS_OF(states)); } -#if ASTERISK_VERSION_NUM >= 100000 && ASTERISK_VERSION_NUM < 130000 /* 10+..13- */ +#if ASTERISK_VERSION_NUM >= 100000 && ASTERISK_VERSION_NUM < 130000 /* 10-13 */ /* Only linear is allowed */ EXPORT_DECL struct ast_format chan_dongle_format; //EXPORT_DEF struct ast_format chan_dongle_format; EXPORT_DECL struct ast_format_cap * chan_dongle_format_cap; -#endif +#endif /* ^10-13 */ typedef enum { RESTATE_TIME_NOW = 0, @@ -131,7 +131,7 @@ typedef struct pvt // char a_read_buf[FRAME_SIZE + AST_FRIENDLY_OFFSET]; /*!< audio read buffer */ // struct ast_frame a_read_frame; /*!< readed frame buffer */ - + char dtmf_digit; /*!< last DTMF digit */ struct timeval dtmf_begin_time; /*!< time of begin of last DTMF digit */ struct timeval dtmf_end_time; /*!< time of end of last DTMF digit */ @@ -139,7 +139,7 @@ typedef struct pvt int timeout; /*!< used to set the timeout for data */ #define DATA_READ_TIMEOUT 10000 /* 10 seconds */ - unsigned long channel_instanse; /*!< number of channels created on this device */ + unsigned long channel_instance; /*!< number of channels created on this device */ unsigned int rings; /*!< ring/ccwa number distributed to at_response_clcc() */ /* device caps */ diff --git a/channel.c b/channel.c index 1875748d..a14bccaf 100644 --- a/channel.c +++ b/channel.c @@ -1,9 +1,9 @@ -/* +/* Copyright (C) 2009 - 2010 - + Artem Makhutov http://www.makhutov.org - + Dmitry Vagin bg @@ -12,10 +12,6 @@ #include #endif /* HAVE_CONFIG_H */ -#ifndef ASTERISK_VERSION_NUM -#error ASTERISK_VERSION_NUM is not set, please supply -D ASTERISK_VERSION_NUM=100501 for version 10.5.1 -#endif - #include #include /* ast_dsp_digitreset() */ #include /* pbx_builtin_setvar_helper() */ @@ -29,7 +25,7 @@ #if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ #include #include -#endif +#endif /* ^13+ */ #include "channel.h" #include "chan_dongle.h" @@ -100,34 +96,45 @@ EXPORT_DEF int channels_loop(struct pvt * pvt, const struct ast_channel * reques /* FIXME: requestor may be just proxy/masquerade for real channel */ // use ast_bridged_channel(chan) ? // use requestor->tech->get_base_channel() ? + struct cpvt *tmp; return (requestor && ast_channel_tech(requestor) == &channel_tech - && ast_channel_tech_pvt(requestor) - && ((struct cpvt*) ast_channel_tech_pvt(requestor))->pvt == pvt) + && (tmp = ast_channel_tech_pvt(requestor)) + && tmp->pvt == pvt) ? 1 : 0; } #if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ -static struct ast_channel * channel_request (attribute_unused const char * type, struct ast_format_cap * cap, const struct ast_assigned_ids * assignedids, const struct ast_channel * requestor, const char * data, int * cause) +static struct ast_channel * channel_request( + attribute_unused const char * type, struct ast_format_cap * cap, + const struct ast_assigned_ids * assignedids, + const struct ast_channel * requestor, const char * data, int * cause) #elif ASTERISK_VERSION_NUM >= 110000 /* 11+ */ -static struct ast_channel * channel_request (attribute_unused const char * type, struct ast_format_cap * cap, const struct ast_channel * requestor, const char * data, int * cause) +static struct ast_channel * channel_request( + attribute_unused const char * type, struct ast_format_cap * cap, + const struct ast_channel * requestor, const char * data, int * cause) #elif ASTERISK_VERSION_NUM >= 100000 /* 10-13 */ -static struct ast_channel * channel_request (attribute_unused const char * type, struct ast_format_cap * cap, const struct ast_channel * requestor, void * data, int * cause) +static struct ast_channel * channel_request( + attribute_unused const char * type, struct ast_format_cap * cap, + const struct ast_channel * requestor, void * data, int * cause) #elif ASTERISK_VERSION_NUM >= 10800 /* 1.8+ */ -// TODO: simplify by move common code to functions -static struct ast_channel * channel_request (attribute_unused const char * type, format_t format, const struct ast_channel * requestor, void * data, int * cause) +static struct ast_channel * channel_request( + attribute_unused const char * type, format_t format, + const struct ast_channel * requestor, void * data, int * cause) #else /* 1.8- */ -/* TODO: add check when request 'holdother' what requestor is not on same device for 1.6 */ -static struct ast_channel * channel_request (attribute_unused const char * type, int format, void * data, int * cause) -#endif +static struct ast_channel * channel_request( + attribute_unused const char * type, int format, void * data, int * cause) +#endif /* ^1.8- */ { +/* TODO: simplify by moving common code to functions */ +/* TODO: add check when request 'holdother' what requestor is not on same device for 1.6 */ #if ASTERISK_VERSION_NUM >= 10800 && ASTERISK_VERSION_NUM < 100000 /* 1.8+ .. 10- */ format_t oldformat; #elif ASTERISK_VERSION_NUM < 10800 /* 1.8- */ int oldformat; const struct ast_channel * requestor = NULL; -#endif +#endif /* ^1.8- */ char * dest_dev; const char * dest_num; struct ast_channel * channel = NULL; @@ -146,23 +153,27 @@ static struct ast_channel * channel_request (attribute_unused const char * type, if (ast_format_cap_iscompatible_format(cap, ast_format_slin) != AST_FORMAT_CMP_EQUAL) #elif ASTERISK_VERSION_NUM >= 100000 /* 10-13 */ if (!ast_format_cap_iscompatible(cap, &chan_dongle_format)) -#else +#else /* 10- */ oldformat = format; format &= AST_FORMAT_SLINEAR; if (!format) -#endif +#endif /* ^10- */ { #if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ struct ast_str *codec_buf = ast_str_alloca(64); - ast_log (LOG_WARNING, "Asked to get a channel of unsupported format '%s'\n", ast_format_cap_get_names(cap, &codec_buf)); + ast_log(LOG_WARNING, "Asked to get a channel of unsupported format '%s'\n", + ast_format_cap_get_names(cap, &codec_buf)); #elif ASTERISK_VERSION_NUM >= 100000 /* 10-13 */ char buf[255]; - ast_log (LOG_WARNING, "Asked to get a channel of unsupported format '%s'\n", ast_getformatname_multiple (buf, 255, cap)); + ast_log(LOG_WARNING, "Asked to get a channel of unsupported format '%s'\n", + ast_getformatname_multiple(buf, 255, cap)); #elif ASTERISK_VERSION_NUM >= 10800 /* 1.8+ */ - ast_log (LOG_WARNING, "Asked to get a channel of unsupported format '%s'\n", ast_getformatname (oldformat)); -#else - ast_log (LOG_WARNING, "Asked to get a channel of unsupported format '%d'\n", oldformat); -#endif + ast_log(LOG_WARNING, "Asked to get a channel of unsupported format '%s'\n", + ast_getformatname(oldformat)); +#else /* 1.8- */ + ast_log(LOG_WARNING, "Asked to get a channel of unsupported format '%d'\n", + oldformat); +#endif /* ^1.8- */ *cause = AST_CAUSE_FACILITY_NOT_IMPLEMENTED; return NULL; } @@ -175,16 +186,19 @@ static struct ast_channel * channel_request (attribute_unused const char * type, #if ASTERISK_VERSION_NUM >= 10800 pvt = find_device_by_resource(dest_dev, opts, requestor, &exists); -#else +#else /* 1.8- */ pvt = find_device_by_resource(dest_dev, opts, NULL, &exists); -#endif +#endif /* ^1.8- */ + if(pvt) { #if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ - channel = new_channel (pvt, AST_STATE_DOWN, NULL, pvt_get_pseudo_call_idx(pvt), CALL_DIR_OUTGOING, CALL_STATE_INIT, NULL, assignedids, requestor); -#else - channel = new_channel (pvt, AST_STATE_DOWN, NULL, pvt_get_pseudo_call_idx(pvt), CALL_DIR_OUTGOING, CALL_STATE_INIT, NULL, requestor); -#endif + channel = new_channel(pvt, AST_STATE_DOWN, NULL, pvt_get_pseudo_call_idx(pvt), + CALL_DIR_OUTGOING, CALL_STATE_INIT, NULL, assignedids, requestor); +#else /* 13- */ + channel = new_channel(pvt, AST_STATE_DOWN, NULL, pvt_get_pseudo_call_idx(pvt), + CALL_DIR_OUTGOING, CALL_STATE_INIT, NULL, requestor); +#endif /* ^13- */ ast_mutex_unlock (&pvt->lock); if(!channel) { @@ -204,10 +218,10 @@ static struct ast_channel * channel_request (attribute_unused const char * type, #/* */ #if ASTERISK_VERSION_NUM >= 110000 /* 11+ */ -static int channel_call (struct ast_channel* channel, const char *dest, attribute_unused int timeout) -#else -static int channel_call (struct ast_channel* channel, char* dest, attribute_unused int timeout) -#endif +static int channel_call(struct ast_channel* channel, const char *dest, attribute_unused int timeout) +#else /* 11- */ +static int channel_call(struct ast_channel* channel, char* dest, attribute_unused int timeout) +#endif /* ^11- */ { struct cpvt* cpvt = ast_channel_tech_pvt(channel); struct pvt* pvt; @@ -251,11 +265,11 @@ static int channel_call (struct ast_channel* channel, char* dest, attribute_unus { if (CONF_SHARED(pvt, callingpres) < 0) { -#if ASTERISK_VERSION_NUM >= 10800 +#if ASTERISK_VERSION_NUM >= 10800 /* 1.8+ */ clir = ast_channel_connected(channel)->id.number.presentation; -#else +#else /* 1.8- */ clir = channel->cid.cid_pres; -#endif +#endif /* ^1.8- */ } else { @@ -487,8 +501,9 @@ static void iov_write(struct pvt* pvt, int fd, struct iovec * iov, int iovcnt) if((errno == EINTR || errno == EAGAIN)) { --count; - if(count != 0) - goto again; + if(count != 0) { + goto again; + } ast_debug (1, "[%s] Deadlock avoided for write!\n", PVT_ID(pvt)); } break; @@ -616,7 +631,7 @@ static void write_conference(struct pvt * pvt, const char * buffer, size_t lengt #else /* 1.8- */ #define subclass_codec subclass #define subclass_integer subclass -#endif +#endif /* ^1.8- */ #/* */ static struct ast_frame* channel_read (struct ast_channel* channel) @@ -661,9 +676,9 @@ static struct ast_frame* channel_read (struct ast_channel* channel) cpvt->a_read_frame.subclass.format = ao2_bump(ast_format_slin); #elif ASTERISK_VERSION_NUM >= 100000 /* 10-13 */ ast_format_copy(&cpvt->a_read_frame.subclass.format, &chan_dongle_format); -#else +#else /* 10- */ cpvt->a_read_frame.subclass_codec = AST_FORMAT_SLINEAR; -#endif +#endif /* ^10- */ cpvt->a_read_frame.data.ptr = cpvt->a_read_buf + AST_FRIENDLY_OFFSET; cpvt->a_read_frame.offset = AST_FRIENDLY_OFFSET; cpvt->a_read_frame.src = AST_MODULE; @@ -733,9 +748,9 @@ static struct ast_frame* channel_read (struct ast_channel* channel) f->subclass_integer = 0; } else if(f->subclass_integer == pvt->dtmf_digit - && + && !ast_tvzero(pvt->dtmf_end_time) - && + && ast_tvdiff_ms(ast_tvnow(), pvt->dtmf_end_time) < CONF_SHARED(pvt, mindtmfinterval)) { ast_debug(1, "[%s] DTMF char %c ignored min interval %d > %ld\n", PVT_ID(pvt), f->subclass_integer, CONF_SHARED(pvt, mindtmfinterval), (long)ast_tvdiff_ms(ast_tvnow(), pvt->dtmf_end_time)); @@ -778,12 +793,15 @@ static int channel_write (struct ast_channel* channel, struct ast_frame* f) int gains[2]; #if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ - if (f->frametype != AST_FRAME_VOICE || ast_format_cmp(f->subclass.format, ast_format_slin) != AST_FORMAT_CMP_EQUAL) + if (f->frametype != AST_FRAME_VOICE + || ast_format_cmp(f->subclass.format, ast_format_slin) != AST_FORMAT_CMP_EQUAL) #elif ASTERISK_VERSION_NUM >= 100000 /* 10-13 */ - if (f->frametype != AST_FRAME_VOICE || f->subclass.format.id != AST_FORMAT_SLINEAR) + if (f->frametype != AST_FRAME_VOICE + || f->subclass.format.id != AST_FORMAT_SLINEAR) #else /* 10- */ - if (f->frametype != AST_FRAME_VOICE || f->subclass_codec != AST_FORMAT_SLINEAR) -#endif + if (f->frametype != AST_FRAME_VOICE + || f->subclass_codec != AST_FORMAT_SLINEAR) +#endif /* ^10- */ { return 0; } @@ -816,9 +834,9 @@ static int channel_write (struct ast_channel* channel, struct ast_frame* f) { #if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ RAII_VAR(struct ast_channel *, bridged, ast_channel_bridge_peer(channel), ast_channel_cleanup); -#else - struct ast_channel* bridged = ast_bridged_channel(channel); -#endif +#else /* 13- */ + struct ast_channel *bridged = ast_bridged_channel(channel); +#endif /* ^13- */ struct cpvt *tmp_cpvt; CPVT_SET_FLAGS(cpvt, CALL_FLAG_BRIDGE_CHECK); @@ -840,8 +858,8 @@ static int channel_write (struct ast_channel* channel, struct ast_frame* f) { if(f->datalen) { - /** try to minimize of ast_frame_adjust_volume() calls: - * one hand we must obey txgain but with other divide gain to + /** try to minimize of ast_frame_adjust_volume() calls: + * one hand we must obey txgain but with other divide gain to * number of mixed channels. In some cases one call of ast_frame_adjust_volume() enough */ @@ -973,9 +991,9 @@ static int channel_fixup (struct ast_channel* oldchannel, struct ast_channel* ne #/* FIXME: must modify in conjuction with state on call not whole device? */ #if ASTERISK_VERSION_NUM >= 110000 /* 11+ */ static int channel_devicestate (const char *data) -#else +#else /* 11- */ static int channel_devicestate (void* data) -#endif +#endif /* ^11- */ { char* device; struct pvt* pvt; @@ -1172,7 +1190,7 @@ EXPORT_DEF void change_channel_state(struct cpvt * cpvt, unsigned newstate, int static void set_channel_vars(struct pvt* pvt, struct ast_channel* channel) { unsigned idx; - channel_var_t dev_vars[] = + channel_var_t dev_vars[] = { { "DONGLENAME", PVT_ID(pvt) }, { "DONGLEPROVIDER", pvt->provider_name }, @@ -1183,25 +1201,29 @@ static void set_channel_vars(struct pvt* pvt, struct ast_channel* channel) #if ASTERISK_VERSION_NUM >= 110000 /* 11+ */ ast_channel_language_set(channel, CONF_SHARED(pvt, language)); -#else +#else /* 11- */ //TODO uncomment and fix //ast_string_field_set (channel, language, CONF_SHARED(pvt, language); -#endif +#endif /* ^11- */ for(idx = 0; idx < ITEMS_OF(dev_vars); ++idx) pbx_builtin_setvar_helper (channel, dev_vars[idx].name, dev_vars[idx].value); - } -#/* NOTE: called from device and current levels with locked pvt */ +/* NOTE: called from device and current levels with locked pvt */ #if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ -EXPORT_DEF struct ast_channel* new_channel (struct pvt* pvt, int ast_state, const char* cid_num, int call_idx, unsigned dir, call_state_t state, const char * dnid, const struct ast_assigned_ids *assignedids, attribute_unused const struct ast_channel * requestor) -#else -EXPORT_DEF struct ast_channel* new_channel (struct pvt* pvt, int ast_state, const char* cid_num, int call_idx, unsigned dir, call_state_t state, const char * dnid, attribute_unused const struct ast_channel * requestor) -#endif +EXPORT_DEF struct ast_channel* new_channel( + struct pvt* pvt, int ast_state, const char* cid_num, int call_idx, + unsigned dir, call_state_t state, const char * dnid, + const struct ast_assigned_ids *assignedids, + attribute_unused const struct ast_channel * requestor) +#else /* 13- */ +EXPORT_DEF struct ast_channel* new_channel( + struct pvt* pvt, int ast_state, const char* cid_num, int call_idx, + unsigned dir, call_state_t state, const char * dnid, + attribute_unused const struct ast_channel * requestor) +#endif /* ^13- */ { -// struct ast_channel * channel = new_channel (pvt, AST_STATE_RING, number, call_idx, CALL_DIR_INCOMING, state, pvt->has_subscriber_number ? pvt->subscriber_number : CONF_SHARED(pvt, exten), $ - struct ast_channel* channel; struct cpvt * cpvt; @@ -1209,16 +1231,29 @@ EXPORT_DEF struct ast_channel* new_channel (struct pvt* pvt, int ast_state, cons if (cpvt) { #if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ - channel = ast_channel_alloc (1, ast_state, cid_num, PVT_ID(pvt), NULL, dnid, CONF_SHARED(pvt, context), assignedids, requestor, 0, "%s/%s-%02u%08lx", channel_tech.type, PVT_ID(pvt), call_idx, pvt->channel_instanse); + channel = ast_channel_alloc( + 1, ast_state, cid_num, PVT_ID(pvt), NULL, dnid, + CONF_SHARED(pvt, context), assignedids, requestor, 0, + "%s/%s-%02u%08lx", channel_tech.type, PVT_ID(pvt), + call_idx, pvt->channel_instance); #elif ASTERISK_VERSION_NUM >= 10800 /* 1.8+ */ - channel = ast_channel_alloc (1, ast_state, cid_num, PVT_ID(pvt), NULL, dnid, CONF_SHARED(pvt, context), requestor ? ast_channel_linkedid(requestor): NULL, 0, "%s/%s-%02u%08lx", channel_tech.type, PVT_ID(pvt), call_idx, pvt->channel_instanse); -#else - channel = ast_channel_alloc (1, ast_state, cid_num, PVT_ID(pvt), NULL, dnid, CONF_SHARED(pvt, context), 0, "%s/%s-%02u%08lx", channel_tech.type, PVT_ID(pvt), call_idx, pvt->channel_instanse); -#endif + channel = ast_channel_alloc( + 1, ast_state, cid_num, PVT_ID(pvt), NULL, dnid, + CONF_SHARED(pvt, context), + requestor ? ast_channel_linkedid(requestor) : NULL, 0, + "%s/%s-%02u%08lx", channel_tech.type, PVT_ID(pvt), + call_idx, pvt->channel_instance); +#else /* 1.8- */ + channel = ast_channel_alloc( + 1, ast_state, cid_num, PVT_ID(pvt), NULL, dnid, + CONF_SHARED(pvt, context), 0, + "%s/%s-%02u%08lx", channel_tech.type, PVT_ID(pvt), + call_idx, pvt->channel_instance); +#endif /* ^1.8- */ if (channel) { cpvt->channel = channel; - pvt->channel_instanse++; + pvt->channel_instance++; ast_channel_tech_pvt_set(channel, cpvt); ast_channel_tech_set(channel, &channel_tech); @@ -1239,7 +1274,7 @@ EXPORT_DEF struct ast_channel* new_channel (struct pvt* pvt, int ast_state, cons channel->nativeformats = AST_FORMAT_SLINEAR; channel->writeformat = AST_FORMAT_SLINEAR; channel->readformat = AST_FORMAT_SLINEAR; -#endif +#endif /* ^10- */ if (ast_state == AST_STATE_RING) { @@ -1329,14 +1364,14 @@ EXPORT_DEF void start_local_channel (struct pvt* pvt, const char* exten, const c snprintf (channel_name, sizeof (channel_name), "%s@%s", exten, CONF_SHARED(pvt, context)); #if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ - channel = ast_request ("Local", channel_tech.capabilities, NULL, NULL, channel_name, &cause); + channel = ast_request("Local", channel_tech.capabilities, NULL, NULL, channel_name, &cause); #elif ASTERISK_VERSION_NUM >= 100000 /* 10-13 */ - channel = ast_request ("Local", chan_dongle_format_cap, NULL, channel_name, &cause); + channel = ast_request("Local", chan_dongle_format_cap, NULL, channel_name, &cause); #elif ASTERISK_VERSION_NUM >= 10800 /* 1.8+ */ - channel = ast_request ("Local", AST_FORMAT_AUDIO_MASK, NULL, channel_name, &cause); + channel = ast_request("Local", AST_FORMAT_AUDIO_MASK, NULL, channel_name, &cause); #else /* 1.8- */ - channel = ast_request ("Local", AST_FORMAT_AUDIO_MASK, channel_name, &cause); -#endif + channel = ast_request("Local", AST_FORMAT_AUDIO_MASK, channel_name, &cause); +#endif /* ^1.8- */ if (channel) { set_channel_vars(pvt, channel); @@ -1479,7 +1514,7 @@ static int channel_func_write(struct ast_channel* channel, const char* function, { pvt_dsp_setup(pvt, PVT_ID(pvt), val); } - + ast_mutex_unlock(&cpvt->pvt->lock); } else @@ -1500,7 +1535,7 @@ EXPORT_DEF struct ast_channel_tech channel_tech = .description = MODULE_DESCRIPTION, #if ASTERISK_VERSION_NUM < 100000 /* 10- */ .capabilities = AST_FORMAT_SLINEAR, -#endif +#endif /* ^10- */ .requester = channel_request, .call = channel_call, .hangup = channel_hangup, diff --git a/channel.h b/channel.h index 702e3f23..3ad75a2b 100644 --- a/channel.h +++ b/channel.h @@ -7,6 +7,8 @@ #include #include /* enum ast_control_frame_type */ +#include "ast_compat.h" /* asterisk compatibility fixes */ + #include "export.h" /* EXPORT_DECL EXPORT_DEF */ @@ -22,10 +24,17 @@ struct cpvt; EXPORT_DECL struct ast_channel_tech channel_tech; #if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ -EXPORT_DECL struct ast_channel* new_channel (struct pvt * pvt, int ast_state, const char * cid_num, int call_idx, unsigned dir, unsigned state, const char * exten, const struct ast_assigned_ids *assignedids, const struct ast_channel * requestor); -#else -EXPORT_DECL struct ast_channel* new_channel (struct pvt * pvt, int ast_state, const char * cid_num, int call_idx, unsigned dir, unsigned state, const char * exten, const struct ast_channel * requestor); -#endif +EXPORT_DECL struct ast_channel* new_channel( + struct pvt * pvt, int ast_state, const char * cid_num, int call_idx, + unsigned dir, unsigned state, const char * exten, + const struct ast_assigned_ids *assignedids, + const struct ast_channel * requestor); +#else /* 13- */ +EXPORT_DECL struct ast_channel* new_channel( + struct pvt * pvt, int ast_state, const char * cid_num, int call_idx, + unsigned dir, unsigned state, const char * exten, + const struct ast_channel * requestor); +#endif /* ^13- */ EXPORT_DECL int queue_control_channel (struct cpvt * cpvt, enum ast_control_frame_type control); EXPORT_DECL int queue_hangup (struct ast_channel * channel, int hangupcause); EXPORT_DECL void start_local_channel (struct pvt * pvt, const char * exten, const char * number, channel_var_t * vars); diff --git a/char_conv.c b/char_conv.c index 2a4be705..f1f47219 100644 --- a/char_conv.c +++ b/char_conv.c @@ -78,7 +78,7 @@ static ssize_t hexstr_to_8bitchars (const char* in, size_t in_length, char* out, return -ENOMEM; } out_size = in_length; - + for (; in_length; --in_length) { d1 = parse_hexdigit(*in++); @@ -105,7 +105,7 @@ static ssize_t chars8bit_to_hexstr (const char* in, size_t in_length, char* out, return -1; } out_size = in_length * 2; - + for (; in_length; --in_length, ++in2) { *out++ = hex_table[*in2 >> 4]; diff --git a/cli.c b/cli.c index 4c255625..b13e222a 100644 --- a/cli.c +++ b/cli.c @@ -1,9 +1,9 @@ -/* +/* Copyright (C) 2009 - 2010 - + Artem Makhutov http://www.makhutov.org - + Dmitry Vagin bg @@ -12,15 +12,12 @@ #include #endif /* HAVE_CONFIG_H */ -#ifndef ASTERISK_VERSION_NUM -#error ASTERISK_VERSION_NUM is not set, please supply \ - -D ASTERISK_VERSION_NUM=100501 for version 10.5.1 -#endif - #include #include /* struct ast_cli_entry; struct ast_cli_args */ #include /* ast_describe_caller_presentation() */ +#include "ast_compat.h" /* asterisk compatibility fixes */ + #include "cli.h" #include "chan_dongle.h" /* devices */ #include "helpers.h" /* ITEMS_OF() send_ccwa_set() send_reset() send_sms() send_ussd() */ @@ -229,7 +226,7 @@ static char* cli_show_device_state (struct ast_cli_entry* e, int cmd, struct ast ast_cli (a->fd, " Current device state : %s\n", dev_state2str(pvt->current_state) ); ast_cli (a->fd, " Desired device state : %s\n", dev_state2str(pvt->desired_state) ); ast_cli (a->fd, " When change state : %s\n", restate2str_msg(pvt->restart_time) ); - + ast_cli (a->fd, " Calls/Channels : %u\n", PVT_STATE(pvt, chansno)); ast_cli (a->fd, " Active : %u\n", PVT_STATE(pvt, chan_count[CALL_STATE_ACTIVE])); ast_cli (a->fd, " Held : %u\n", PVT_STATE(pvt, chan_count[CALL_STATE_ONHOLD])); @@ -337,10 +334,10 @@ static char* cli_show_device_statistics (struct ast_cli_entry* e, int cmd, struc /* ast_cli (a->fd, " ACD : %d\n", getACD( - PVT_STAT(pvt, calls_answered[CALL_DIR_OUTGOING]) - + PVT_STAT(pvt, calls_answered[CALL_DIR_INCOMING]), + PVT_STAT(pvt, calls_answered[CALL_DIR_OUTGOING]) + + PVT_STAT(pvt, calls_answered[CALL_DIR_INCOMING]), - PVT_STAT(pvt, calls_duration[CALL_DIR_OUTGOING]) + PVT_STAT(pvt, calls_duration[CALL_DIR_OUTGOING]) + PVT_STAT(pvt, calls_duration[CALL_DIR_INCOMING]) ) ); @@ -556,11 +553,11 @@ static char * cli_pdu(struct ast_cli_entry * e, int cmd, struct ast_cli_args * a return CLI_SUCCESS; } -#if ASTERISK_VERSION_NUM >= 10800 +#if ASTERISK_VERSION_NUM >= 10800 /* 1.8+ */ typedef const char * const * ast_cli_complete2_t; -#else +#else /* 1.8- */ typedef char * const * ast_cli_complete2_t; -#endif +#endif /* ^1.8- */ static char* cli_ccwa_set (struct ast_cli_entry* e, int cmd, struct ast_cli_args* a) { @@ -843,7 +840,7 @@ static char * cli_discovery(struct ast_cli_entry * e, int cmd, struct ast_cli_ar const char * imsi; int imeilen; int imsilen; - + switch (cmd) { case CLI_INIT: e->command = "dongle discovery"; @@ -908,7 +905,7 @@ static char * cli_discovery(struct ast_cli_entry * e, int cmd, struct ast_cli_ar } pdiscovery_list_end(); AST_RWLIST_UNLOCK(&gpublic->devices); - + return CLI_SUCCESS; } diff --git a/cpvt.c b/cpvt.c index 762d9fc1..e0dc9dba 100644 --- a/cpvt.c +++ b/cpvt.c @@ -65,7 +65,7 @@ EXPORT_DEF struct cpvt * cpvt_alloc(struct pvt * pvt, int call_idx, unsigned dir pvt_on_create_1st_channel(pvt); PVT_STATE(pvt, chansno)++; PVT_STATE(pvt, chan_count[cpvt->state])++; - + ast_debug (3, "[%s] create cpvt for call_idx %d dir %d state '%s'\n", PVT_ID(pvt), call_idx, dir, call_state2str(state)); diff --git a/dc_config.c b/dc_config.c index a57b00e4..806f789c 100644 --- a/dc_config.c +++ b/dc_config.c @@ -8,7 +8,7 @@ #include "dc_config.h" #include /* ast_parse_caller_presentation() */ -static struct ast_jb_conf jbconf_default = +static struct ast_jb_conf jbconf_default = { .flags = 0, .max_size = -1, @@ -22,7 +22,7 @@ static const char * const dtmf_values[] = { "off", "inband", "relax" }; EXPORT_DEF int dc_dtmf_str2setting(const char * value) { - return str2enum(value, dtmf_values, ITEMS_OF(dtmf_values)); + return str2enum(value, dtmf_values, ITEMS_OF(dtmf_values)); } EXPORT_DEF const char * dc_dtmf_setting2str(dc_dtmf_setting_t dtmf) diff --git a/helpers.c b/helpers.c index 163fb7af..d5948c28 100644 --- a/helpers.c +++ b/helpers.c @@ -1,9 +1,9 @@ -/* +/* Copyright (C) 2009 - 2010 - + Artem Makhutov http://www.makhutov.org - + Dmitry Vagin bg diff --git a/manager.c b/manager.c index 0f9a0ca6..f30ec55a 100644 --- a/manager.c +++ b/manager.c @@ -3,12 +3,12 @@ #endif /* HAVE_CONFIG_H */ #ifdef BUILD_MANAGER /* no manager, no copyright */ -/* +/* Copyright (C) 2009 - 2010 - + Artem Makhutov http://www.makhutov.org - + Dmitry Vagin bg @@ -20,6 +20,8 @@ #include /* ast_strlen_zero() */ #include /* ast_describe_caller_presentation */ +#include "ast_compat.h" /* asterisk compatibility fixes */ + #include "manager.h" #include "chan_dongle.h" /* devices */ #include "helpers.h" /* ITEMS_OF() send_ccwa_set() send_reset() send_sms() send_ussd() */ @@ -118,7 +120,7 @@ static int manager_show_devices (struct mansession* s, const struct message* m) astman_append (s, "Event: DongleShowDevicesComplete\r\n"); if(!ast_strlen_zero (id)) astman_append (s, "ActionID: %s\r\n", id); - astman_append (s, + astman_append (s, "EventList: Complete\r\n" "ListItems: %zu\r\n" "\r\n", @@ -176,7 +178,7 @@ static int manager_send_sms (struct mansession* s, const struct message* m) const char* msg; int status; void * msgid; - + if (ast_strlen_zero (device)) { astman_send_error (s, m, "Device not specified"); @@ -218,7 +220,7 @@ static int manager_send_pdu (struct mansession* s, const struct message* m) const char* msg; int status; void * msgid; - + if (ast_strlen_zero (device)) { astman_send_error (s, m, "Device not specified"); @@ -342,11 +344,11 @@ static char * espace_newlines(const char * text) escaped[j++] = 'n'; } else { escaped[j++] = text[i]; - } + } } escaped[j] = 0; } - + return escaped; } @@ -605,13 +607,13 @@ static const struct dongle_manager const char* name; const char* brief; const char* desc; -} dcm[] = +} dcm[] = { { - manager_show_devices, + manager_show_devices, EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, - "DongleShowDevices", - "List Dongle devices", + "DongleShowDevices", + "List Dongle devices", "Description: Lists Dongle devices in text format with details on current status.\n\n" "DongleShowDevicesComplete.\n" "Variables:\n" @@ -619,9 +621,9 @@ static const struct dongle_manager " Device: Optional name of device.\n" }, { - manager_send_ussd, + manager_send_ussd, EVENT_FLAG_CALL, - "DongleSendUSSD", + "DongleSendUSSD", "Send a ussd command to the dongle.", "Description: Send a ussd message to a dongle.\n\n" "Variables: (Names marked with * are required)\n" @@ -630,10 +632,10 @@ static const struct dongle_manager " *USSD: The ussd code that will be send to the device.\n" }, { - manager_send_sms, + manager_send_sms, EVENT_FLAG_CALL, - "DongleSendSMS", - "Send a SMS message.", + "DongleSendSMS", + "Send a SMS message.", "Description: Send a SMS message from a dongle.\n\n" "Variables: (Names marked with * are required)\n" " ActionID: Action ID for this transaction. Will be returned.\n" @@ -642,10 +644,10 @@ static const struct dongle_manager " *Message: The SMS message that will be send.\n" }, { - manager_send_pdu, + manager_send_pdu, EVENT_FLAG_CALL, - "DongleSendPDU", - "Send a PDU of message.", + "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" @@ -653,9 +655,9 @@ static const struct dongle_manager " *PDU: The PDU of SMS.\n" }, { - manager_ccwa_set, + manager_ccwa_set, EVENT_FLAG_CONFIG, - "DongleSetCCWA", + "DongleSetCCWA", "Enable/Disabled Call-Waiting on a dongle.", "Description: Enable/Disabled Call-Waiting on a dongle.\n\n" "Variables: (Names marked with * are required)\n" @@ -666,8 +668,8 @@ static const struct dongle_manager { manager_reset, EVENT_FLAG_SYSTEM | EVENT_FLAG_CONFIG, - "DongleReset", - "Reset a dongle.", + "DongleReset", + "Reset a dongle.", "Description: Reset a dongle.\n\n" "Variables: (Names marked with * are required)\n" " ActionID: Action ID for this transaction. Will be returned.\n" @@ -676,8 +678,8 @@ static const struct dongle_manager { manager_restart, EVENT_FLAG_SYSTEM | EVENT_FLAG_CONFIG, - "DongleRestart", - "Restart a dongle.", + "DongleRestart", + "Restart a dongle.", "Description: Restart a dongle.\n\n" "Variables: (Names marked with * are required)\n" " ActionID: Action ID for this transaction. Will be returned.\n" @@ -687,8 +689,8 @@ static const struct dongle_manager { manager_stop, EVENT_FLAG_SYSTEM | EVENT_FLAG_CONFIG, - "DongleStop", - "Stop a dongle.", + "DongleStop", + "Stop a dongle.", "Description: Stop a dongle.\n\n" "Variables: (Names marked with * are required)\n" " ActionID: Action ID for this transaction. Will be returned.\n" @@ -698,8 +700,8 @@ static const struct dongle_manager { manager_start, EVENT_FLAG_SYSTEM | EVENT_FLAG_CONFIG, - "DongleStart", - "Start a dongle.", + "DongleStart", + "Start a dongle.", "Description: Start a dongle.\n\n" "Variables: (Names marked with * are required)\n" " ActionID: Action ID for this transaction. Will be returned.\n" @@ -708,8 +710,8 @@ static const struct dongle_manager { manager_remove, EVENT_FLAG_SYSTEM | EVENT_FLAG_CONFIG, - "DongleRemove", - "Remove a dongle.", + "DongleRemove", + "Remove a dongle.", "Description: Remove a dongle.\n\n" "Variables: (Names marked with * are required)\n" " ActionID: Action ID for this transaction. Will be returned.\n" @@ -719,8 +721,8 @@ static const struct dongle_manager { manager_reload, EVENT_FLAG_SYSTEM | EVENT_FLAG_CONFIG, - "DongleReload", - "Reload a module configuration.", + "DongleReload", + "Reload a module configuration.", "Description: Reload the module configuration.\n\n" "Variables: (Names marked with * are required)\n" " ActionID: Action ID for this transaction. Will be returned.\n" @@ -733,7 +735,7 @@ EXPORT_DEF void manager_register() unsigned i; #if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ struct ast_module* module = self_module(); -#endif +#endif /* ^13+ */ for(i = 0; i < ITEMS_OF(dcm); i++) { @@ -742,9 +744,9 @@ EXPORT_DEF void manager_register() module, dcm[i].brief, dcm[i].desc); #elif ASTERISK_VERSION_NUM >= 110000 /* 11+ */ ast_manager_register2 (dcm[i].name, dcm[i].authority, dcm[i].func, NULL, dcm[i].brief, dcm[i].desc); -#else +#else /* 11- */ ast_manager_register2 (dcm[i].name, dcm[i].authority, dcm[i].func, dcm[i].brief, dcm[i].desc); -#endif +#endif /* ^11- */ } } diff --git a/mixbuffer.c b/mixbuffer.c index ef97c7c6..b5898a91 100644 --- a/mixbuffer.c +++ b/mixbuffer.c @@ -47,7 +47,7 @@ static inline size_t mixb_mix_write(struct mixbuffer * mb, struct mixstream * st /* save global state */ size_t save_write = mb->rb.write; size_t save_used = mb->rb.used; - + /* load local state */ mb->rb.write = stream->write; mb->rb.used = stream->used; diff --git a/pdiscovery.c b/pdiscovery.c index f5576e80..0ac2877e 100644 --- a/pdiscovery.c +++ b/pdiscovery.c @@ -1,4 +1,4 @@ -/*1 +/* Copyright (C) 2011 bg */ #ifdef HAVE_CONFIG_H @@ -20,7 +20,7 @@ #include "manager.h" /* manager_event_message_raw() */ /* -static const char sys_bus_usb_drivers_usb[] = "/sys/bus/usb/drivers/usb"; +static const char sys_bus_usb_drivers_usb[] = "/sys/bus/usb/drivers/usb"; */ static const char sys_bus_usb_devices[] = "/sys/bus/usb/devices"; @@ -94,7 +94,7 @@ static int ports_copy(struct pdiscovery_ports * dst, const struct pdiscovery_por if(dst->ports[i] == NULL) return 0; } - return i; + return i; } #/* */ @@ -114,12 +114,12 @@ static void info_free(struct pdiscovery_result * res) if(res->imsi) { ast_free(res->imsi); res->imsi = NULL; - } + } if(res->imei) { ast_free(res->imei); res->imei = NULL; - } + } } #/* */ @@ -152,7 +152,7 @@ static void cache_item_update(struct pdiscovery_cache_item * item, const struct { info_free(&item->res); info_copy(&item->res, res); - + item->status = status; item->validtill = ast_tvnow(); @@ -171,7 +171,7 @@ static struct pdiscovery_cache_item * cache_item_create(const struct pdiscovery_ item = NULL; } } - + return item; } @@ -517,11 +517,8 @@ static int pdiscovery_handle_response(const struct pdiscovery_request * req, con len--; if(iovcnt == 2) { str = alloca(len + 1); - if(!str) { - return 1; - memcpy(str , iov[0].iov_base, iov[0].iov_len); + memcpy(str, iov[0].iov_base, iov[0].iov_len); memcpy(str + iov[0].iov_len, iov[1].iov_base, iov[1].iov_len); - } } else { str = iov[0].iov_base; } @@ -606,7 +603,7 @@ static int pdiscovery_get_info(const char * port, const struct pdiscovery_reques unsigned want_imei = req->imei && res->imei == NULL; // 1 && 0 unsigned want_imsi = req->imsi && res->imsi == NULL; // 1 && 1 unsigned cmd = want_map[want_imei][want_imsi]; - + /* clean queue first ? */ fail = pdiscovery_do_cmd(req, fd, port, cmds[cmd].cmd, cmds[cmd].length, res); closetty(fd, &lock_file); @@ -627,7 +624,7 @@ static int pdiscovery_get_info_cached(const char * port, const struct pdiscovery } else { ast_debug(4, "[%s discovery] %s use cached IMEI %s IMSI %s failed %d\n", req->name, port, S_OR(res->imei, ""), S_OR(res->imsi, ""), fail); } - + return fail; } @@ -689,11 +686,11 @@ static int pdiscovery_check_device(const char * name, int len, const char * subd device = pdiscovery_lookup_ids(req->name, name2, len2); if(device) { -// ast_debug(4, "[%s discovery] should ports <-> interfaces map for %04x:%04x modem=%02x voice=%02x data=%02x\n", - ast_debug(4, "[%s discovery] should ports <-> interfaces map for %04x:%04x voice=%02x data=%02x\n", +// ast_debug(4, "[%s discovery] should ports <-> interfaces map for %04x:%04x modem=%02x voice=%02x data=%02x\n", + ast_debug(4, "[%s discovery] should ports <-> interfaces map for %04x:%04x voice=%02x data=%02x\n", req->name, - device->vendor_id, - device->product_id, + device->vendor_id, + device->product_id, // device->interfaces[INTERFACE_TYPE_COM], device->interfaces[INTERFACE_TYPE_VOICE], device->interfaces[INTERFACE_TYPE_DATA] @@ -750,7 +747,7 @@ EXPORT_DEF int pdiscovery_lookup(const char * devname, const char * imei, const int found; struct pdiscovery_result res; const struct pdiscovery_request req = { - devname, + devname, ((imei && imei[0]) ? imei : NULL), ((imsi && imsi[0]) ? imsi : NULL), }; @@ -771,9 +768,9 @@ EXPORT_DEF const struct pdiscovery_result * pdiscovery_list_begin(const struct p const struct pdiscovery_cache_item * item; struct pdiscovery_result res; const struct pdiscovery_request req = { - "list", - "ANY", - "ANY", + "list", + "ANY", + "ANY", }; memset(&res, 0, sizeof(res)); diff --git a/pdu.c b/pdu.c index e0bdab78..be840e01 100644 --- a/pdu.c +++ b/pdu.c @@ -406,7 +406,7 @@ static int pdu_parse_byte(char ** digits2hex, size_t * length) } /*! - * \brief Store number in PDU + * \brief Store number in PDU * \param buffer -- pointer to place where number will be stored, CALLER MUST be provide length + 2 bytes of buffer * \param number -- phone number w/o leading '+' * \param length -- length of number @@ -431,10 +431,10 @@ static int pdu_store_number(char* buffer, const char* number, unsigned length) } /* -failed parse 07 91 97 62 02 00 01 F9 44 14 D0 F7 FB DD D5 2E 9F C3 E6 B7 1B 0008117050815073618C0500037A020100680066006C0067006800200066006800670020006800640066006A006C006700680066006400680067000A002F00200415043604350434043D04350432043D044B04390020043B04380447043D044B043900200433043E0440043E0441043A043E043F003A0020002A003500300035002300360023002000200028003300200440002F0441 +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 +failed parse 07 91 97 30 07 11 11 F1 04 14 D0 D9B09B5CC637DFEE721E0008117020616444617E041A043E04340020043F043E04340442043204350440043604340435043D0438044F003A00200036003900320037002E0020041D0438043A043E043C04430020043D043500200441043E043E043104490430043904420435002C002004320432043504340438044204350020043D0430002004410430043904420435002E ^^ not a international format */ #/* reverse of pdu_store_number() */ @@ -673,170 +673,170 @@ EXPORT_DEF const char * pdu_parse(char ** pdu, size_t tpdu_length, char * oa, si int field_len = pdu_parse_sca(pdu, &pdu_length); if(field_len > 0) { - if(tpdu_length * 2 == pdu_length) - { - int pdu_type = pdu_parse_byte(pdu, &pdu_length); - if(pdu_type >= 0) + if(tpdu_length * 2 == pdu_length) { - /* TODO: also handle PDUTYPE_MTI_SMS_SUBMIT_REPORT and PDUTYPE_MTI_SMS_STATUS_REPORT */ - if(PDUTYPE_MTI(pdu_type) == PDUTYPE_MTI_SMS_DELIVER) + int pdu_type = pdu_parse_byte(pdu, &pdu_length); + if(pdu_type >= 0) { - int oa_digits = pdu_parse_byte(pdu, &pdu_length); - if(oa_digits > 0) + /* TODO: also handle PDUTYPE_MTI_SMS_SUBMIT_REPORT and PDUTYPE_MTI_SMS_STATUS_REPORT */ + if(PDUTYPE_MTI(pdu_type) == PDUTYPE_MTI_SMS_DELIVER) { - int oa_toa; - field_len = pdu_parse_number(pdu, &pdu_length, oa_digits, &oa_toa, oa, oa_len); - if(field_len > 0) + int oa_digits = pdu_parse_byte(pdu, &pdu_length); + if(oa_digits > 0) { - int pid = pdu_parse_byte(pdu, &pdu_length); - *oa_enc = STR_ENCODING_7BIT; - if(pid >= 0) + int oa_toa; + field_len = pdu_parse_number(pdu, &pdu_length, oa_digits, &oa_toa, oa, oa_len); + if(field_len > 0) { - /* TODO: support other types of messages */ - if(pid == PDU_PID_SMS) - { - int dcs = pdu_parse_byte(pdu, &pdu_length); - if(dcs >= 0) + int pid = pdu_parse_byte(pdu, &pdu_length); + *oa_enc = STR_ENCODING_7BIT; + if(pid >= 0) { - // TODO: support compression - if( PDU_DCS_76(dcs) == PDU_DCS_76_00 - && - PDU_DCS_COMPRESSION(dcs) == PDU_DCS_NOT_COMPESSED - && - ( - PDU_DCS_ALPABET(dcs) == PDU_DCS_ALPABET_7BIT - || - PDU_DCS_ALPABET(dcs) == PDU_DCS_ALPABET_8BIT - || - PDU_DCS_ALPABET(dcs) == PDU_DCS_ALPABET_UCS2 - ) - ) - { - int ts = pdu_parse_timestamp(pdu, &pdu_length); - *msg_enc = pdu_dcs_alpabet2encoding(PDU_DCS_ALPABET(dcs)); - if(ts >= 0) + /* TODO: support other types of messages */ + if(pid == PDU_PID_SMS) { - int udl = pdu_parse_byte(pdu, &pdu_length); - if(udl >= 0) + int dcs = pdu_parse_byte(pdu, &pdu_length); + if(dcs >= 0) { - /* calculate number of octets in UD */ - if(PDU_DCS_ALPABET(dcs) == PDU_DCS_ALPABET_7BIT) - udl = ((udl + 1) * 7) >> 3; - if((size_t)udl * 2 == pdu_length) + // TODO: support compression + if( PDU_DCS_76(dcs) == PDU_DCS_76_00 + && + PDU_DCS_COMPRESSION(dcs) == PDU_DCS_NOT_COMPESSED + && + ( + PDU_DCS_ALPABET(dcs) == PDU_DCS_ALPABET_7BIT + || + PDU_DCS_ALPABET(dcs) == PDU_DCS_ALPABET_8BIT + || + PDU_DCS_ALPABET(dcs) == PDU_DCS_ALPABET_UCS2 + ) + ) { - if(PDUTYPE_UDHI(pdu_type) == PDUTYPE_UDHI_HAS_HEADER) + int ts = pdu_parse_timestamp(pdu, &pdu_length); + *msg_enc = pdu_dcs_alpabet2encoding(PDU_DCS_ALPABET(dcs)); + if(ts >= 0) { - /* TODO: implement header parse */ - int udhl = pdu_parse_byte(pdu, &pdu_length); - if(udhl >= 0) + int udl = pdu_parse_byte(pdu, &pdu_length); + if(udl >= 0) { - /* NOTE: UDHL count octets no need calculation */ - if(pdu_length >= (size_t)(udhl * 2)) + /* calculate number of octets in UD */ + if(PDU_DCS_ALPABET(dcs) == PDU_DCS_ALPABET_7BIT) + udl = ((udl + 1) * 7) >> 3; + if((size_t)udl * 2 == pdu_length) { - /* skip UDH */ - *pdu += udhl * 2; - pdu_length -= udhl * 2; + if(PDUTYPE_UDHI(pdu_type) == PDUTYPE_UDHI_HAS_HEADER) + { + /* TODO: implement header parse */ + int udhl = pdu_parse_byte(pdu, &pdu_length); + if(udhl >= 0) + { + /* NOTE: UDHL count octets no need calculation */ + if(pdu_length >= (size_t)(udhl * 2)) + { + /* skip UDH */ + *pdu += udhl * 2; + pdu_length -= udhl * 2; + } + else + { + err = "Invalid UDH"; + } + } + else + { + err = "Can't parse UDHL"; + } + } + /* save message */ + *msg = *pdu; } else { - err = "Invalid UDH"; + *pdu -= 2; + err = "UDL not match with UD length"; } } else { - err = "Can't parse UDHL"; + err = "Can't parse UDL"; } } - /* save message */ - *msg = *pdu; + else + { + err = "Can't parse Timestamp"; + } } else { *pdu -= 2; - err = "UDL not match with UD length"; + err = "Unsupported DCS value"; } } else { - err = "Can't parse UDL"; + err = "Can't parse DSC"; } } else { - err = "Can't parse Timestamp"; + err = "Unhandled PID value, only SMS supported"; } - } - else - { - *pdu -= 2; - err = "Unsupported DCS value"; - } } else { - err = "Can't parse DSC"; + err = "Can't parse PID"; } - } - else - { - err = "Unhandled PID value, only SMS supported"; - } } else { - err = "Can't parse PID"; + err = "Can't parse OA"; } } else { - err = "Can't parse OA"; + err = "Can't parse length of OA"; } } - else + else if(PDUTYPE_MTI(pdu_type) == PDUTYPE_MTI_SMS_STATUS_REPORT) { - err = "Can't parse length of OA"; - } - } - else if(PDUTYPE_MTI(pdu_type) == PDUTYPE_MTI_SMS_STATUS_REPORT) - { - 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) { - err = (void*)0x1; /* HACK! */ - *msg = (char*)(ssize_t)reference; /* HACK! */ + 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) { + err = (void*)0x1; /* HACK! */ + *msg = (char*)(ssize_t)reference; /* HACK! */ + } else { + err = "Good report, but delivery failed"; + } } else { - err = "Good report, but delivery failed"; + err = "FIXME error 1"; } } else { - err = "FIXME error 1"; + err = "FIXME error 2"; } - } else { - err = "FIXME error 2"; + } + else + { + *pdu -= 2; + err = "Unhandled PDU Type MTI only SMS-DELIVER/SMS-STATUS-REPORT supported"; } } else { - *pdu -= 2; - err = "Unhandled PDU Type MTI only SMS-DELIVER/SMS-STATUS-REPORT supported"; + err = "Can't parse PDU Type"; } } else { - err = "Can't parse PDU Type"; + err = "TPDU length not matched with actual length"; } - } - else - { - err = "TPDU length not matched with actual length"; - } } else { diff --git a/ringbuffer.c b/ringbuffer.c index 655be762..08429ab0 100644 --- a/ringbuffer.c +++ b/ringbuffer.c @@ -1,9 +1,9 @@ -/* +/* Copyright (C) 2009 - 2010 - + Artem Makhutov http://www.makhutov.org - + Dmitry Vagin */ #ifdef HAVE_CONFIG_H @@ -35,7 +35,7 @@ EXPORT_DEF int rb_memcmp (const struct ringbuffer* rb, const char* mem, size_t l } } } - else + else { if (memcmp (rb->buffer + rb->read, mem, len) == 0) { @@ -119,7 +119,7 @@ EXPORT_DEF int rb_read_until_char_iov (const struct ringbuffer* rb, struct iovec iov[1].iov_len = 0; return 1; } - + if ((p = memchr (rb->buffer, c, rb->used - iov[0].iov_len)) != NULL) { iov[1].iov_base = rb->buffer; @@ -127,7 +127,7 @@ EXPORT_DEF int rb_read_until_char_iov (const struct ringbuffer* rb, struct iovec return 2; } } - else + else { iov[0].iov_base = rb->buffer + rb->read; iov[0].iov_len = rb->used; @@ -205,14 +205,14 @@ EXPORT_DEF int rb_read_until_mem_iov (const struct ringbuffer* rb, struct iovec iov[1].iov_len = 0; return 1; } - + iov[1].iov_base = rb->buffer; iov[1].iov_len = p - rb->buffer; return 2; } } } - else + else { iov[0].iov_base = rb->buffer + rb->read; iov[0].iov_len = rb->used; diff --git a/ringbuffer.h b/ringbuffer.h index 7bc0766b..8e51c3bd 100644 --- a/ringbuffer.h +++ b/ringbuffer.h @@ -1,9 +1,9 @@ -/* +/* Copyright (C) 2009 - 2010 - + Artem Makhutov http://www.makhutov.org - + Dmitry Vagin */ diff --git a/test/parse.c b/test/parse.c index 43d58cf0..37d50101 100644 --- a/test/parse.c +++ b/test/parse.c @@ -107,15 +107,11 @@ void test_parse_creg() fprintf(stderr, "%s(\"%s\")...", "at_parse_creg", input); result.res = at_parse_creg(input, strlen(input), &result.gsm_reg, &result.gsm_reg_status, &result.lac, &result.ci); if(result.res == cases[idx].result.res - && - result.gsm_reg == cases[idx].result.gsm_reg - && - result.gsm_reg_status == cases[idx].result.gsm_reg_status - && - strcmp(result.lac, cases[idx].result.lac) == 0 - && - strcmp(result.ci, cases[idx].result.ci) == 0 - ) { + && result.gsm_reg == cases[idx].result.gsm_reg + && result.gsm_reg_status == cases[idx].result.gsm_reg_status + && strcmp(result.lac, cases[idx].result.lac) == 0 + && strcmp(result.ci, cases[idx].result.ci) == 0) + { msg = "OK"; ok++; } else { @@ -230,18 +226,13 @@ void test_parse_cmgr() result.str = input = strdup(cases[idx].input); fprintf(stderr, "%s(\"%s\")...", "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); - if( ((result.res == NULL && result.res == cases[idx].result.res) || strcmp(result.res, cases[idx].result.res) == 0) - && - strcmp(result.str, cases[idx].result.str) == 0 - && - strcmp(result.oa, cases[idx].result.oa) == 0 - && - result.oa_enc == cases[idx].result.oa_enc - && - strcmp(result.msg, cases[idx].result.msg) == 0 - && - result.msg_enc == cases[idx].result.msg_enc - ) { + if(((result.res == NULL && result.res == cases[idx].result.res) || strcmp(result.res, cases[idx].result.res) == 0) + && strcmp(result.str, cases[idx].result.str) == 0 + && strcmp(result.oa, cases[idx].result.oa) == 0 + && result.oa_enc == cases[idx].result.oa_enc + && strcmp(result.msg, cases[idx].result.msg) == 0 + && result.msg_enc == cases[idx].result.msg_enc) + { msg = "OK"; ok++; } else { @@ -281,13 +272,10 @@ void test_parse_cusd() fprintf(stderr, "%s(\"%s\")...", "at_parse_cusd", input); result.res = at_parse_cusd(input, &result.type, &result.cusd, &result.dcs); if(result.res == cases[idx].result.res - && - result.type == cases[idx].result.type - && - result.dcs == cases[idx].result.dcs - && - strcmp(result.cusd, cases[idx].result.cusd) == 0 - ) { + && result.type == cases[idx].result.type + && result.dcs == cases[idx].result.dcs + && strcmp(result.cusd, cases[idx].result.cusd) == 0) + { msg = "OK"; ok++; } else { @@ -358,21 +346,14 @@ void test_parse_clcc() fprintf(stderr, "%s(\"%s\")...", "at_parse_clcc", input); result.res = at_parse_clcc(input, &result.index, &result.dir, &result.stat, &result.mode, &result.mpty, &result.number, &result.toa); if(result.res == cases[idx].result.res - && - result.index == cases[idx].result.index - && - result.dir == cases[idx].result.dir - && - result.stat == cases[idx].result.stat - && - result.mode == cases[idx].result.mode - && - result.mpty == cases[idx].result.mpty - && - strcmp(result.number, cases[idx].result.number) == 0 - && - result.toa == cases[idx].result.toa - ) { + && result.index == cases[idx].result.index + && result.dir == cases[idx].result.dir + && result.stat == cases[idx].result.stat + && result.mode == cases[idx].result.mode + && result.mpty == cases[idx].result.mpty + && strcmp(result.number, cases[idx].result.number) == 0 + && result.toa == cases[idx].result.toa) + { msg = "OK"; ok++; } else { @@ -409,4 +390,4 @@ int main() fprintf(stderr, "done %d tests: %d OK %d FAILS\n", ok + faults, ok, faults); return 0; -} \ No newline at end of file +} From 58014345d15f60b746a6ce62cc5dc8b83762da3b Mon Sep 17 00:00:00 2001 From: yylyyl Date: Sat, 19 Sep 2015 20:11:09 +0800 Subject: [PATCH 010/117] Applied chan-dongle-alphanumeric-pdu.patch. USSD for E261 works now. Source: https://code.google.com/p/asterisk-chan-dongle/issues/detail?id=17 --- README.txt | 1 + at_response.c | 1 + pdu.c | 15 ++++++++++++++- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/README.txt b/README.txt index d9a8c8b1..4a267049 100644 --- a/README.txt +++ b/README.txt @@ -16,6 +16,7 @@ This channel driver should work with the folowing UMTS cards: * Huawei E169 / K3520 * Huawei E155X * Huawei E175X +* Huawei E261 * Huawei K3765 Check complete list in: diff --git a/at_response.c b/at_response.c index 83e800a7..3ac54110 100644 --- a/at_response.c +++ b/at_response.c @@ -1616,6 +1616,7 @@ static int at_response_cgmm (struct pvt* pvt, const char* str) "E153", "E156B", "E1752", + "E261" }; ast_copy_string (pvt->model, str, sizeof (pvt->model)); diff --git a/pdu.c b/pdu.c index be840e01..d2c84033 100644 --- a/pdu.c +++ b/pdu.c @@ -182,6 +182,8 @@ */ #define NUMBER_TYPE_INTERNATIONAL 0x91 +#define NUMBER_TYPE_NATIONAL 0xC8 +#define NUMBER_TYPE_ALPHANUMERIC 0xD0 /* Message Type Indicator Parameter */ #define PDUTYPE_MTI_SHIFT 0 @@ -229,6 +231,7 @@ #define PDU_PID_SMS 0x00 /* bit5 No interworking, but SME-to-SME protocol = SMS */ #define PDU_PID_EMAIL 0x32 /* bit5 Telematic interworking, bits 4..0 0x 12 = email */ +#define PDU_PID_SMS_REPLACE_MASK 0x40 /* bit7 Replace Short Message function activated (TP-PID = 0x41 to 0x47) */ /* DCS */ /* bits 1..0 Class */ @@ -454,7 +457,15 @@ static int pdu_parse_number(char ** pdu, size_t * pdu_length, unsigned digits, i { char digit; if(*toa == NUMBER_TYPE_INTERNATIONAL) + { *number++ = '+'; + } + else if(*toa == NUMBER_TYPE_ALPHANUMERIC) + { + for(; syms > 0; syms--, *pdu += 1, *pdu_length -= 1) + *number++ = pdu[0][0]; + return *pdu - begin; + } for(; syms > 0; syms -= 2, *pdu += 2, *pdu_length -= 2) { digit = pdu_code2digit(pdu[0][1]); @@ -690,10 +701,12 @@ EXPORT_DEF const char * pdu_parse(char ** pdu, size_t tpdu_length, char * oa, si { int pid = pdu_parse_byte(pdu, &pdu_length); *oa_enc = STR_ENCODING_7BIT; + if(oa_toa == NUMBER_TYPE_ALPHANUMERIC) + *oa_enc = STR_ENCODING_7BIT_HEX; if(pid >= 0) { /* TODO: support other types of messages */ - if(pid == PDU_PID_SMS) + if( (pid == PDU_PID_SMS) || (pid & PDU_PID_SMS_REPLACE_MASK) ) { int dcs = pdu_parse_byte(pdu, &pdu_length); if(dcs >= 0) From 1cf0f4bcf65fc6ee8e2a9c0a0fb0d532ce913a27 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Sun, 20 Sep 2015 11:48:50 +0200 Subject: [PATCH 011/117] Move ASTERISK_VERSION_NUM setting to ./configure invocation: Now do: ./configure --with-astversion=110102 if you have Asterisk version 11.1.2. --- configure.in | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/configure.in b/configure.in index 9edd7a8b..0a0c8615 100644 --- a/configure.in +++ b/configure.in @@ -23,6 +23,15 @@ AC_ARG_WITH( [ with_asterisk="../include /usr/include /usr/local/include /opt/local/include" ] ) +dnl Required: asterisk version +AC_ARG_WITH( + [astversion], + AS_HELP_STRING([--with-astversion=MMmmuu], [set asterisk version to 10813 for version 1.8.13]), + [ if ! test "$((with_astversion + 0))" -eq "$with_astversion" ; then AC_MSG_ERROR([Invalid --with-astversion=MMmmuu value]); fi ], + [ AC_MSG_ERROR([Please set --with-astversion=MMmmuu (major.minor.micro)]) ] +) +CPPFLAGS="$CPPFLAGS -DASTERISK_VERSION_NUM=$with_astversion" + AC_ARG_ENABLE( [debug], AS_HELP_STRING([--enable-debug], [enable code debugging]), From 6e282785a7b9fc3b635d4d3425e31dc66c7fca01 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Sun, 20 Sep 2015 12:08:57 +0200 Subject: [PATCH 012/117] Fix channel unlock issue in Asterisk >= 12. Thanks @bg111 for the review. --- at_response.c | 8 ++++---- channel.c | 7 +++++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/at_response.c b/at_response.c index 3ac54110..c47d0ba5 100644 --- a/at_response.c +++ b/at_response.c @@ -868,10 +868,10 @@ static int start_pbx(struct pvt* pvt, const char * number, int call_idx, call_st // FIXME: not execute if channel_new() failed CPVT_SET_FLAGS(cpvt, CALL_FLAG_NEED_HANGUP); - // ast_pbx_start() usually failed if asterisk.conf minmemfree set too low, try drop buffer cache sync && echo 3 > /proc/sys/vm/drop_caches -#if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ - ast_channel_unlock(channel); -#endif /* ^13+ */ + /* ast_pbx_start() usually failed if asterisk.conf minmemfree + * set too low, try drop buffer cache + * sync && echo 3 >/proc/sys/vm/drop_caches + */ if (ast_pbx_start (channel)) { ast_channel_tech_pvt_set(channel, NULL); diff --git a/channel.c b/channel.c index a14bccaf..a4aaf926 100644 --- a/channel.c +++ b/channel.c @@ -1296,6 +1296,13 @@ EXPORT_DEF struct ast_channel* new_channel( ast_module_ref (self_module()); +#if ASTERISK_VERSION_NUM >= 120000 /* 12+ */ + /* commit e2630fcd516b8f794bf342d9fd267b0c905e79ce + * Date: Wed Dec 18 19:28:05 2013 +0000a + * ast_channel_alloc() returns allocated channels locked. */ + ast_channel_unlock(channel); +#endif /* ^12+ */ + return channel; } cpvt_free(cpvt); From 6dad1231763977b12db712943c02bcbe89c32a17 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Sun, 20 Sep 2015 12:19:23 +0200 Subject: [PATCH 013/117] Clean up horrific ASTERISK_VERSION_NUM #if/#elif in channel_request. --- channel.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/channel.c b/channel.c index a4aaf926..40dd6292 100644 --- a/channel.c +++ b/channel.c @@ -151,23 +151,28 @@ static struct ast_channel * channel_request( #if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ if (ast_format_cap_iscompatible_format(cap, ast_format_slin) != AST_FORMAT_CMP_EQUAL) -#elif ASTERISK_VERSION_NUM >= 100000 /* 10-13 */ - if (!ast_format_cap_iscompatible(cap, &chan_dongle_format)) -#else /* 10- */ - oldformat = format; - format &= AST_FORMAT_SLINEAR; - if (!format) -#endif /* ^10- */ { -#if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ struct ast_str *codec_buf = ast_str_alloca(64); ast_log(LOG_WARNING, "Asked to get a channel of unsupported format '%s'\n", ast_format_cap_get_names(cap, &codec_buf)); + *cause = AST_CAUSE_FACILITY_NOT_IMPLEMENTED; + return NULL; + } #elif ASTERISK_VERSION_NUM >= 100000 /* 10-13 */ + if (!ast_format_cap_iscompatible(cap, &chan_dongle_format)) + { char buf[255]; ast_log(LOG_WARNING, "Asked to get a channel of unsupported format '%s'\n", ast_getformatname_multiple(buf, 255, cap)); -#elif ASTERISK_VERSION_NUM >= 10800 /* 1.8+ */ + *cause = AST_CAUSE_FACILITY_NOT_IMPLEMENTED; + return NULL; + } +#else /* 10- */ + oldformat = format; + format &= AST_FORMAT_SLINEAR; + if (!format) + { +#if ASTERISK_VERSION_NUM >= 10800 /* 1.8+ */ ast_log(LOG_WARNING, "Asked to get a channel of unsupported format '%s'\n", ast_getformatname(oldformat)); #else /* 1.8- */ @@ -177,6 +182,7 @@ static struct ast_channel * channel_request( *cause = AST_CAUSE_FACILITY_NOT_IMPLEMENTED; return NULL; } +#endif /* ^10- */ dest_dev = ast_strdupa (data); From c5c0c5da56e55908395a1ba4f284e190a36184dc Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Sun, 20 Sep 2015 13:17:30 +0200 Subject: [PATCH 014/117] build: Properly force a recompile on config.h change. Also move the ASTERISK_VERSION_NUM to config.h. --- Makefile.in | 6 +++--- config.h.in | 48 ++++++++++++++++++++++++++++++------------------ configure.in | 4 +++- stamp-h.in | 1 + 4 files changed, 37 insertions(+), 22 deletions(-) diff --git a/Makefile.in b/Makefile.in index 92674a85..1265182b 100644 --- a/Makefile.in +++ b/Makefile.in @@ -61,7 +61,7 @@ $(PROJS): $(chan_dongles_so_OBJS) Makefile $(CHMOD) 755 $@ mv $@ chan_dongle.so -.c.o: +.c.o: Makefile config.h $(CC) $(CFLAGS) $(MAKE_DEPS) -o $@ -c $< tests: test/test1 test/parse @@ -88,8 +88,8 @@ dist: $(SOURCES) $(HEADERS) $(EXTRA_DIST) $(BUILD_TOOLS) tar czf $(DISTNAME).tgz $(DISTNAME) --exclude .svn -h @$(RM) $(DISTNAME) -${srcdir}/configure: configure.in - cd ${srcdir} && autoconf +configure: configure.in + autoconf config.h: stamp-h stamp-h: config.h.in config.status diff --git a/config.h.in b/config.h.in index a977ab92..6ac3f327 100644 --- a/config.h.in +++ b/config.h.in @@ -1,5 +1,7 @@ -#ifndef CONFIG_H_INCLUDED -#define CONFIG_H_INCLUDED +/* config.h.in. Generated from configure.in by autoheader. */ + +/* The Asterisk version as configured --with-astversion. */ +#undef ASTERISK_VERSION_NUM /* name of asterisk module */ #undef AST_MODULE @@ -10,12 +12,12 @@ /* Build Manager extentions */ #undef BUILD_MANAGER +/* Define to 1 if you have HAVE_AST_CONTROL_SRCCHANGE in asterisk/frame.h */ +#undef HAVE_AST_CONTROL_SRCCHANGE + /* Define to 1 if you have the header file. */ #undef HAVE_FCNTL_H -/* Define to 1 if you have the header file. */ -#undef HAVE_ICONV_H - /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H @@ -34,6 +36,9 @@ /* Define to 1 if you have the `memset' function. */ #undef HAVE_MEMSET +/* Define to 1 if you have the `realpath' function. */ +#undef HAVE_REALPATH + /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H @@ -46,9 +51,6 @@ /* Define to 1 if you have the `strchr' function. */ #undef HAVE_STRCHR -/* Define to 1 if you have the `realpath' function. */ -#undef HAVE_REALPATH - /* Define to 1 if you have the header file. */ #undef HAVE_STRINGS_H @@ -82,33 +84,45 @@ /* Define to iconv_t if you has iconv_t in iconv.h */ #undef ICONV_T -/* Define to 1 if you have HAVE_AST_CONTROL_SRCCHANGE in asterisk/frame.h */ -#undef HAVE_AST_CONTROL_SRCCHANGE +/* Define to the address where bug reports for this package should be sent */ +#undef MODULE_BUGREPORT + +/* Define to the home page for this package */ +#undef MODULE_URL + +/* Define to the version of this package */ +#undef MODULE_VERSION + +/* Name of package */ +#undef PACKAGE /* Define to the address where bug reports for this package should be sent. */ -#undef MODULE_BUGREPORT +#undef PACKAGE_BUGREPORT /* Define to the full name of this package. */ -//#undef PACKAGE_NAME +#undef PACKAGE_NAME /* Revision of package */ #undef PACKAGE_REVISION /* Define to the full name and version of this package. */ -//#undef PACKAGE_STRING +#undef PACKAGE_STRING /* Define to the one symbol short name of this package. */ -//#undef PACKAGE_TARNAME +#undef PACKAGE_TARNAME /* Define to the home page for this package. */ -#undef MODULE_URL +#undef PACKAGE_URL /* Define to the version of this package. */ -#undef MODULE_VERSION +#undef PACKAGE_VERSION /* Define to 1 if you have the ANSI C header files. */ #undef STDC_HEADERS +/* Version number of package */ +#undef VERSION + /* Define for Solaris 2.5.1 so the uint64_t typedef from , , or is not used. If the typedef were allowed, the #define below would cause a syntax error. */ @@ -135,5 +149,3 @@ /* Define to the type of an unsigned integer type of width exactly 64 bits if such a type exists and the standard includes do not define it. */ #undef uint64_t - -#endif /* CONFIG_H_INCLUDED */ diff --git a/configure.in b/configure.in index 0a0c8615..52befd64 100644 --- a/configure.in +++ b/configure.in @@ -7,6 +7,7 @@ AC_CANONICAL_TARGET AM_INIT_AUTOMAKE AC_CONFIG_HEADERS([config.h]) AC_CONFIG_SRCDIR([chan_dongle.c]) +AC_CONFIG_FILES([stamp-h], [echo timestamp > stamp-h]) AC_CANONICAL_SYSTEM AC_CANONICAL_HOST @@ -30,7 +31,8 @@ AC_ARG_WITH( [ if ! test "$((with_astversion + 0))" -eq "$with_astversion" ; then AC_MSG_ERROR([Invalid --with-astversion=MMmmuu value]); fi ], [ AC_MSG_ERROR([Please set --with-astversion=MMmmuu (major.minor.micro)]) ] ) -CPPFLAGS="$CPPFLAGS -DASTERISK_VERSION_NUM=$with_astversion" +AC_DEFINE_UNQUOTED([ASTERISK_VERSION_NUM], [$with_astversion], + [The Asterisk version as configured --with-astversion.]) AC_ARG_ENABLE( [debug], diff --git a/stamp-h.in b/stamp-h.in index e69de29b..9788f702 100644 --- a/stamp-h.in +++ b/stamp-h.in @@ -0,0 +1 @@ +timestamp From c3490bbae8ba96f937ebd93d17155d8ad0766b48 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Sun, 20 Sep 2015 13:47:50 +0200 Subject: [PATCH 015/117] Clean up format_cap_set stuff. Possibly fixing Local channel support. --- chan_dongle.c | 1 - chan_dongle.h | 1 - channel.c | 26 +++++++++++++++++--------- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/chan_dongle.c b/chan_dongle.c index 01d17f7a..ff87e325 100644 --- a/chan_dongle.c +++ b/chan_dongle.c @@ -1662,7 +1662,6 @@ static int public_state_init(struct public_state * state) return AST_MODULE_LOAD_FAILURE; } ast_format_cap_append(channel_tech.capabilities, ast_format_slin, 0); - //chan_dongle_format_cap = channel_tech.capabilities; #elif ASTERISK_VERSION_NUM >= 100000 /* 10-13 */ ast_format_set(&chan_dongle_format, AST_FORMAT_SLINEAR, 0); if (!(channel_tech.capabilities = ast_format_cap_alloc())) { diff --git a/chan_dongle.h b/chan_dongle.h index b6e0436c..362cffca 100644 --- a/chan_dongle.h +++ b/chan_dongle.h @@ -41,7 +41,6 @@ INLINE_DECL const char * dev_state2str_msg(dev_state_t state) #if ASTERISK_VERSION_NUM >= 100000 && ASTERISK_VERSION_NUM < 130000 /* 10-13 */ /* Only linear is allowed */ EXPORT_DECL struct ast_format chan_dongle_format; -//EXPORT_DEF struct ast_format chan_dongle_format; EXPORT_DECL struct ast_format_cap * chan_dongle_format_cap; #endif /* ^10-13 */ diff --git a/channel.c b/channel.c index 40dd6292..e5dd43cb 100644 --- a/channel.c +++ b/channel.c @@ -679,7 +679,7 @@ static struct ast_frame* channel_read (struct ast_channel* channel) cpvt->a_read_frame.frametype = AST_FRAME_VOICE; #if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ - cpvt->a_read_frame.subclass.format = ao2_bump(ast_format_slin); + cpvt->a_read_frame.subclass.format = ast_format_slin; #elif ASTERISK_VERSION_NUM >= 100000 /* 10-13 */ ast_format_copy(&cpvt->a_read_frame.subclass.format, &chan_dongle_format); #else /* 10- */ @@ -1265,21 +1265,29 @@ EXPORT_DEF struct ast_channel* new_channel( ast_channel_tech_set(channel, &channel_tech); #if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ - ast_format_cap_append(ast_channel_nativeformats(channel), ast_format_slin, 0); + ast_channel_nativeformats_set(channel, channel_tech.capabilities); + ast_channel_set_rawreadformat(channel, ast_format_slin); + ast_channel_set_rawwriteformat(channel, ast_format_slin); ast_channel_set_writeformat(channel, ast_format_slin); ast_channel_set_readformat(channel, ast_format_slin); #elif ASTERISK_VERSION_NUM >= 110000 /* 11+ */ - ast_format_set(ast_channel_readformat(channel), AST_FORMAT_SLINEAR, 0); - ast_format_set(ast_channel_writeformat(channel), AST_FORMAT_SLINEAR, 0); - ast_format_cap_set(ast_channel_nativeformats(channel), ast_channel_writeformat(channel)); + ast_format_cap_add(ast_channel_nativeformats(channel), &chan_dongle_format); + ast_format_copy(ast_channel_rawreadformat(channel), &chan_dongle_format); + ast_format_copy(ast_channel_rawwriteformat(channel), &chan_dongle_format); + ast_format_copy(ast_channel_writeformat(channel), &chan_dongle_format); + ast_format_copy(ast_channel_readformat(channel), &chan_dongle_format); #elif ASTERISK_VERSION_NUM >= 100000 /* 10+ */ - ast_format_set(&channel->readformat, AST_FORMAT_SLINEAR, 0); - ast_format_set(&channel->writeformat, AST_FORMAT_SLINEAR, 0); - ast_format_cap_set(channel->nativeformats, &channel->writeformat); + ast_format_cap_add(channel->nativeformats, &chan_dongle_format); + ast_format_copy(&channel->rawreadformat, &chan_dongle_format); + ast_format_copy(&channel->rawwriteformat, &chan_dongle_format); + ast_format_copy(&channel->writeformat, &chan_dongle_format); + ast_format_copy(&channel->readformat, &chan_dongle_format); #else /* 10- */ channel->nativeformats = AST_FORMAT_SLINEAR; - channel->writeformat = AST_FORMAT_SLINEAR; + channel->rawreadformat = AST_FORMAT_SLINEAR; + channel->rawwriteformat = AST_FORMAT_SLINEAR; channel->readformat = AST_FORMAT_SLINEAR; + channel->writeformat = AST_FORMAT_SLINEAR; #endif /* ^10- */ if (ast_state == AST_STATE_RING) From db3c629f38cf75c704c2f264963900f55d67be83 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Fri, 25 Dec 2015 15:52:14 +0100 Subject: [PATCH 016/117] Clarify --with-astversion parameter in ./configure. As suggested by @arekm. Note that "010813" does not work, as gcc will treat it as base 8. --- configure.in | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/configure.in b/configure.in index 52befd64..7d19e52f 100644 --- a/configure.in +++ b/configure.in @@ -27,9 +27,9 @@ AC_ARG_WITH( dnl Required: asterisk version AC_ARG_WITH( [astversion], - AS_HELP_STRING([--with-astversion=MMmmuu], [set asterisk version to 10813 for version 1.8.13]), - [ if ! test "$((with_astversion + 0))" -eq "$with_astversion" ; then AC_MSG_ERROR([Invalid --with-astversion=MMmmuu value]); fi ], - [ AC_MSG_ERROR([Please set --with-astversion=MMmmuu (major.minor.micro)]) ] + AS_HELP_STRING([--with-astversion=Mmmuu], [set asterisk version; examples: 1.8.13 => 10813, 11.1.2 => 110102]), + [ if ! test "$((with_astversion + 0))" -eq "$with_astversion" ; then AC_MSG_ERROR([Invalid --with-astversion=Mmmuu value]); fi ], + [ AC_MSG_ERROR([Please set --with-astversion=Mmmuu (major.minor.micro)]) ] ) AC_DEFINE_UNQUOTED([ASTERISK_VERSION_NUM], [$with_astversion], [The Asterisk version as configured --with-astversion.]) From 53e0be53a097087cfc34ba4974027303fe80ac32 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Tue, 10 May 2016 22:02:24 +0200 Subject: [PATCH 017/117] Fix compile issues with master (Asterisk 14). --- chan_dongle.c | 3 +++ cli.c | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/chan_dongle.c b/chan_dongle.c index ff87e325..b18d143b 100644 --- a/chan_dongle.c +++ b/chan_dongle.c @@ -36,7 +36,10 @@ #endif /* HAVE_CONFIG_H */ #include + +#if ASTERISK_VERSION_NUM < 140000 /* 14- */ ASTERISK_FILE_VERSION(__FILE__, "$Rev: " PACKAGE_REVISION " $") +#endif /* 14- */ #include /* AST_DECLARE_STRING_FIELDS for asterisk/manager.h */ #include diff --git a/cli.c b/cli.c index b13e222a..71468da3 100644 --- a/cli.c +++ b/cli.c @@ -933,6 +933,10 @@ static struct ast_cli_entry cli[] = { AST_CLI_DEFINE (cli_discovery, "Discovery devices and create config"), }; +#if ASTERISK_VERSION_NUM >= 140000 /* 14+ */ +# define AST_MODULE_SELF self_module() +#endif /* 14+ */ + #/* */ EXPORT_DEF void cli_register() { From c2d9c34c4356e933e041ce22d2f6350f406e2e35 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Sun, 4 Sep 2016 15:33:51 +0200 Subject: [PATCH 018/117] Don't crash ongoing calls when receiving SMS. According to the bug report by Marcelo Fernandes Vianna: > Under some circumstances (if parsing the sms returns -1 in > at_response.c), even if sms is disabled in dongle.conf, > receiving a sms can still crash an ongoing call. Thanks for the suggested patch. Refactored and merged. Closes #7. --- at_response.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/at_response.c b/at_response.c index c47d0ba5..95ef4104 100644 --- a/at_response.c +++ b/at_response.c @@ -1151,34 +1151,33 @@ static int at_response_cmti (struct pvt* pvt, const char* str) // FIXME: check format in PDU mode int index = at_parse_cmti (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 (CONF_SHARED(pvt, disablesms)) - { - ast_log (LOG_WARNING, "[%s] SMS reception has been disabled in the configuration.\n", PVT_ID(pvt)); - } - else if(pvt_enabled(pvt)) + if (pvt_enabled(pvt)) { if (at_enque_retrive_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; } - else - { - pvt->incoming_sms = 1; - } + pvt->incoming_sms = 1; } - - return 0; } else { ast_log (LOG_ERROR, "[%s] Error parsing incoming sms message alert '%s', disconnecting\n", PVT_ID(pvt), str); return -1; } + + return 0; } /*! From 9529a68056331e46fceca8a3480c5883d931e1f1 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Sun, 4 Sep 2016 16:19:28 +0200 Subject: [PATCH 019/117] Fix compile issues with Asterisk 14. Declare AST_MODULE_SELF_SYM=__internal_$(module_name)_self. --- Makefile.in | 3 ++- chan_dongle.c | 2 +- cli.c | 4 ---- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/Makefile.in b/Makefile.in index 1265182b..776de428 100644 --- a/Makefile.in +++ b/Makefile.in @@ -38,7 +38,8 @@ INSTALL = @INSTALL@ CHMOD = chmod # -DAST_MODULE=\"$(PROJM)\" -D_THREAD_SAFE -CFLAGS = @CFLAGS@ -I$(srcdir) @CPPFLAGS@ @DEFS@ @AC_CFLAGS@ +CFLAGS = @CFLAGS@ -I$(srcdir) -DAST_MODULE_SELF_SYM=__internal_chan_dongle_self \ + @CPPFLAGS@ @DEFS@ @AC_CFLAGS@ LDFLAGS = @LDFLAGS@ SOLINK = @SOLINK@ LIBS = @LIBS@ diff --git a/chan_dongle.c b/chan_dongle.c index b18d143b..f6f03306 100644 --- a/chan_dongle.c +++ b/chan_dongle.c @@ -1784,7 +1784,7 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, MODULE_DESCRIPTION, //AST_MODULE_INFO_STANDARD (ASTERISK_GPL_KEY, MODULE_DESCRIPTION); -EXPORT_DEF struct ast_module* self_module() +EXPORT_DEF struct ast_module* self_module(void) { return ast_module_info->self; } diff --git a/cli.c b/cli.c index 71468da3..b13e222a 100644 --- a/cli.c +++ b/cli.c @@ -933,10 +933,6 @@ static struct ast_cli_entry cli[] = { AST_CLI_DEFINE (cli_discovery, "Discovery devices and create config"), }; -#if ASTERISK_VERSION_NUM >= 140000 /* 14+ */ -# define AST_MODULE_SELF self_module() -#endif /* 14+ */ - #/* */ EXPORT_DEF void cli_register() { From bf38e687d978f17a6afeb1afe0a34a1f3142069d Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Fri, 30 Sep 2016 15:24:14 +0200 Subject: [PATCH 020/117] Fix decoding of SMS with non-language characters. Character conversion assumed UCS-2, which handles only the Basic Multilingual Plane, but various parties will have switched to using UTF-16 there. Fix suggested by @fadasi. Reported and tested by @pjmichel. Thanks! Closes #8. --- char_conv.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/char_conv.c b/char_conv.c index f1f47219..4cc737e9 100644 --- a/char_conv.c +++ b/char_conv.c @@ -133,7 +133,10 @@ static ssize_t hexstr_ucs2_to_utf8 (const char* in, size_t in_length, char* out, return res; } - res = convert_string (buf, res, out, out_size, "UCS-2BE", "UTF-8"); + /* 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; } @@ -148,7 +151,10 @@ static ssize_t utf8_to_hexstr_ucs2 (const char* in, size_t in_length, char* out, return -1; } - res = convert_string (in, in_length, buf, out_size, "UTF-8", "UCS-2BE"); + /* 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; From 15f2a39d26fb54356667d3605e96b0c5e2f80614 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Sat, 1 Oct 2016 14:01:39 +0200 Subject: [PATCH 021/117] Add macros for decoding TP-DA, TP-OA and TP-RA addresses. --- pdu.c | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/pdu.c b/pdu.c index d2c84033..816a3bf5 100644 --- a/pdu.c +++ b/pdu.c @@ -181,9 +181,36 @@ ... */ -#define NUMBER_TYPE_INTERNATIONAL 0x91 -#define NUMBER_TYPE_NATIONAL 0xC8 -#define NUMBER_TYPE_ALPHANUMERIC 0xD0 +/* Address octets: 0=length_in_nibbles, 1=EXT/TON/NPI, 2..11=address + * (destination address (TP-DA), originator address (TP-OA) and recipient address (TP-RA)) + * EXT: bit7: 1 "no extension" + * TON: bit6..4: see below + * NPI: bit3..0: see below + * Source: https://en.wikipedia.org/wiki/GSM_03.40 */ +#define TP_A_EXT (1 << 7) +#define TP_A_EXT_NOEXT (1 << 7) +#define TP_A_TON (7 << 4) +#define TP_A_TON_UNKNOWN (0 << 4) +#define TP_A_TON_INTERNATIONAL (1 << 4) +#define TP_A_TON_NATIONAL (2 << 4) +#define TP_A_TON_NETSPECIFIC (3 << 4) +#define TP_A_TON_SUBSCRIBERNUM (4 << 4) +#define TP_A_TON_ALPHANUMERIC (5 << 4) +#define TP_A_TON_ABBREVIATEDNUM (6 << 4) +#define TP_A_TON_RESERVED (7 << 4) +#define TP_A_NPI (15 << 0) +#define TP_A_NPI_UNKNOWN (0 << 0) +#define TP_A_NPI_TEL_E164_E163 (1 << 0) +#define TP_A_NPI_TELEX (3 << 0) +#define TP_A_NPI_SVCCENTR_SPEC1 (4 << 0) +#define TP_A_NPI_SVCCENTR_SPEC2 (5 << 0) +#define TP_A_NPI_NATIONALNUM (8 << 0) +#define TP_A_NPI_PRIVATENUM (9 << 0) +#define TP_A_NPI_ERMESNUM (10 << 0) +#define TP_A_NPI_RESERVED (15 << 0) +#define NUMBER_TYPE_INTERNATIONAL (TP_A_EXT_NOEXT | TP_A_TON_INTERNATIONAL | TP_A_NPI_TEL_E164_E163) /* 0x91 */ +#define NUMBER_TYPE_NATIONAL (TP_A_EXT_NOEXT | TP_A_TON_SUBSCRIBERNUM | TP_A_NPI_NATIONALNUM) /* 0xC8 */ +#define NUMBER_TYPE_ALPHANUMERIC (TP_A_EXT_NOEXT | TP_A_TON_ALPHANUMERIC | TP_A_NPI_UNKNOWN) /* 0xD0 */ /* Message Type Indicator Parameter */ #define PDUTYPE_MTI_SHIFT 0 From 39c3e04938e1da4ee7f91ed7378ee3d91043b049 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Sat, 1 Oct 2016 14:13:39 +0200 Subject: [PATCH 022/117] Support TON=5 with any NPI (instead of just 0) for alphanumeric address. @fadasi observed addresses with 0xD0 and 0xD1 (NPI is UNKNOWN or E164) in the wild. Fix comparisons to only check the TON. Closes #9. --- pdu.c | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/pdu.c b/pdu.c index 816a3bf5..a4803765 100644 --- a/pdu.c +++ b/pdu.c @@ -477,36 +477,38 @@ static int pdu_parse_number(char ** pdu, size_t * pdu_length, unsigned digits, i begin = *pdu; *toa = pdu_parse_byte(pdu, pdu_length); - if(*toa >= 0) + if (*toa >= 0) { unsigned syms = ROUND_UP2(digits); - if(syms <= *pdu_length) + if (syms <= *pdu_length) { char digit; - if(*toa == NUMBER_TYPE_INTERNATIONAL) - { - *number++ = '+'; - } - else if(*toa == NUMBER_TYPE_ALPHANUMERIC) + 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; } - for(; syms > 0; syms -= 2, *pdu += 2, *pdu_length -= 2) + 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) + if (digit <= 0) return -1; *number++ = digit; digit = pdu_code2digit(pdu[0][0]); - if((signed char)digit < 0 || (digit == 0 && (syms != 2 || (digits & 0x1) == 0))) + if ((signed char)digit < 0 || (digit == 0 && (syms != 2 || (digits & 0x1) == 0))) return -1; *number++ = digit; } - if((digits & 0x1) == 0) + if ((digits & 0x1) == 0) *number = 0; return *pdu - begin; } @@ -728,9 +730,10 @@ EXPORT_DEF const char * pdu_parse(char ** pdu, size_t tpdu_length, char * oa, si { int pid = pdu_parse_byte(pdu, &pdu_length); *oa_enc = STR_ENCODING_7BIT; - if(oa_toa == NUMBER_TYPE_ALPHANUMERIC) + if ((oa_toa & TP_A_TON) == TP_A_TON_ALPHANUMERIC) { *oa_enc = STR_ENCODING_7BIT_HEX; - if(pid >= 0) + } + if (pid >= 0) { /* TODO: support other types of messages */ if( (pid == PDU_PID_SMS) || (pid & PDU_PID_SMS_REPLACE_MASK) ) From c24a3150ac61097c6e4b3cca288de66b9d1be2d2 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Sun, 2 Oct 2016 13:36:21 +0200 Subject: [PATCH 023/117] Fix so compilation with different include dirs work. Now you can do this: ./configure --with-astversion=140000 \ --with-asterisk=$HOME/src/asterisk-14/include make clean all And: ./configure --with-astversion=10800 \ --with-asterisk=$HOME/src/asterisk-1.8/include make clean all Etc.. --- app.c | 7 ++----- ast_config.h | 30 ++++++++++++++++++++++++++++++ at_command.c | 5 +---- at_command.h | 2 ++ at_parse.c | 4 +--- at_queue.c | 5 +---- at_read.c | 7 ++----- at_response.c | 5 +---- chan_dongle.c | 6 +----- chan_dongle.h | 3 ++- channel.c | 5 +---- channel.h | 3 ++- char_conv.c | 4 +--- cli.c | 4 +--- configure.in | 7 ++++++- cpvt.c | 5 +---- cpvt.h | 3 ++- dc_config.c | 4 ---- dc_config.h | 3 ++- helpers.c | 5 +---- manager.c | 6 +----- memmem.c | 6 +----- memmem.h | 4 ---- mixbuffer.c | 5 +---- mixbuffer.h | 3 ++- mutils.h | 2 ++ pdiscovery.c | 4 +--- pdu.c | 4 +--- ringbuffer.c | 4 +--- 29 files changed, 70 insertions(+), 85 deletions(-) create mode 100644 ast_config.h diff --git a/app.c b/app.c index 6ea5e935..732ff3de 100644 --- a/app.c +++ b/app.c @@ -1,7 +1,3 @@ -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ - #ifdef BUILD_APPLICATIONS /* Copyright (C) 2009 - 2010 @@ -14,7 +10,8 @@ bg */ -#include +#include "ast_config.h" + #include /* AST_DECLARE_APP_ARGS() ... */ #include /* pbx_builtin_setvar_helper() */ #include /* ast_register_application2() ast_unregister_application() */ diff --git a/ast_config.h b/ast_config.h new file mode 100644 index 00000000..7c6d4ad4 --- /dev/null +++ b/ast_config.h @@ -0,0 +1,30 @@ +#ifndef AST_CONFIG_H +#define AST_CONFIG_H + +/* If we want to include config.h, we need to import asterisk.h first + * so we can fix the duplicate PACKAGE_* first. + * Note that this bug did not show itself until trying to compile with + * include files from *outside* /usr/include. Apparently gcc has a + * -Wsystem-headers which is *not* enabled by default, causing the + * difference. */ +#include + +#ifdef HAVE_CONFIG_H + +/* For some reason, Asterisk exports PACKAGE_* variables which we don't want. + * See also: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=733598 + * + * Simply defining ASTERISK_AUTOCONFIG_H to skip importing of the autoconf.h + * altogether does not work. It breaks the other Asterisk header files. */ +#undef PACKAGE_BUGREPORT +#undef PACKAGE_NAME +#undef PACKAGE_STRING +#undef PACKAGE_TARNAME +#undef PACKAGE_URL +#undef PACKAGE_VERSION + +#include "config.h" + +#endif /* HAVE_CONFIG_H */ + +#endif /* AST_CONFIG_H */ diff --git a/at_command.c b/at_command.c index 8ed6c842..33f0673b 100644 --- a/at_command.c +++ b/at_command.c @@ -15,11 +15,8 @@ http://www.makhutov.org */ -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ +#include "ast_config.h" -#include #include #include "at_command.h" diff --git a/at_command.h b/at_command.h index 7552dd48..e6cb69b7 100644 --- a/at_command.h +++ b/at_command.h @@ -4,6 +4,8 @@ #ifndef CHAN_DONGLE_AT_SEND_H_INCLUDED #define CHAN_DONGLE_AT_SEND_H_INCLUDED +#include "ast_config.h" + #include "export.h" /* EXPORT_DECL EXPORT_DEF */ #include "dc_config.h" /* call_waiting_t */ #include "mutils.h" /* enum2str_def() ITEMS_OF() */ diff --git a/at_parse.c b/at_parse.c index 85172cb6..888ff8a6 100644 --- a/at_parse.c +++ b/at_parse.c @@ -8,9 +8,7 @@ bg */ -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ +#include "ast_config.h" #include "memmem.h" diff --git a/at_queue.c b/at_queue.c index dc480a71..c5ac2553 100644 --- a/at_queue.c +++ b/at_queue.c @@ -9,11 +9,8 @@ Copyright (C) 2010 - 2011 bg */ -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ +#include "ast_config.h" -#include #include /* ast_free() */ #include "at_queue.h" diff --git a/at_read.c b/at_read.c index 5bf7d74f..2109a50a 100644 --- a/at_read.c +++ b/at_read.c @@ -9,18 +9,15 @@ Copyright (C) 2010 - 2011 bg */ -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ - #ifndef _GNU_SOURCE #define _GNU_SOURCE /* vasprintf() in asterisk/utils.h */ #endif /* #ifndef _GNU_SOURCE */ +#include "ast_config.h" + #include #include -#include #include /* ast_waitfor_n_fd() */ #include /* ast_debug() */ diff --git a/at_response.c b/at_response.c index 18b7b21d..6d20c345 100644 --- a/at_response.c +++ b/at_response.c @@ -8,11 +8,8 @@ bg */ -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ +#include "ast_config.h" -#include #include /* ast_debug() */ #include /* ast_pbx_start() */ diff --git a/chan_dongle.c b/chan_dongle.c index f6f03306..22459c3f 100644 --- a/chan_dongle.c +++ b/chan_dongle.c @@ -31,11 +31,7 @@ * * \ingroup channel_drivers */ -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ - -#include +#include "ast_config.h" #if ASTERISK_VERSION_NUM < 140000 /* 14- */ ASTERISK_FILE_VERSION(__FILE__, "$Rev: " PACKAGE_REVISION " $") diff --git a/chan_dongle.h b/chan_dongle.h index 362cffca..44a96282 100644 --- a/chan_dongle.h +++ b/chan_dongle.h @@ -12,7 +12,8 @@ #ifndef CHAN_DONGLE_H_INCLUDED #define CHAN_DONGLE_H_INCLUDED -#include +#include "ast_config.h" + #include #include diff --git a/channel.c b/channel.c index b61ff84e..b08fa800 100644 --- a/channel.c +++ b/channel.c @@ -8,11 +8,8 @@ bg */ -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ +#include "ast_config.h" -#include #include /* ast_dsp_digitreset() */ #include /* pbx_builtin_setvar_helper() */ #include /* ast_module_ref() ast_module_info = shit */ diff --git a/channel.h b/channel.h index 3ad75a2b..5b775687 100644 --- a/channel.h +++ b/channel.h @@ -4,7 +4,8 @@ #ifndef CHAN_DONGLE_CHANNEL_H_INCLUDED #define CHAN_DONGLE_CHANNEL_H_INCLUDED -#include +#include "ast_config.h" + #include /* enum ast_control_frame_type */ #include "ast_compat.h" /* asterisk compatibility fixes */ diff --git a/char_conv.c b/char_conv.c index 4cc737e9..8f443e78 100644 --- a/char_conv.c +++ b/char_conv.c @@ -8,9 +8,7 @@ Copyright (C) 2010 - 2011 bg */ -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ +#include "ast_config.h" #include diff --git a/cli.c b/cli.c index b13e222a..24859ed9 100644 --- a/cli.c +++ b/cli.c @@ -8,9 +8,7 @@ bg */ -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ +#include "ast_config.h" #include #include /* struct ast_cli_entry; struct ast_cli_args */ diff --git a/configure.in b/configure.in index 7d19e52f..96147de7 100644 --- a/configure.in +++ b/configure.in @@ -206,12 +206,17 @@ AC_CHECK_FUNCS([memchr memmove memset memmem strcasecmp strchr strncasecmp strto dnl Apply options to defines if test "x$enable_debug" = "xyes" ; then - CFLAGS="$CFLAGS -O0 -g" + CFLAGS="$CFLAGS -O0 -g " AC_DEFINE([__DEBUG__], [1], [Build with debugging]) else CFLAGS="$CFLAGS -O6" fi +dnl Asterisk header files use lots of old style declarations, ignore those. +dnl > warning: ‘inline’ is not at beginning of declaration [-Wold-style-declaration] +dnl > static void force_inline _ast_assert(...) +CFLAGS="$CFLAGS -Wno-old-style-declaration" + if test "x$enable_manager" = "xyes" ; then AC_DEFINE([BUILD_MANAGER],[1], [Build Manager extentions]) fi diff --git a/cpvt.c b/cpvt.c index e0dc9dba..babe4289 100644 --- a/cpvt.c +++ b/cpvt.c @@ -1,14 +1,11 @@ /* Copyright (C) 2010,2011 bg */ -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ +#include "ast_config.h" #include #include -#include #include #include "cpvt.h" diff --git a/cpvt.h b/cpvt.h index 97b34ec5..cfdae646 100644 --- a/cpvt.h +++ b/cpvt.h @@ -4,7 +4,8 @@ #ifndef CHAN_DONGLE_CPVT_H_INCLUDED #define CHAN_DONGLE_CPVT_H_INCLUDED -#include +#include "ast_config.h" + #include /* AST_LIST_ENTRY() */ #include /* AST_FRIENDLY_OFFSET */ diff --git a/dc_config.c b/dc_config.c index 806f789c..f25331f1 100644 --- a/dc_config.c +++ b/dc_config.c @@ -1,10 +1,6 @@ /* Copyright (C) 2010 bg */ -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ - #include "dc_config.h" #include /* ast_parse_caller_presentation() */ diff --git a/dc_config.h b/dc_config.h index bb19cee1..02858586 100644 --- a/dc_config.h +++ b/dc_config.h @@ -4,7 +4,8 @@ #ifndef CHAN_DONGLE_DC_CONFIG_H_INCLUDED #define CHAN_DONGLE_DC_CONFIG_H_INCLUDED -#include +#include "ast_config.h" + #include /* AST_MAX_CONTEXT MAX_LANGUAGE */ #include "export.h" /* EXPORT_DECL EXPORT_DEF */ diff --git a/helpers.c b/helpers.c index d5948c28..bbf7e6ca 100644 --- a/helpers.c +++ b/helpers.c @@ -8,13 +8,10 @@ bg */ -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ +#include "ast_config.h" #include /* SIGURG */ -#include #include /* AST_PRES_* */ #include "helpers.h" diff --git a/manager.c b/manager.c index f30ec55a..3555d3d2 100644 --- a/manager.c +++ b/manager.c @@ -1,7 +1,3 @@ -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ - #ifdef BUILD_MANAGER /* no manager, no copyright */ /* Copyright (C) 2009 - 2010 @@ -13,8 +9,8 @@ bg */ +#include "ast_config.h" -#include #include /* AST_DECLARE_STRING_FIELDS for asterisk/manager.h */ #include /* struct mansession, struct message ... */ #include /* ast_strlen_zero() */ diff --git a/memmem.c b/memmem.c index 2fe8891d..bfd4f7b2 100644 --- a/memmem.c +++ b/memmem.c @@ -1,7 +1,3 @@ -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ - #ifndef HAVE_MEMMEM /*- * Copyright (c) 2005 Pascal Gloor @@ -30,8 +26,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ - #include "memmem.h" + #include /* memchr() memcmp() NULL */ /* diff --git a/memmem.h b/memmem.h index d1b86255..86020570 100644 --- a/memmem.h +++ b/memmem.h @@ -4,10 +4,6 @@ #ifndef CHAN_DONGLE_MEMMEM_H_INCLUDED #define CHAN_DONGLE_MEMMEM_H_INCLUDED -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ - #ifdef HAVE_MEMMEM #ifndef _GNU_SOURCE diff --git a/mixbuffer.c b/mixbuffer.c index b5898a91..74a80416 100644 --- a/mixbuffer.c +++ b/mixbuffer.c @@ -1,11 +1,8 @@ /* Copyright (C) 2010 bg */ -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ +#include "ast_config.h" -#include #include /* ast_slinear_saturated_add() */ #include "mixbuffer.h" diff --git a/mixbuffer.h b/mixbuffer.h index c9b41bb1..356a0837 100644 --- a/mixbuffer.h +++ b/mixbuffer.h @@ -5,7 +5,8 @@ #ifndef CHAN_DONGLE_MIXBUFFER_H_INCLUDED #define CHAN_DONGLE_MIXBUFFER_H_INCLUDED -#include +#include "ast_config.h" + #include /* AST_LIST_ENTRY() AST_LIST_HEAD_NOLOCK() */ #include "ringbuffer.h" diff --git a/mutils.h b/mutils.h index ebc65634..d264f6dc 100644 --- a/mutils.h +++ b/mutils.h @@ -6,6 +6,8 @@ #include "export.h" +#include + #define ITEMS_OF(x) (sizeof(x)/sizeof((x)[0])) #define STRLEN(string) (sizeof(string)-1) diff --git a/pdiscovery.c b/pdiscovery.c index 0ac2877e..f7712775 100644 --- a/pdiscovery.c +++ b/pdiscovery.c @@ -1,9 +1,7 @@ /* Copyright (C) 2011 bg */ -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ +#include "ast_config.h" #include /* u_int16_t u_int8_t */ #include /* DIR */ diff --git a/pdu.c b/pdu.c index a4803765..54dbbb7b 100644 --- a/pdu.c +++ b/pdu.c @@ -1,9 +1,7 @@ /* Copyright (C) 2010 bg */ -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ +#include "ast_config.h" #include /* EINVAL ENOMEM E2BIG */ diff --git a/ringbuffer.c b/ringbuffer.c index 08429ab0..f8ce01ac 100644 --- a/ringbuffer.c +++ b/ringbuffer.c @@ -6,9 +6,7 @@ Dmitry Vagin */ -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ +#include "ast_config.h" #include "memmem.h" #include /* memchr() */ From 6f316d1e16a1440434158d7b37824410dde207f9 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Sun, 2 Oct 2016 16:12:42 +0200 Subject: [PATCH 024/117] Add travis file. --- .travis.yml | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..82367d1e --- /dev/null +++ b/.travis.yml @@ -0,0 +1,34 @@ +# Config + +language: c +sudo: false +dist: trusty +cache: bundler +notifications: + email: false + +env: + - ASTVER=14 + +# Extend build matrix +matrix: + include: + - env: ASTVER=13 + - env: ASTVER=12 + - env: ASTVER=11 + - env: ASTVER=10 + - env: ASTVER=1.8 + +before_install: + - wget http://junk.devs.nu/asterisk-$ASTVER-include.tar.bz2 + - tar jxf asterisk-$ASTVER-include.tar.bz2 + +before_script: + - aclocal + - autoconf + - automake -a || true + - ASTVERINT=$(echo $ASTVER | sed -e 's/^[^.]*$/&.0/;s/\./0/')00 + - ./configure --with-astversion=$ASTVERINT --with-asterisk=./asterisk-$ASTVER/include + +script: + - make clean all From a2c6a2434c4cc3a2c838c077beec6572f0fa0796 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Sun, 2 Oct 2016 16:30:46 +0200 Subject: [PATCH 025/117] Build fix for Asterisk 12. --- .travis.yml | 2 +- at_response.c | 6 +++--- chan_dongle.c | 6 ++++++ channel.c | 24 ++++++++++++------------ channel.h | 6 +++--- 5 files changed, 25 insertions(+), 19 deletions(-) diff --git a/.travis.yml b/.travis.yml index 82367d1e..bbd83663 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,7 +26,7 @@ before_install: before_script: - aclocal - autoconf - - automake -a || true + - automake -a || true # do install stuff, don't create makefile - ASTVERINT=$(echo $ASTVER | sed -e 's/^[^.]*$/&.0/;s/\./0/')00 - ./configure --with-astversion=$ASTVERINT --with-asterisk=./asterisk-$ASTVER/include diff --git a/at_response.c b/at_response.c index 6d20c345..3a026564 100644 --- a/at_response.c +++ b/at_response.c @@ -837,17 +837,17 @@ static int start_pbx(struct pvt* pvt, const char * number, int call_idx, call_st struct cpvt* cpvt; /* TODO: pass also Subscriber number or other DID info for exten */ -#if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ +#if ASTERISK_VERSION_NUM >= 120000 /* 12+ */ struct ast_channel * channel = new_channel( pvt, AST_STATE_RING, number, call_idx, CALL_DIR_INCOMING, state, pvt->has_subscriber_number ? pvt->subscriber_number : CONF_SHARED(pvt, exten), NULL, NULL); -#else /* 13- */ +#else /* 12- */ struct ast_channel * channel = new_channel( pvt, AST_STATE_RING, number, call_idx, CALL_DIR_INCOMING, state, pvt->has_subscriber_number ? pvt->subscriber_number : CONF_SHARED(pvt, exten), NULL); -#endif /* ^13- */ +#endif /* ^12- */ if (!channel) { diff --git a/chan_dongle.c b/chan_dongle.c index 22459c3f..25fdf87c 100644 --- a/chan_dongle.c +++ b/chan_dongle.c @@ -1663,9 +1663,15 @@ static int public_state_init(struct public_state * state) ast_format_cap_append(channel_tech.capabilities, ast_format_slin, 0); #elif ASTERISK_VERSION_NUM >= 100000 /* 10-13 */ ast_format_set(&chan_dongle_format, AST_FORMAT_SLINEAR, 0); +# if ASTERISK_VERSION_NUM >= 120000 /* 12+ */ + if (!(channel_tech.capabilities = ast_format_cap_alloc(0))) { + return AST_MODULE_LOAD_FAILURE; + } +# else if (!(channel_tech.capabilities = ast_format_cap_alloc())) { return AST_MODULE_LOAD_FAILURE; } +# endif ast_format_cap_add(channel_tech.capabilities, &chan_dongle_format); chan_dongle_format_cap = channel_tech.capabilities; #endif /* ^10-13 */ diff --git a/channel.c b/channel.c index b08fa800..d1c32a2d 100644 --- a/channel.c +++ b/channel.c @@ -102,7 +102,7 @@ EXPORT_DEF int channels_loop(struct pvt * pvt, const struct ast_channel * reques : 0; } -#if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ +#if ASTERISK_VERSION_NUM >= 120000 /* 12+ */ static struct ast_channel * channel_request( attribute_unused const char * type, struct ast_format_cap * cap, const struct ast_assigned_ids * assignedids, @@ -111,7 +111,7 @@ static struct ast_channel * channel_request( static struct ast_channel * channel_request( attribute_unused const char * type, struct ast_format_cap * cap, const struct ast_channel * requestor, const char * data, int * cause) -#elif ASTERISK_VERSION_NUM >= 100000 /* 10-13 */ +#elif ASTERISK_VERSION_NUM >= 100000 /* 10+ */ static struct ast_channel * channel_request( attribute_unused const char * type, struct ast_format_cap * cap, const struct ast_channel * requestor, void * data, int * cause) @@ -195,13 +195,13 @@ static struct ast_channel * channel_request( if(pvt) { -#if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ +#if ASTERISK_VERSION_NUM >= 120000 /* 12+ */ channel = new_channel(pvt, AST_STATE_DOWN, NULL, pvt_get_pseudo_call_idx(pvt), CALL_DIR_OUTGOING, CALL_STATE_INIT, NULL, assignedids, requestor); -#else /* 13- */ +#else /* 12- */ channel = new_channel(pvt, AST_STATE_DOWN, NULL, pvt_get_pseudo_call_idx(pvt), CALL_DIR_OUTGOING, CALL_STATE_INIT, NULL, requestor); -#endif /* ^13- */ +#endif /* ^12- */ ast_mutex_unlock (&pvt->lock); if(!channel) { @@ -835,11 +835,11 @@ static int channel_write (struct ast_channel* channel, struct ast_frame* f) if(CPVT_TEST_FLAG(cpvt, CALL_FLAG_MULTIPARTY) && !CPVT_TEST_FLAG(cpvt, CALL_FLAG_BRIDGE_CHECK)) { -#if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ +#if ASTERISK_VERSION_NUM >= 120000 /* 12+ */ RAII_VAR(struct ast_channel *, bridged, ast_channel_bridge_peer(channel), ast_channel_cleanup); -#else /* 13- */ +#else /* 12- */ struct ast_channel *bridged = ast_bridged_channel(channel); -#endif /* ^13- */ +#endif /* ^12- */ struct cpvt *tmp_cpvt; CPVT_SET_FLAGS(cpvt, CALL_FLAG_BRIDGE_CHECK); @@ -1215,7 +1215,7 @@ static void set_channel_vars(struct pvt* pvt, struct ast_channel* channel) } /* NOTE: called from device and current levels with locked pvt */ -#if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ +#if ASTERISK_VERSION_NUM >= 120000 /* 12+ */ EXPORT_DEF struct ast_channel* new_channel( struct pvt* pvt, int ast_state, const char* cid_num, int call_idx, unsigned dir, call_state_t state, const char * dnid, @@ -1234,7 +1234,7 @@ EXPORT_DEF struct ast_channel* new_channel( cpvt = cpvt_alloc(pvt, call_idx, dir, state); if (cpvt) { -#if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ +#if ASTERISK_VERSION_NUM >= 120000 /* 12+ */ channel = ast_channel_alloc( 1, ast_state, cid_num, PVT_ID(pvt), NULL, dnid, CONF_SHARED(pvt, context), assignedids, requestor, 0, @@ -1382,9 +1382,9 @@ EXPORT_DEF void start_local_channel (struct pvt* pvt, const char* exten, const c snprintf (channel_name, sizeof (channel_name), "%s@%s", exten, CONF_SHARED(pvt, context)); -#if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ +#if ASTERISK_VERSION_NUM >= 120000 /* 12+ */ channel = ast_request("Local", channel_tech.capabilities, NULL, NULL, channel_name, &cause); -#elif ASTERISK_VERSION_NUM >= 100000 /* 10-13 */ +#elif ASTERISK_VERSION_NUM >= 100000 /* 10-12 */ channel = ast_request("Local", chan_dongle_format_cap, NULL, channel_name, &cause); #elif ASTERISK_VERSION_NUM >= 10800 /* 1.8+ */ channel = ast_request("Local", AST_FORMAT_AUDIO_MASK, NULL, channel_name, &cause); diff --git a/channel.h b/channel.h index 5b775687..fe3e9408 100644 --- a/channel.h +++ b/channel.h @@ -24,18 +24,18 @@ struct cpvt; EXPORT_DECL struct ast_channel_tech channel_tech; -#if ASTERISK_VERSION_NUM >= 130000 /* 13+ */ +#if ASTERISK_VERSION_NUM >= 120000 /* 12+ */ EXPORT_DECL struct ast_channel* new_channel( struct pvt * pvt, int ast_state, const char * cid_num, int call_idx, unsigned dir, unsigned state, const char * exten, const struct ast_assigned_ids *assignedids, const struct ast_channel * requestor); -#else /* 13- */ +#else /* 12- */ EXPORT_DECL struct ast_channel* new_channel( struct pvt * pvt, int ast_state, const char * cid_num, int call_idx, unsigned dir, unsigned state, const char * exten, const struct ast_channel * requestor); -#endif /* ^13- */ +#endif /* ^12- */ EXPORT_DECL int queue_control_channel (struct cpvt * cpvt, enum ast_control_frame_type control); EXPORT_DECL int queue_hangup (struct ast_channel * channel, int hangupcause); EXPORT_DECL void start_local_channel (struct pvt * pvt, const char * exten, const char * number, channel_var_t * vars); From 8c657be3551c316bae350ddaced7dfee230d3765 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Sun, 2 Oct 2016 16:38:13 +0200 Subject: [PATCH 026/117] Add Travis badge. Update README to Markdown. --- README.md | 128 +++++++++++++++++++++++++++++++++++++++++++++++++++++ README.txt | 115 ----------------------------------------------- 2 files changed, 128 insertions(+), 115 deletions(-) create mode 100644 README.md delete mode 100644 README.txt diff --git a/README.md b/README.md new file mode 100644 index 00000000..1de66bce --- /dev/null +++ b/README.md @@ -0,0 +1,128 @@ + + Travis Build Status + + +chan\_dongle channel driver for Huawei UMTS cards +================================================= + +WARNING: + +This channel driver is in alpha stage. +I am not responsible if this channel driver will eat your money on +your SIM card or do any unpredicted things. + +Please use a recent Linux kernel, 2.6.33+ recommended. +If you use FreeBSD, 8.0+ recommended. + +This channel driver should work with the folowing UMTS cards: +* Huawei K3715 +* Huawei E169 / K3520 +* Huawei E155X +* Huawei E175X +* Huawei E261 +* Huawei K3765 + +Check complete list in: +http://wiki.e1550.mobi/doku.php?id=requirements#list_of_supported_models + +Before using the channel driver make sure to: +* Disable PIN code on your SIM card + +Supported features: +* Place voice calls and terminate voice calls +* Send SMS and receive SMS +* Send and receive USSD commands / messages + +Some useful AT commands: + + AT+CCWA=0,0,1 #disable call-waiting + AT+CFUN=1,1 #reset dongle + AT^CARDLOCK="" #unlock code + AT^SYSCFG=13,0,3FFFFFFF,0,3 #modem 2G only, automatic search any band, no roaming + AT^U2DIAG=0 #enable modem function + +Here is an example for the dialplan: +------------------------------------ + + [dongle-incoming] + exten => sms,1,Verbose(Incoming SMS from ${CALLERID(num)} ${BASE64_DECODE(${SMS_BASE64})}) + exten => sms,n,System(echo '${STRFTIME(${EPOCH},,%Y-%m-%d %H:%M:%S)} - ${DONGLENAME} - ${CALLERID(num)}: ${BASE64_DECODE(${SMS_BASE64})}' >> /var/log/asterisk/sms.txt) + exten => sms,n,Hangup() + + exten => ussd,1,Verbose(Incoming USSD: ${BASE64_DECODE(${USSD_BASE64})}) + exten => ussd,n,System(echo '${STRFTIME(${EPOCH},,%Y-%m-%d %H:%M:%S)} - ${DONGLENAME}: ${BASE64_DECODE(${USSD_BASE64})}' >> /var/log/asterisk/ussd.txt) + exten => ussd,n,Hangup() + + exten => s,1,Dial(SIP/2001@othersipserver) + exten => s,n,Hangup() + + [othersipserver-incoming] + + exten => _X.,1,Dial(Dongle/r1/${EXTEN}) + exten => _X.,n,Hangup + +You can also use this: +---------------------- + +Call using a specific group: + + exten => _X.,1,Dial(Dongle/g1/${EXTEN}) + +Call using a specific group in round robin: + + exten => _X.,1,Dial(Dongle/r1/${EXTEN}) + +Call using a specific dongle: + + exten => _X.,1,Dial(Dongle/dongle0/${EXTEN}) + +Call using a specific provider name: + + exten => _X.,1,Dial(Dongle/p:PROVIDER NAME/${EXTEN}) + +Call using a specific IMEI: + + exten => _X.,1,Dial(Dongle/i:123456789012345/${EXTEN}) + +Call using a specific IMSI prefix: + + exten => _X.,1,Dial(Dongle/s:25099203948/${EXTEN}) + +How to store your own number: + + dongle cmd dongle0 AT+CPBS=\"ON\" + dongle cmd dongle0 AT+CPBW=1,\"+123456789\",145 + + +Other CLI commands: +------------------- + + dongle reset + dongle restart gracefully + dongle restart now + dongle restart when convenient + dongle show device + dongle show devices + dongle show version + dongle sms number message + dongle ussd ussd + dongle stop gracefully + dongle stop now + dongle stop when convenient + dongle start + dongle restart gracefully + dongle restart now + dongle restart when convenient + dongle remove gracefully + dongle remove now + dongle remove when convenient + dongle reload gracefully + dongle reload now + dongle reload when convenient + +For reading installation notes please look to INSTALL file. + +For additional information about Huawei dongle usage look to +chan\_dongle Wiki at http://wiki.e1550.mobi and chan\_dongle project home at +https://github.com/wdoekes/asterisk-chan-dongle/ diff --git a/README.txt b/README.txt deleted file mode 100644 index 4a267049..00000000 --- a/README.txt +++ /dev/null @@ -1,115 +0,0 @@ --------------------------------------------------- -chan_dongle channel driver for Huawei UMTS cards --------------------------------------------------- - -WARNING: - -This channel driver is in alpha stage. -I am not responsible if this channel driver will eat your money on -your SIM card or do any unpredicted things. - -Please use a recent Linux kernel, 2.6.33+ recommended. -If you use FreeBSD, 8.0+ recommended. - -This channel driver should work with the folowing UMTS cards: -* Huawei K3715 -* Huawei E169 / K3520 -* Huawei E155X -* Huawei E175X -* Huawei E261 -* Huawei K3765 - -Check complete list in: -http://wiki.e1550.mobi/doku.php?id=requirements#list_of_supported_models - -Before using the channel driver make sure to: - -* Disable PIN code on your SIM card - -Supported features: -* Place voice calls and terminate voice calls -* Send SMS and receive SMS -* Send and receive USSD commands / messages - -Some useful AT commands: -AT+CCWA=0,0,1 #disable call-waiting -AT+CFUN=1,1 #reset dongle -AT^CARDLOCK="" #unlock code -AT^SYSCFG=13,0,3FFFFFFF,0,3 #modem 2G only, automatic search any band, no roaming -AT^U2DIAG=0 #enable modem function - -Here is an example for the dialplan: - -[dongle-incoming] -exten => sms,1,Verbose(Incoming SMS from ${CALLERID(num)} ${BASE64_DECODE(${SMS_BASE64})}) -exten => sms,n,System(echo '${STRFTIME(${EPOCH},,%Y-%m-%d %H:%M:%S)} - ${DONGLENAME} - ${CALLERID(num)}: ${BASE64_DECODE(${SMS_BASE64})}' >> /var/log/asterisk/sms.txt) -exten => sms,n,Hangup() - -exten => ussd,1,Verbose(Incoming USSD: ${BASE64_DECODE(${USSD_BASE64})}) -exten => ussd,n,System(echo '${STRFTIME(${EPOCH},,%Y-%m-%d %H:%M:%S)} - ${DONGLENAME}: ${BASE64_DECODE(${USSD_BASE64})}' >> /var/log/asterisk/ussd.txt) -exten => ussd,n,Hangup() - -exten => s,1,Dial(SIP/2001@othersipserver) -exten => s,n,Hangup() - -[othersipserver-incoming] - -exten => _X.,1,Dial(Dongle/r1/${EXTEN}) -exten => _X.,n,Hangup - -you can also use this: - -Call using a specific group: -exten => _X.,1,Dial(Dongle/g1/${EXTEN}) - -Call using a specific group in round robin: -exten => _X.,1,Dial(Dongle/r1/${EXTEN}) - -Call using a specific dongle: -exten => _X.,1,Dial(Dongle/dongle0/${EXTEN}) - -Call using a specific provider name: -exten => _X.,1,Dial(Dongle/p:PROVIDER NAME/${EXTEN}) - -Call using a specific IMEI: -exten => _X.,1,Dial(Dongle/i:123456789012345/${EXTEN}) - -Call using a specific IMSI prefix: -exten => _X.,1,Dial(Dongle/s:25099203948/${EXTEN}) - -How to store your own number: - -dongle cmd dongle0 AT+CPBS=\"ON\" -dongle cmd dongle0 AT+CPBW=1,\"+123456789\",145 - - -Other CLI commands: - -dongle reset -dongle restart gracefully -dongle restart now -dongle restart when convenient -dongle show device -dongle show devices -dongle show version -dongle sms number message -dongle ussd ussd -dongle stop gracefully -dongle stop now -dongle stop when convenient -dongle start -dongle restart gracefully -dongle restart now -dongle restart when convenient -dongle remove gracefully -dongle remove now -dongle remove when convenient -dongle reload gracefully -dongle reload now -dongle reload when convenient - -For reading installation notes please look to INSTALL file. - -For additional information about Huawei dongle usage -look to chan_dongle Wiki at http://wiki.e1550.mobi -and chan_dongle project home at https://github.com/bg111/asterisk-chan-dongle/ From 428a746e23733c149d49ea3a8857af3fc20cc79f Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Sun, 2 Oct 2016 21:39:58 +0200 Subject: [PATCH 027/117] Unbreak compiled module loading broken in c24a3150. Fixes #15 but doesn't explain it yet. --- app.c | 7 +++++-- manager.c | 6 +++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/app.c b/app.c index 732ff3de..6ea5e935 100644 --- a/app.c +++ b/app.c @@ -1,3 +1,7 @@ +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + #ifdef BUILD_APPLICATIONS /* Copyright (C) 2009 - 2010 @@ -10,8 +14,7 @@ bg */ -#include "ast_config.h" - +#include #include /* AST_DECLARE_APP_ARGS() ... */ #include /* pbx_builtin_setvar_helper() */ #include /* ast_register_application2() ast_unregister_application() */ diff --git a/manager.c b/manager.c index 3555d3d2..f30ec55a 100644 --- a/manager.c +++ b/manager.c @@ -1,3 +1,7 @@ +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + #ifdef BUILD_MANAGER /* no manager, no copyright */ /* Copyright (C) 2009 - 2010 @@ -9,8 +13,8 @@ bg */ -#include "ast_config.h" +#include #include /* AST_DECLARE_STRING_FIELDS for asterisk/manager.h */ #include /* struct mansession, struct message ... */ #include /* ast_strlen_zero() */ From dcf4f68e1352458657623518bfbd1e085ed56052 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Sun, 2 Oct 2016 21:45:02 +0200 Subject: [PATCH 028/117] Better fix for breakage from c24a3150. Closes #15. --- app.c | 6 ++---- configure.in | 6 +++--- manager.c | 6 ++---- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/app.c b/app.c index 6ea5e935..73fa337c 100644 --- a/app.c +++ b/app.c @@ -1,6 +1,5 @@ -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ +/* Required outside the BUILD_APPLICATIONS #ifdef! */ +#include "ast_config.h" #ifdef BUILD_APPLICATIONS /* @@ -14,7 +13,6 @@ bg */ -#include #include /* AST_DECLARE_APP_ARGS() ... */ #include /* pbx_builtin_setvar_helper() */ #include /* ast_register_application2() ast_unregister_application() */ diff --git a/configure.in b/configure.in index 96147de7..f1de803f 100644 --- a/configure.in +++ b/configure.in @@ -206,7 +206,7 @@ AC_CHECK_FUNCS([memchr memmove memset memmem strcasecmp strchr strncasecmp strto dnl Apply options to defines if test "x$enable_debug" = "xyes" ; then - CFLAGS="$CFLAGS -O0 -g " + CFLAGS="$CFLAGS -O0 -g" AC_DEFINE([__DEBUG__], [1], [Build with debugging]) else CFLAGS="$CFLAGS -O6" @@ -218,11 +218,11 @@ dnl > static void force_inline _ast_assert(...) CFLAGS="$CFLAGS -Wno-old-style-declaration" if test "x$enable_manager" = "xyes" ; then - AC_DEFINE([BUILD_MANAGER],[1], [Build Manager extentions]) + AC_DEFINE([BUILD_MANAGER],[1], [Build Manager extensions]) fi if test "x$enable_apps" = "xyes" ; then - AC_DEFINE([BUILD_APPLICATIONS],[1],[Build extention applications]) + AC_DEFINE([BUILD_APPLICATIONS],[1],[Build Application extensions]) fi case "$target_os" in diff --git a/manager.c b/manager.c index f30ec55a..df747d9a 100644 --- a/manager.c +++ b/manager.c @@ -1,6 +1,5 @@ -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ +/* Required outside the BUILD_MANAGER #ifdef! */ +#include "ast_config.h" #ifdef BUILD_MANAGER /* no manager, no copyright */ /* @@ -14,7 +13,6 @@ bg */ -#include #include /* AST_DECLARE_STRING_FIELDS for asterisk/manager.h */ #include /* struct mansession, struct message ... */ #include /* ast_strlen_zero() */ From e221694297ac992d78d50c8eacf5e578bb8c0fb7 Mon Sep 17 00:00:00 2001 From: fadasi Date: Thu, 6 Oct 2016 19:21:52 +0200 Subject: [PATCH 029/117] Adds a DongleSendUSSD function (Device, USSD) dialplan function Inspired from http://a.dmin.pro/?p=2712 (presumably October 4, 2011 by arturius) Also adds DongleSendUSSD and DongleSendSMS dialplan functions examples For more visibility and traceability, the unmodified code style has not been changed --- README.md | 12 ++++++++++++ app.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/README.md b/README.md index 1de66bce..0687b51e 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,18 @@ Here is an example for the dialplan: exten => _X.,1,Dial(Dongle/r1/${EXTEN}) exten => _X.,n,Hangup + exten => *#123#,1,DongleSendUSSD(dongle0,${EXTEN}) + exten => *#123#,n,Answer() + exten => *#123#,n,Wait(2) + exten => *#123#,n,Playback(vm-goodbye) + exten => *#123#,n,Hangup() + + exten => _#X.,1,DongleSendSMS(dongle0,${EXTEN:1},"Please call me",1440,yes) + exten => _#X.,n,Answer() + exten => _#X.,n,Wait(2) + exten => _#X.,n,Playback(vm-goodbye) + exten => _#X.,n,Hangup() + You can also use this: ---------------------- diff --git a/app.c b/app.c index 73fa337c..3a3ce371 100644 --- a/app.c +++ b/app.c @@ -113,6 +113,46 @@ static int app_send_sms_exec (attribute_unused struct ast_channel* channel, cons return !status; } +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); + AST_APP_ARG(ussd); + ); + + if (ast_strlen_zero(data)) + { + return -1; + } + + parse = ast_strdupa(data); + + AST_STANDARD_APP_ARGS(args, parse); + + if (ast_strlen_zero(args.device)) + { + ast_log(LOG_ERROR, "NULL device for ussd -- USSD will not be sent\n"); + return -1; + } + + if (ast_strlen_zero(args.ussd)) + { + ast_log(LOG_ERROR, "NULL ussd command -- USSD will not be sent\n"); + 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); + } + return !status; +} static const struct dongle_application @@ -143,6 +183,14 @@ static const struct dongle_application " Message - text of the message\n" " Validity - Validity period in minutes\n" " Report - Boolean flag for report request\n" + }, + { + "DongleSendUSSD", + app_send_ussd_exec, + "DongleSendUSSD(Device,USSD)", + "DongleSendUSSD(Device,USSD)\n" + " Device - Id of device from dongle.conf\n" + " USSD - ussd command\n" } }; From 2a5a097b27e49ebf291cdf91e9f60cdf0df3627c Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Sun, 13 Nov 2016 18:05:36 +0100 Subject: [PATCH 030/117] Clarify ./bootstrap procedure. Drop ancient INSTALL docs. Clarifies the "no makefile.am" problem from #18, but doesn't fix it. --- .gitignore | 1 + INSTALL | 34 ---------------------------------- Makefile.in | 9 +++++++-- README.md | 7 +++++++ bootstap | 8 ++++++++ config.h.in | 6 +++--- configure.in => configure.ac | 0 7 files changed, 26 insertions(+), 39 deletions(-) delete mode 100644 INSTALL create mode 100755 bootstap rename configure.in => configure.ac (100%) diff --git a/.gitignore b/.gitignore index 9170b951..bc334a9a 100644 --- a/.gitignore +++ b/.gitignore @@ -17,4 +17,5 @@ /configure /install-sh /missing +/stamp-h /stamp-h1 diff --git a/INSTALL b/INSTALL deleted file mode 100644 index fc169afe..00000000 --- a/INSTALL +++ /dev/null @@ -1,34 +0,0 @@ -1) For build and install from svn sources - 1.1) Goto package source directory - - 1.2) Run aclocal - - 1.3) Run autoconf - autoconf - 1.4) Install missing files - automake -a - continue with step 2.3) - -2) For build and install from package - 2.1) Unpack sources - tar xzvf package.tgz - 2.2) Goto package source directory - 2.3) Configure package - simple - ./configure - explicite set install directory for module - DESTDIR="/usr/lib/asterisk/modules" ./configure - turn off some parts - ./configure --disable-manager --disable-apps - explicite set path to asterisk headers - ./configure --with-asterisk=/usr/src/asterisk-1.6.2.13/include - or - CFLAGS="-I /usr/src/asterisk-1.6.2.13/include" ./configure - enable debugging - ./configure --enable-debug - 2.4) Build package - make - 2.5) Install package - make install - -More documentation on http://wiki.e1550.mobi/ diff --git a/Makefile.in b/Makefile.in index 776de428..9590bc00 100644 --- a/Makefile.in +++ b/Makefile.in @@ -26,7 +26,7 @@ HEADERS = app.h at_command.h at_parse.h at_queue.h at_read.h at_response.h \ tools_HEADERS = tools/tty.h EXTRA_DIST = BUGS COPYRIGHT.txt LICENSE.txt README.txt TODO.txt INSTALL \ - Makefile.in config.h.in configure.in stamp-h.in etc contrib + Makefile.in config.h.in configure.ac stamp-h.in etc contrib BUILD_TOOLS = configure config.sub install-sh missing config.guess @@ -81,6 +81,11 @@ tools/discovery: $(discovery_OBJS) clean: $(RM) $(PROJM) $(PROJS) *.o *.core .*.d autom4te.cache test/test1 test/*.o tools/discovery test/*.o +distclean: clean + $(RM) Makefile aclocal.m4 compile \ + config.guess config.h config.log config.status config.sub \ + configure install-sh missing stamp-h stamp-h1 + dist: $(SOURCES) $(HEADERS) $(EXTRA_DIST) $(BUILD_TOOLS) @mkdir $(DISTNAME) $(DISTNAME)/test $(DISTNAME)/tools @cp -a $(SOURCES) $(HEADERS) $(EXTRA_DIST) $(BUILD_TOOLS) $(DISTNAME) @@ -89,7 +94,7 @@ dist: $(SOURCES) $(HEADERS) $(EXTRA_DIST) $(BUILD_TOOLS) tar czf $(DISTNAME).tgz $(DISTNAME) --exclude .svn -h @$(RM) $(DISTNAME) -configure: configure.in +configure: configure.ac autoconf config.h: stamp-h diff --git a/README.md b/README.md index 0687b51e..f9236f7e 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,13 @@ Some useful AT commands: AT^SYSCFG=13,0,3FFFFFFF,0,3 #modem 2G only, automatic search any band, no roaming AT^U2DIAG=0 #enable modem function +Building: +---------- + + $ ./bootstrap + $ ./configure --with-astversion=131200 + $ make + Here is an example for the dialplan: ------------------------------------ diff --git a/bootstap b/bootstap new file mode 100755 index 00000000..ac637f2b --- /dev/null +++ b/bootstap @@ -0,0 +1,8 @@ +#!/bin/sh + +aclocal +autoconf + +# We don't use Makefile.am, but we *do* use automake to add +# "missing" files used by configure. +automake -a 2>&1 | grep -vF Makefile.am diff --git a/config.h.in b/config.h.in index 6ac3f327..6e0ec346 100644 --- a/config.h.in +++ b/config.h.in @@ -1,4 +1,4 @@ -/* config.h.in. Generated from configure.in by autoheader. */ +/* config.h.in. Generated from configure.ac by autoheader. */ /* The Asterisk version as configured --with-astversion. */ #undef ASTERISK_VERSION_NUM @@ -6,10 +6,10 @@ /* name of asterisk module */ #undef AST_MODULE -/* Build extention applications */ +/* Build Application extensions */ #undef BUILD_APPLICATIONS -/* Build Manager extentions */ +/* Build Manager extensions */ #undef BUILD_MANAGER /* Define to 1 if you have HAVE_AST_CONTROL_SRCCHANGE in asterisk/frame.h */ diff --git a/configure.in b/configure.ac similarity index 100% rename from configure.in rename to configure.ac From 283f3998ef1000b308657dd47c15b1f2cbe0b675 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Fri, 20 Jan 2017 11:44:00 +0100 Subject: [PATCH 031/117] bootstrap: Fix file naming (not 'bootstap'). Reported by @k008 in ghwdoekes/asterisk-chan-dongle issue #20. --- bootstap => bootstrap | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename bootstap => bootstrap (100%) diff --git a/bootstap b/bootstrap similarity index 100% rename from bootstap rename to bootstrap From 8bbba90ec2d5de7214705395b923e12efdb73a86 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Fri, 20 Jan 2017 11:59:53 +0100 Subject: [PATCH 032/117] build: Improve --with-astversion to take X.Y.Z version. This should clarify a few things. --- README.md | 2 +- configure.ac | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f9236f7e..a1a36a29 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ Building: ---------- $ ./bootstrap - $ ./configure --with-astversion=131200 + $ ./configure --with-astversion=13.13.1 # if running Asterisk-13.13.1 $ make Here is an example for the dialplan: diff --git a/configure.ac b/configure.ac index f1de803f..31efa32b 100644 --- a/configure.ac +++ b/configure.ac @@ -27,9 +27,11 @@ AC_ARG_WITH( dnl Required: asterisk version AC_ARG_WITH( [astversion], - AS_HELP_STRING([--with-astversion=Mmmuu], [set asterisk version; examples: 1.8.13 => 10813, 11.1.2 => 110102]), - [ if ! test "$((with_astversion + 0))" -eq "$with_astversion" ; then AC_MSG_ERROR([Invalid --with-astversion=Mmmuu value]); fi ], - [ AC_MSG_ERROR([Please set --with-astversion=Mmmuu (major.minor.micro)]) ] + AS_HELP_STRING([--with-astversion=M.m.u], [set asterisk version; for example: 13.13.1]), + [ if echo "$with_astversion" | grep -q '^[[0-9]][[0-9]]\?\.[[0-9]][[0-9]]\?\.[[0-9]][[0-9]]\?$' + then with_astversion=$(echo $with_astversion | tr . '\n' | sed -e 's/^.$/0&/' | tr -d '\n') + else AC_MSG_ERROR([Invalid --with-astversion=M.m.u value]); fi ], + [ AC_MSG_ERROR([Please set --with-astversion=M.m.u (major.minor.micro)]) ] ) AC_DEFINE_UNQUOTED([ASTERISK_VERSION_NUM], [$with_astversion], [The Asterisk version as configured --with-astversion.]) From 5b99b7518a4a6c072bc7595b0168accc81c15d3c Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Fri, 20 Jan 2017 12:03:07 +0100 Subject: [PATCH 033/117] build: Explain that you may use --with-asterisk on configure. Closes ghwdoekes/asterisk-chan-dongle issue #20. --- README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a1a36a29..c6540484 100644 --- a/README.md +++ b/README.md @@ -46,9 +46,16 @@ Building: ---------- $ ./bootstrap - $ ./configure --with-astversion=13.13.1 # if running Asterisk-13.13.1 + $ ./configure --with-astversion=13.13.1 $ make +If you run a different version of Asterisk, you'll need to update the +`13.13.1` as appropriate, obviously. + +If you did not `make install` Asterisk in the usual location and configure +cannot find the asterisk header files in `/usr/include/asterisk`, you may +optionally pass `--with-asterisk=PATH/TO/INCLUDE`. + Here is an example for the dialplan: ------------------------------------ From 48df4f606a0b7ea0aa67daec5090be45f004d3e9 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Fri, 20 Jan 2017 13:26:27 +0100 Subject: [PATCH 034/117] build: Fix builds for Asterisk < 10.y.z. And fix Travis CI-test. --- .travis.yml | 3 +-- README.md | 4 ++-- configure.ac | 7 ++++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index bbd83663..7072da2b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,8 +27,7 @@ before_script: - aclocal - autoconf - automake -a || true # do install stuff, don't create makefile - - ASTVERINT=$(echo $ASTVER | sed -e 's/^[^.]*$/&.0/;s/\./0/')00 - - ./configure --with-astversion=$ASTVERINT --with-asterisk=./asterisk-$ASTVER/include + - ./configure --with-astversion=$ASTVER --with-asterisk=./asterisk-$ASTVER/include script: - make clean all diff --git a/README.md b/README.md index c6540484..b02cb8df 100644 --- a/README.md +++ b/README.md @@ -46,11 +46,11 @@ Building: ---------- $ ./bootstrap - $ ./configure --with-astversion=13.13.1 + $ ./configure --with-astversion=13.7 $ make If you run a different version of Asterisk, you'll need to update the -`13.13.1` as appropriate, obviously. +`13.7` as appropriate, obviously. If you did not `make install` Asterisk in the usual location and configure cannot find the asterisk header files in `/usr/include/asterisk`, you may diff --git a/configure.ac b/configure.ac index 31efa32b..8fc2bd59 100644 --- a/configure.ac +++ b/configure.ac @@ -27,9 +27,10 @@ AC_ARG_WITH( dnl Required: asterisk version AC_ARG_WITH( [astversion], - AS_HELP_STRING([--with-astversion=M.m.u], [set asterisk version; for example: 13.13.1]), - [ if echo "$with_astversion" | grep -q '^[[0-9]][[0-9]]\?\.[[0-9]][[0-9]]\?\.[[0-9]][[0-9]]\?$' - then with_astversion=$(echo $with_astversion | tr . '\n' | sed -e 's/^.$/0&/' | tr -d '\n') + AS_HELP_STRING([--with-astversion=M.m.u], [set asterisk version; for example: 14 or 13.13.1]), + [ if echo "$with_astversion" | grep -qE '^[[0-9]][[0-9]]?(\.[[0-9]][[0-9]]?){0,2}$' + then with_astversion=$(echo $with_astversion | sed -e 's/.*/&.0.0/' | tr . '\n' | + head -n3 | sed -e 's/^.$/0&/' | tr -d '\n' | sed -e 's/^0*//') else AC_MSG_ERROR([Invalid --with-astversion=M.m.u value]); fi ], [ AC_MSG_ERROR([Please set --with-astversion=M.m.u (major.minor.micro)]) ] ) From fba6ed16b79fa3fc6a5dc8e7776166468f9af8fb Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Sun, 16 Apr 2017 17:20:32 +0200 Subject: [PATCH 035/117] pdu/tests: Clean up pdu_parse if-tree and restore test capability. make check to build and run tests. Cleanups were required to even begin attempting to fix #28. --- Makefile.in | 4 + at_response.c | 2 +- pdu.c | 309 ++++++++++++++++++++++---------------------------- test/parse.c | 58 ++++++++-- test/test1.c | 5 +- 5 files changed, 190 insertions(+), 188 deletions(-) diff --git a/Makefile.in b/Makefile.in index 9590bc00..1c7a6094 100644 --- a/Makefile.in +++ b/Makefile.in @@ -65,6 +65,10 @@ $(PROJS): $(chan_dongles_so_OBJS) Makefile .c.o: Makefile config.h $(CC) $(CFLAGS) $(MAKE_DEPS) -o $@ -c $< +check: tests + ./test/test1 + ./test/parse + tests: test/test1 test/parse test/test1: $(test1_OBJS) diff --git a/at_response.c b/at_response.c index 3a026564..3a4e36ff 100644 --- a/at_response.c +++ b/at_response.c @@ -1225,7 +1225,7 @@ static int at_response_cmgr (struct pvt* pvt, const char * str, size_t len) } else if (err) { - ast_log (LOG_WARNING, "[%s] Error parsing incoming message '%s' at possition %d: %s\n", PVT_ID(pvt), str, (int)(err_pos - cmgr), 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; } diff --git a/pdu.c b/pdu.c index 54dbbb7b..1e172c7f 100644 --- a/pdu.c +++ b/pdu.c @@ -278,9 +278,9 @@ /* bit 4 */ #define PDU_DCS_BITS10_CTRL_SHIFT 4 -#define PDU_DCS_BITS10_RETAIN (0x00 << PDU_DCS_BIT10_CTRL_SHIFT) -#define PDU_DCS_BITS10_INUSE (0x01 << PDU_DCS_BIT10_CTRL_SHIFT) -#define PDU_DCS_BITS10_CTRL_MASK (0x01 << PDU_DCS_BIT10_CTRL_SHIFT) +#define PDU_DCS_BITS10_RETAIN (0x00 << PDU_DCS_BITS10_CTRL_SHIFT) +#define PDU_DCS_BITS10_INUSE (0x01 << PDU_DCS_BITS10_CTRL_SHIFT) +#define PDU_DCS_BITS10_CTRL_MASK (0x01 << PDU_DCS_BITS10_CTRL_SHIFT) #define PDU_DCS_BITS10_CTRL(dcs) ((dcs) & PDU_DCS_BITS10_CTRL_MASK) /* bit 5 */ @@ -704,185 +704,146 @@ static str_encoding_t pdu_dcs_alpabet2encoding(int alpabet) /* 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) { - const char * err = NULL; size_t pdu_length = strlen(*pdu); + int field_len, pdu_type, oa_digits, oa_toa, pid, dcs, ts, udl, udhl; + + /* set msg as NULL until the end */ + *msg = NULL; /* decode SCA */ - int field_len = pdu_parse_sca(pdu, &pdu_length); - if(field_len > 0) + field_len = pdu_parse_sca(pdu, &pdu_length); + if (field_len <= 0) { + return "Can't parse SCA"; + } + + if (tpdu_length * 2 != pdu_length) { + return "TPDU length not matched with actual length"; + } + + pdu_type = pdu_parse_byte(pdu, &pdu_length); + if (pdu_type < 0) { + return "Can't parse PDU Type"; + } + + /* TODO: also handle PDUTYPE_MTI_SMS_SUBMIT_REPORT and PDUTYPE_MTI_SMS_STATUS_REPORT */ + if (PDUTYPE_MTI(pdu_type) == PDUTYPE_MTI_SMS_STATUS_REPORT) { - if(tpdu_length * 2 == pdu_length) - { - int pdu_type = pdu_parse_byte(pdu, &pdu_length); - if(pdu_type >= 0) - { - /* TODO: also handle PDUTYPE_MTI_SMS_SUBMIT_REPORT and PDUTYPE_MTI_SMS_STATUS_REPORT */ - if(PDUTYPE_MTI(pdu_type) == PDUTYPE_MTI_SMS_DELIVER) - { - int oa_digits = pdu_parse_byte(pdu, &pdu_length); - if(oa_digits > 0) - { - int oa_toa; - field_len = pdu_parse_number(pdu, &pdu_length, oa_digits, &oa_toa, oa, oa_len); - if(field_len > 0) - { - int pid = pdu_parse_byte(pdu, &pdu_length); - *oa_enc = STR_ENCODING_7BIT; - if ((oa_toa & TP_A_TON) == TP_A_TON_ALPHANUMERIC) { - *oa_enc = STR_ENCODING_7BIT_HEX; - } - if (pid >= 0) - { - /* TODO: support other types of messages */ - if( (pid == PDU_PID_SMS) || (pid & PDU_PID_SMS_REPLACE_MASK) ) - { - int dcs = pdu_parse_byte(pdu, &pdu_length); - if(dcs >= 0) - { - // TODO: support compression - if( PDU_DCS_76(dcs) == PDU_DCS_76_00 - && - PDU_DCS_COMPRESSION(dcs) == PDU_DCS_NOT_COMPESSED - && - ( - PDU_DCS_ALPABET(dcs) == PDU_DCS_ALPABET_7BIT - || - PDU_DCS_ALPABET(dcs) == PDU_DCS_ALPABET_8BIT - || - PDU_DCS_ALPABET(dcs) == PDU_DCS_ALPABET_UCS2 - ) - ) - { - int ts = pdu_parse_timestamp(pdu, &pdu_length); - *msg_enc = pdu_dcs_alpabet2encoding(PDU_DCS_ALPABET(dcs)); - if(ts >= 0) - { - int udl = pdu_parse_byte(pdu, &pdu_length); - if(udl >= 0) - { - /* calculate number of octets in UD */ - if(PDU_DCS_ALPABET(dcs) == PDU_DCS_ALPABET_7BIT) - udl = ((udl + 1) * 7) >> 3; - if((size_t)udl * 2 == pdu_length) - { - if(PDUTYPE_UDHI(pdu_type) == PDUTYPE_UDHI_HAS_HEADER) - { - /* TODO: implement header parse */ - int udhl = pdu_parse_byte(pdu, &pdu_length); - if(udhl >= 0) - { - /* NOTE: UDHL count octets no need calculation */ - if(pdu_length >= (size_t)(udhl * 2)) - { - /* skip UDH */ - *pdu += udhl * 2; - pdu_length -= udhl * 2; - } - else - { - err = "Invalid UDH"; - } - } - else - { - err = "Can't parse UDHL"; - } - } - /* save message */ - *msg = *pdu; - } - else - { - *pdu -= 2; - err = "UDL not match with UD length"; - } - } - else - { - err = "Can't parse UDL"; - } - } - else - { - err = "Can't parse Timestamp"; - } - } - else - { - *pdu -= 2; - err = "Unsupported DCS value"; - } - } - else - { - err = "Can't parse DSC"; - } - } - else - { - err = "Unhandled PID value, only SMS supported"; - } - } - else - { - err = "Can't parse PID"; - } - } - else - { - err = "Can't parse OA"; - } - } - else - { - err = "Can't parse length of OA"; - } - } - else if(PDUTYPE_MTI(pdu_type) == PDUTYPE_MTI_SMS_STATUS_REPORT) - { - 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) { - err = (void*)0x1; /* HACK! */ - *msg = (char*)(ssize_t)reference; /* HACK! */ - } else { - err = "Good report, but delivery failed"; - } - } else { - err = "FIXME error 1"; - } - } else { - err = "FIXME error 2"; - } - } - else - { - *pdu -= 2; - err = "Unhandled PDU Type MTI only SMS-DELIVER/SMS-STATUS-REPORT supported"; + 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 - { - err = "Can't parse PDU Type"; - } - } - else - { - err = "TPDU length not matched with actual length"; + } else { + ret = "FIXME error 2"; } + return ret; } - else - { - err = "Can't parse SCA"; + if (PDUTYPE_MTI(pdu_type) != PDUTYPE_MTI_SMS_DELIVER) { + *pdu -= 2; + return "Unhandled PDU Type MTI only SMS-DELIVER/SMS-STATUS-REPORT supported"; + } + + oa_digits = pdu_parse_byte(pdu, &pdu_length); + if (oa_digits <= 0) { + return "Can't parse length of OA"; + } + + field_len = pdu_parse_number(pdu, &pdu_length, oa_digits, &oa_toa, oa, oa_len); + if (field_len <= 0) { + return "Can't parse OA"; + } + + pid = pdu_parse_byte(pdu, &pdu_length); + *oa_enc = STR_ENCODING_7BIT; + if ((oa_toa & TP_A_TON) == TP_A_TON_ALPHANUMERIC) { + *oa_enc = STR_ENCODING_7BIT_HEX; + } + + if (pid < 0) { + return "Can't parse PID"; + } + + /* TODO: support other types of messages */ + if (pid != PDU_PID_SMS && !(pid & PDU_PID_SMS_REPLACE_MASK)) { + return "Unhandled PID value, only SMS supported"; + } + + dcs = pdu_parse_byte(pdu, &pdu_length); + if (dcs < 0) { + return "Can't parse DSC"; + } + + /* TODO: support compression */ + if (!(PDU_DCS_76(dcs) == PDU_DCS_76_00 + && + PDU_DCS_COMPRESSION(dcs) == PDU_DCS_NOT_COMPESSED + && + ( + PDU_DCS_ALPABET(dcs) == PDU_DCS_ALPABET_7BIT + || + PDU_DCS_ALPABET(dcs) == PDU_DCS_ALPABET_8BIT + || + PDU_DCS_ALPABET(dcs) == PDU_DCS_ALPABET_UCS2 + ))) { + *pdu -= 2; + return "Unsupported DCS value"; + } + + ts = pdu_parse_timestamp(pdu, &pdu_length); + *msg_enc = pdu_dcs_alpabet2encoding(PDU_DCS_ALPABET(dcs)); + if(ts < 0) { + return "Can't parse Timestamp"; + } + + udl = pdu_parse_byte(pdu, &pdu_length); + if (udl < 0) { + return "Can't parse UDL"; + } + + /* calculate number of octets in UD */ + if (PDU_DCS_ALPABET(dcs) == PDU_DCS_ALPABET_7BIT) { + udl = ((udl + 1) * 7) >> 3; + } + if ((size_t)udl * 2 != pdu_length) { + *pdu -= 2; + return "UDL not match with UD length"; + } + + /* save message */ + *msg = *pdu; + + if (PDUTYPE_UDHI(pdu_type) != PDUTYPE_UDHI_HAS_HEADER) { + return NULL; + } + + /* TODO: implement header parse */ + udhl = pdu_parse_byte(pdu, &pdu_length); + if (udhl < 0) { + return "Can't parse UDHL"; + } + + /* NOTE: UDHL count octets no need calculation */ + if (pdu_length < (size_t)(udhl * 2)) { + return "Invalid UDH"; } - return err; + /* skip UDH */ + *pdu += udhl * 2; + pdu_length -= udhl * 2; + return NULL; } diff --git a/test/parse.c b/test/parse.c index 37d50101..ed0da082 100644 --- a/test/parse.c +++ b/test/parse.c @@ -157,6 +157,20 @@ void test_parse_cmti() fprintf(stderr, "\n"); } +int safe_strcmp(const char *a, const char *b) +{ + if (a == NULL && b == NULL) { + return 0; + } + if (a == NULL) { + return -1; + } + if (b == NULL) { + return 1; + } + return strcmp(a, b); +} + #/* */ void test_parse_cmgr() { @@ -208,11 +222,27 @@ void test_parse_cmgr() "0031003100310031003100310031003100310031003200320032003200320032003200320032003200330033003300330033003300330033003300330034003400340034003400340034003400340034003500350035003500350035003500350035003500360036003600360036003600360036003600360037003700370037003700370037", "+22222222022", STR_ENCODING_7BIT, + "050003000301" /* FIXME: what is this? data header? why is this here? test failure without it... */ "0031003100310031003100310031003100310031003200320032003200320032003200320032003200330033003300330033003300330033003300330034003400340034003400340034003400340034003500350035003500350035003500350035003500360036003600360036003600360036003600360037003700370037003700370037", STR_ENCODING_UCS2_HEX } }, - +#if 0 + { "+CMGR: 0,,137\r\n07919333851805320409D034186C360300F0713032810105408849A7F1099A36A720D9EC059BB140319C2E06D38186EF39FD0D1AA3D3E176981E06155D20184B467381926CD0585E26A7E96F1001547481683816ACE60241CB7250DA6D7E83E67550D95E76D3EB61761AF486EBD36F771A14A6D3D3F632A80C12BFDDF539485E9EA7C9F534688C4E87DB61100D968BD95C", + /* 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. */ + { + NULL, + "49A7F1099A36A720D9EC059BB140319C2E06D38186EF39FD0D1AA3D3E176981E06155D20184B467381926CD0585E26A7E96F1001547481683816ACE60241CB7250DA6D7E83E67550D95E76D3EB61761AF486EBD36F771A14A6D3D3F632A80C12BFDDF539485E9EA7C9F534688C4E87DB61100D968BD95C", + "09D034186C3603", /* from 40033 .. ? */ + STR_ENCODING_7BIT, /* ? */ + "49A7F1099A36A720D9EC059BB140319C2E06D38186EF39FD0D1AA3D3E176981E06155D20184B467381926CD0585E26A7E96F1001547481683816ACE60241CB7250DA6D7E83E67550D95E76D3EB61761AF486EBD36F771A14A6D3D3F632A80C12BFDDF539485E9EA7C9F534688C4E87DB61100D968BD95C", + STR_ENCODING_UCS2_HEX /* ? */ + } + }, +#endif }; unsigned idx = 0; @@ -222,24 +252,28 @@ void test_parse_cmgr() const char * msg; result.oa = oa; - for(; idx < ITEMS_OF(cases); ++idx) { + for (; idx < ITEMS_OF(cases); ++idx) { + int failidx = 0; result.str = input = strdup(cases[idx].input); - fprintf(stderr, "%s(\"%s\")...", "at_parse_cmgr", input); + 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); - if(((result.res == NULL && result.res == cases[idx].result.res) || strcmp(result.res, cases[idx].result.res) == 0) - && strcmp(result.str, cases[idx].result.str) == 0 - && strcmp(result.oa, cases[idx].result.oa) == 0 - && result.oa_enc == cases[idx].result.oa_enc - && strcmp(result.msg, cases[idx].result.msg) == 0 - && result.msg_enc == cases[idx].result.msg_enc) + if (++failidx && safe_strcmp(result.res, cases[idx].result.res) == 0 && + ++failidx && safe_strcmp(result.str, cases[idx].result.str) == 0 && + ++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) { msg = "OK"; ok++; + failidx = 0; } else { msg = "FAIL"; faults++; } - fprintf(stderr, " = '%s' ('%s','%s',%d,'%s',%d)\t%s\n", result.res, result.str, result.oa, result.oa_enc, result.msg, result.msg_enc, msg); + fprintf(stderr, " = '%s' ('%s','%s',%d,'%s',%d) [fail@%d]\t%s\n", + result.res, result.str, result.oa, result.oa_enc, result.msg, result.msg_enc, failidx, msg); free(input); } fprintf(stderr, "\n"); @@ -389,5 +423,9 @@ int main() test_parse_ccwa(); fprintf(stderr, "done %d tests: %d OK %d FAILS\n", ok + faults, ok, faults); + + if (faults) { + return 1; + } return 0; } diff --git a/test/test1.c b/test/test1.c index 8ba05373..0f65525e 100644 --- a/test/test1.c +++ b/test/test1.c @@ -96,9 +96,8 @@ void test_suite1() } #/* */ -int main(int argc, char * argv[]) +int main() { test_suite1(); - return 0; -} \ No newline at end of file +} From 2152a58cba7b3015c0f51e8903e62854590ae028 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Sun, 16 Apr 2017 19:00:39 +0200 Subject: [PATCH 036/117] pdu/tests: Add tests for multipart 7bit encoded message decoding. See bug #13. This creates stub tests. They're disabled, since it's broken at the moment. --- pdu.c | 4 +++ test/parse.c | 91 +++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 79 insertions(+), 16 deletions(-) diff --git a/pdu.c b/pdu.c index 1e172c7f..a8248ebf 100644 --- a/pdu.c +++ b/pdu.c @@ -845,5 +845,9 @@ EXPORT_DEF const char * pdu_parse(char ** pdu, size_t tpdu_length, char * oa, si /* skip UDH */ *pdu += udhl * 2; pdu_length -= udhl * 2; + + /* TODO: 7-bit alphabetet coding scheme doesn't properly skip past the udh! + * see github.com/wdoekes/asterisk-chan-dongle/issues/13 */ + return NULL; } diff --git a/test/parse.c b/test/parse.c index ed0da082..850892c9 100644 --- a/test/parse.c +++ b/test/parse.c @@ -181,29 +181,32 @@ void test_parse_cmgr() str_encoding_t oa_enc; char * msg; str_encoding_t msg_enc; + char * msg_utf8; }; 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", + { "+CMGR: \"REC READ\",\"+79139131234\",,\"10/12/05,22:00:04+12\"\r\n041F04400438043204350442", { NULL, "\"REC READ\",\"+79139131234", "+79139131234", STR_ENCODING_7BIT, "041F04400438043204350442", - STR_ENCODING_UNKNOWN + STR_ENCODING_UNKNOWN, + NULL } }, - { "+CMGR: \"REC READ\",\"002B00370039003500330037003600310032003000350032\",,\"10/12/05,22:00:04+12\"\r\n041F04400438043204350442", + { "+CMGR: \"REC READ\",\"002B00370039003500330037003600310032003000350032\",,\"10/12/05,22:00:04+12\"\r\n041F04400438043204350442", { - NULL, + NULL, "\"REC READ\",\"002B00370039003500330037003600310032003000350032", - "002B00370039003500330037003600310032003000350032", + "002B00370039003500330037003600310032003000350032", STR_ENCODING_UNKNOWN, "041F04400438043204350442", - STR_ENCODING_UNKNOWN + STR_ENCODING_UNKNOWN, + NULL } }, { "+CMGR: 0,,106\r\n07911111111100F3040B911111111111F200000121702214952163B1582C168BC562B1984C2693C96432994C369BCD66B3D96C369BD168341A8D46A3D168B55AAD56ABD56AB59ACD66B3D96C369BCD76BBDD6EB7DBED76BBE170381C0E87C3E170B95C2E97CBE572B91C0C0683C16030180C", @@ -213,8 +216,9 @@ void test_parse_cmgr() "+11111111112", STR_ENCODING_7BIT, "B1582C168BC562B1984C2693C96432994C369BCD66B3D96C369BD168341A8D46A3D168B55AAD56ABD56AB59ACD66B3D96C369BCD76BBDD6EB7DBED76BBE170381C0E87C3E170B95C2E97CBE572B91C0C0683C16030180C", - STR_ENCODING_7BIT_HEX - } + STR_ENCODING_7BIT_HEX, + "111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999000000000" + } }, { "+CMGR: 0,,159\r\n07919740430900F3440B912222222220F20008012180004390218C0500030003010031003100310031003100310031003100310031003200320032003200320032003200320032003200330033003300330033003300330033003300330034003400340034003400340034003400340034003500350035003500350035003500350035003500360036003600360036003600360036003600360037003700370037003700370037", { @@ -222,10 +226,35 @@ void test_parse_cmgr() "0031003100310031003100310031003100310031003200320032003200320032003200320032003200330033003300330033003300330033003300330034003400340034003400340034003400340034003500350035003500350035003500350035003500360036003600360036003600360036003600360037003700370037003700370037", "+22222222022", STR_ENCODING_7BIT, - "050003000301" /* FIXME: what is this? data header? why is this here? test failure without it... */ + "050003000301" /* This is including a 6 octet UDH; see .str above for the 'text' */ "0031003100310031003100310031003100310031003200320032003200320032003200320032003200330033003300330033003300330033003300330034003400340034003400340034003400340034003500350035003500350035003500350035003500360036003600360036003600360036003600360037003700370037003700370037", - STR_ENCODING_UCS2_HEX - } + STR_ENCODING_UCS2_HEX, + "1111111111222222222233333333334444444444555555555566666666667777777" + } + }, + { "+CMGR: 0,,159\r\n07913306000000F0440B913306000000F0000061011012939280A0050003CA020182E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3", + { + NULL, + "82E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3", + "+33600000000", + STR_ENCODING_7BIT, + "050003CA020182E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3", + STR_ENCODING_7BIT_HEX, + //"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" + "[broken! see github.com/wdoekes/asterisk-chan-dongle/issues/13]" + } + }, + { "+CMGR: 0,,43\r\n07913306000000F0640B913306000000F00000610110129303801B050003CA0202C26150301C0E8741C170381C0605C3E17018", + { + NULL, + "C26150301C0E8741C170381C0605C3E17018", + "+33600000000", + STR_ENCODING_7BIT, + "050003CA0202C26150301C0E8741C170381C0605C3E17018", + STR_ENCODING_7BIT_HEX, + //"aa Aaaaa Aaaaa Aaaaa" + "[broken! see github.com/wdoekes/asterisk-chan-dongle/issues/13]" + } }, #if 0 { "+CMGR: 0,,137\r\n07919333851805320409D034186C360300F0713032810105408849A7F1099A36A720D9EC059BB140319C2E06D38186EF39FD0D1AA3D3E176981E06155D20184B467381926CD0585E26A7E96F1001547481683816ACE60241CB7250DA6D7E83E67550D95E76D3EB61761AF486EBD36F771A14A6D3D3F632A80C12BFDDF539485E9EA7C9F534688C4E87DB61100D968BD95C", @@ -253,16 +282,41 @@ void test_parse_cmgr() result.oa = oa; for (; idx < ITEMS_OF(cases); ++idx) { + char buf[4096]; + int res; int failidx = 0; result.str = input = strdup(cases[idx].input); + result.msg_utf8 = buf; + 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.res = at_parse_cmgr( + &result.str, strlen(result.str), result.oa, sizeof(oa), &result.oa_enc, + &result.msg, &result.msg_enc); + + /* convert to utf8 representation */ + result.msg_utf8 = NULL; + if (!result.res) { + if (result.str != result.msg && + result.msg_enc == STR_ENCODING_7BIT_HEX) { + strcpy(buf, "[broken! see github.com/wdoekes/asterisk-chan-dongle/issues/13]"); + result.msg_utf8 = buf; + } else if (result.msg_enc != STR_ENCODING_UNKNOWN) { + if (str_recode( + RECODE_DECODE, result.msg_enc, + result.str, strlen(result.str), + buf, sizeof(buf)) >= 0) { + result.msg_utf8 = buf; + } + } + } + if (++failidx && safe_strcmp(result.res, cases[idx].result.res) == 0 && ++failidx && safe_strcmp(result.str, cases[idx].result.str) == 0 && ++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 && ++failidx) { msg = "OK"; @@ -272,8 +326,9 @@ void test_parse_cmgr() msg = "FAIL"; faults++; } - fprintf(stderr, " = '%s' ('%s','%s',%d,'%s',%d) [fail@%d]\t%s\n", - result.res, result.str, result.oa, result.oa_enc, result.msg, result.msg_enc, failidx, msg); + 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); free(input); } fprintf(stderr, "\n"); @@ -378,7 +433,9 @@ void test_parse_clcc() for(; idx < ITEMS_OF(cases); ++idx) { input = strdup(cases[idx].input); fprintf(stderr, "%s(\"%s\")...", "at_parse_clcc", input); - result.res = at_parse_clcc(input, &result.index, &result.dir, &result.stat, &result.mode, &result.mpty, &result.number, &result.toa); + result.res = at_parse_clcc( + input, &result.index, &result.dir, &result.stat, &result.mode, + &result.mpty, &result.number, &result.toa); if(result.res == cases[idx].result.res && result.index == cases[idx].result.index && result.dir == cases[idx].result.dir @@ -394,7 +451,9 @@ void test_parse_clcc() msg = "FAIL"; faults++; } - fprintf(stderr, " = %d (%d,%d,%d,%d,%d,\"%s\",%d)\t%s\n", result.res, result.index, result.dir, result.stat, result.mode, result.mpty, result.number, result.toa, msg); + fprintf(stderr, " = %d (%d,%d,%d,%d,%d,\"%s\",%d)\t%s\n", + result.res, result.index, result.dir, result.stat, result.mode, + result.mpty, result.number, result.toa, msg); free(input); } fprintf(stderr, "\n"); From 9f32405f5eadd8f4e3c4dc5b6b2307e8eba39210 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Sun, 16 Apr 2017 21:16:57 +0200 Subject: [PATCH 037/117] char_conv: Handle 7bit encoded multi-part SMS correctly. Most of this patch was taken from: https://github.com/bg111/asterisk-chan-dongle/issues/183 ==> https://github.com/bg111/asterisk-chan-dongle/pull/214 Some changes were elided because: - of uncertainty of their workings - they did not improve the results of the available tests ---- From 2aba9dd30da7bfe69377c7b4cbf313ce01fa347c Mon Sep 17 00:00:00 2001 From: Rodrigo Freire Date: Fri, 28 Aug 2015 16:10:20 -0300 Subject: [PATCH] Resolves bg111/asterisk-chan-dongle#183 From: HPS From: Shabbira Subject: Parses correctly multi-part SMS messages Comitter: Rodrigo Freire Signed-off-by: Rodrigo Freire This patch was retrieved from https://code.google.com/p/asterisk-chan-dongle/issues/detail?id=183 Now parses correctly multi-part SMS Messages. Verified on a Huawei E303S. ---- Closes #13. --- at_command.c | 2 +- at_response.c | 2 +- char_conv.c | 250 +++++++++++++++++++++++++++++++++----------------- char_conv.h | 8 +- pdu.c | 164 ++++++++++++++++++++++----------- test/parse.c | 66 ++++++++----- 6 files changed, 329 insertions(+), 163 deletions(-) diff --git a/at_command.c b/at_command.c index 33f0673b..7d7bdb4a 100644 --- a/at_command.c +++ b/at_command.c @@ -415,7 +415,7 @@ EXPORT_DEF int at_enque_ussd (struct cpvt * cpvt, const char * code, attribute_u length = STRLEN(cmd); if (pvt->cusd_use_7bit_encoding) - cusd_encoding = STR_ENCODING_7BIT_HEX; + cusd_encoding = STR_ENCODING_7BIT_HEX_PAD_0; else if (pvt->use_ucs2_encoding) cusd_encoding = STR_ENCODING_UCS2_HEX; else diff --git a/at_response.c b/at_response.c index 3a4e36ff..4427ed1f 100644 --- a/at_response.c +++ b/at_response.c @@ -1369,7 +1369,7 @@ static int at_response_cusd (struct pvt * pvt, char * str, size_t len) // FIXME: strictly check USSD encoding and detect encoding if ((dcs == 0 || dcs == 15) && !pvt->cusd_use_ucs2_decoding) - ussd_encoding = STR_ENCODING_7BIT_HEX; + ussd_encoding = STR_ENCODING_7BIT_HEX_PAD_0; else ussd_encoding = STR_ENCODING_UCS2_HEX; res = str_recode (RECODE_DECODE, ussd_encoding, cusd, strlen (cusd), cusd_utf8_str, sizeof (cusd_utf8_str)); diff --git a/char_conv.c b/char_conv.c index 8f443e78..ab9c8e52 100644 --- a/char_conv.c +++ b/char_conv.c @@ -163,107 +163,184 @@ static ssize_t utf8_to_hexstr_ucs2 (const char* in, size_t in_length, char* out, return res; } -static ssize_t char_to_hexstr_7bit (const char* in, size_t in_length, char* out, size_t out_size) +static ssize_t char_to_hexstr_7bit_padded(const char* in, size_t in_length, char* out, + size_t out_size, unsigned out_padding) { - size_t i; - size_t x = 0; - size_t s; - unsigned char c; - unsigned char b; - char buf[] = { 0x0, 0x0, 0x0 }; - - x = (in_length - in_length / 8) * 2; - if (out_size - 1 < x) - { + 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; - } - - if(in_length > 0) - { - in_length--; - for (i = 0, x = 0, s = 0; i < in_length; i++) - { - if (s == 7) - { - s = 0; - continue; - } - - c = in[i] >> s; - b = in[i + 1] << (7 - s); - c = c | b; - s++; - - snprintf (buf, sizeof(buf), "%.2X", c); - - memcpy (out + x, buf, 2); - x = x + 2; - } - c = in[i] >> s; - snprintf (buf, sizeof(buf), "%.2X", c); + 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; + } + /* zero terminate final string */ out[x] = '\0'; + /* return total string length, excluding terminating zero */ return x; } -static ssize_t hexstr_7bit_to_char (const char* in, size_t in_length, char* out, size_t out_size) +static ssize_t char_to_hexstr_7bit_pad_0(const char* in, size_t in_length, char* out, + size_t out_size) { - size_t i; - size_t x; - size_t s; - int hexval; - unsigned char c; - unsigned char b; - char buf[] = { 0x0, 0x0, 0x0 }; - - in_length = in_length / 2; - x = in_length + in_length / 7; - if (out_size - 1 < x) - { - return -1; + return char_to_hexstr_7bit_padded(in, in_length, out, out_size, 0); +} + +static ssize_t char_to_hexstr_7bit_pad_1(const char* in, size_t in_length, char* out, + size_t out_size) +{ + return char_to_hexstr_7bit_padded(in, in_length, out, out_size, 1); +} + +static ssize_t char_to_hexstr_7bit_pad_2(const char* in, size_t in_length, char* out, + size_t out_size) +{ + return char_to_hexstr_7bit_padded(in, in_length, out, out_size, 2); +} + +static ssize_t char_to_hexstr_7bit_pad_3(const char* in, size_t in_length, char* out, + size_t out_size) +{ + return char_to_hexstr_7bit_padded(in, in_length, out, out_size, 3); +} + +static ssize_t char_to_hexstr_7bit_pad_4(const char* in, size_t in_length, char* out, + size_t out_size) +{ + return char_to_hexstr_7bit_padded(in, in_length, out, out_size, 4); +} + +static ssize_t char_to_hexstr_7bit_pad_5(const char* in, size_t in_length, char* out, + size_t out_size) +{ + return char_to_hexstr_7bit_padded(in, in_length, out, out_size, 5); +} + +static ssize_t char_to_hexstr_7bit_pad_6(const char* in, size_t in_length, char* out, + size_t out_size) +{ + return char_to_hexstr_7bit_padded(in, in_length, out, out_size, 6); +} + +static ssize_t hexstr_7bit_to_char_padded(const char* in, size_t in_length, char* out, + size_t out_size, unsigned in_padding) +{ + size_t i; + size_t x; + char buf[4]; + unsigned value = 0; + unsigned hexval; + + /* compute number of bytes */ + in_length /= 2; + + /* check if string is empty */ + if (in_length == 0) { + out[0] = '\0'; + return (0); } - for (i = 0, x = 0, s = 1, b = 0; i < in_length; i++) - { - memcpy (buf, in + i * 2, 2); - if (sscanf (buf, "%x", &hexval) != 1) - { - return -1; - } + /* compute number of characters */ + x = (((in_length * 8) - in_padding) / 7) + 1 /* terminating zero */; - c = ((unsigned char) hexval) << s; - c = (c >> 1) | b; - b = ((unsigned char) hexval) >> (8 - s); + /* check if final string fits within buffer */ + if (x > out_size) + return -1; - if (c == 0 && i + 1 < in_length) { - /* @ is encoded as NUL */ - c = '@'; - } + /* account for the bit padding */ + in_padding = 7 - in_padding; - out[x] = c; - x++; s++; + /* clear temporary buffer */ + memset(buf, 0, sizeof(buf)); - if (s == 8) - { - if (b == 0 && i + 1 < in_length) { - /* @ is encoded as NUL */ - b = '@'; - } - out[x] = b; - s = 1; b = 0; - x++; + /* parse the hexstring */ + for (x = i = 0; i != in_length; i++) { + memcpy (buf, in + i * 2, 2); + if (sscanf (buf, "%x", &hexval) != 1) + return -1; + value |= (hexval & 0xFF) << in_padding; + in_padding += 8; + + while (in_padding >= (2 * 7)) { + in_padding -= 7; + value >>= 7; + out[x++] = value & 0x7F; } } + /* zero terminate final string */ out[x] = '\0'; + /* return total string length, excluding terminating zero */ return x; } +static ssize_t hexstr_7bit_to_char_pad_0(const char* in, size_t in_length, char* out, + size_t out_size) +{ + return hexstr_7bit_to_char_padded(in, in_length, out, out_size, 0); +} + +static ssize_t hexstr_7bit_to_char_pad_1(const char* in, size_t in_length, char* out, + size_t out_size) +{ + return hexstr_7bit_to_char_padded(in, in_length, out, out_size, 1); +} + +static ssize_t hexstr_7bit_to_char_pad_2(const char* in, size_t in_length, char* out, + size_t out_size) +{ + return hexstr_7bit_to_char_padded(in, in_length, out, out_size, 2); +} + +static ssize_t hexstr_7bit_to_char_pad_3(const char* in, size_t in_length, char* out, + size_t out_size) +{ + return hexstr_7bit_to_char_padded(in, in_length, out, out_size, 3); +} + +static ssize_t hexstr_7bit_to_char_pad_4(const char* in, size_t in_length, char* out, + size_t out_size) +{ + return hexstr_7bit_to_char_padded(in, in_length, out, out_size, 4); +} + +static ssize_t hexstr_7bit_to_char_pad_5(const char* in, size_t in_length, char* out, + size_t out_size) +{ + return hexstr_7bit_to_char_padded(in, in_length, out, out_size, 5); +} + +static ssize_t hexstr_7bit_to_char_pad_6(const char* in, size_t in_length, char* out, + size_t out_size) +{ + return hexstr_7bit_to_char_padded(in, in_length, out, out_size, 6); +} + #/* */ ssize_t just_copy (const char* in, size_t in_length, char* out, size_t out_size) { @@ -279,14 +356,19 @@ ssize_t just_copy (const char* in, size_t in_length, char* out, size_t out_size) typedef ssize_t (*coder) (const char* in, size_t in_length, char* out, size_t out_size); -/* array in order of values RECODE_* */ -static const coder recoders[][2] = +/* array in order of values RECODE_* */ +static const coder recoders[STR_ENCODING_UNKNOWN][2] = { -/* in order of values STR_ENCODING_* */ - { hexstr_7bit_to_char, char_to_hexstr_7bit }, /* STR_ENCODING_7BIT_HEX */ - { hexstr_to_8bitchars, chars8bit_to_hexstr }, /* STR_ENCODING_8BIT_HEX */ - { hexstr_ucs2_to_utf8, utf8_to_hexstr_ucs2 }, /* STR_ENCODING_UCS2_HEX */ - { just_copy, just_copy }, /* STR_ENCODING_7BIT */ + [STR_ENCODING_7BIT_HEX_PAD_0] = { hexstr_7bit_to_char_pad_0, char_to_hexstr_7bit_pad_0 }, + [STR_ENCODING_8BIT_HEX] = { hexstr_to_8bitchars, chars8bit_to_hexstr }, + [STR_ENCODING_UCS2_HEX] = { hexstr_ucs2_to_utf8, utf8_to_hexstr_ucs2 }, + [STR_ENCODING_7BIT] = { just_copy, just_copy }, + [STR_ENCODING_7BIT_HEX_PAD_1] = { hexstr_7bit_to_char_pad_1, char_to_hexstr_7bit_pad_1 }, + [STR_ENCODING_7BIT_HEX_PAD_2] = { hexstr_7bit_to_char_pad_2, char_to_hexstr_7bit_pad_2 }, + [STR_ENCODING_7BIT_HEX_PAD_3] = { hexstr_7bit_to_char_pad_3, char_to_hexstr_7bit_pad_3 }, + [STR_ENCODING_7BIT_HEX_PAD_4] = { hexstr_7bit_to_char_pad_4, char_to_hexstr_7bit_pad_4 }, + [STR_ENCODING_7BIT_HEX_PAD_5] = { hexstr_7bit_to_char_pad_5, char_to_hexstr_7bit_pad_5 }, + [STR_ENCODING_7BIT_HEX_PAD_6] = { hexstr_7bit_to_char_pad_6, char_to_hexstr_7bit_pad_6 }, }; #/* */ @@ -306,7 +388,7 @@ EXPORT_DEF str_encoding_t get_encoding(recode_direction_t hint, const char* in, for(; length; --length, ++in) if(*in & 0x80) return STR_ENCODING_UCS2_HEX; - return STR_ENCODING_7BIT_HEX; + return STR_ENCODING_7BIT_HEX_PAD_0; } else { @@ -317,7 +399,7 @@ EXPORT_DEF str_encoding_t get_encoding(recode_direction_t hint, const char* in, return STR_ENCODING_7BIT; } } - // TODO: STR_ENCODING_7BIT_HEX or STR_ENCODING_8BIT_HEX or STR_ENCODING_UCS2_HEX + // TODO: STR_ENCODING_7BIT_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 5104d440..592f507a 100644 --- a/char_conv.h +++ b/char_conv.h @@ -11,11 +11,17 @@ /* 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_7BIT_HEX = 0, /* 7bit encoding */ + STR_ENCODING_7BIT_HEX_PAD_0 = 0, /* 7bit encoding, 0 bits of 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_7BIT, /* 7bit ASCII no need recode to utf-8 */ + STR_ENCODING_7BIT_HEX_PAD_1, /* 7bit encoding, 1 bit of padding */ + STR_ENCODING_7BIT_HEX_PAD_2, /* 7bit encoding, 2 bits of padding */ + STR_ENCODING_7BIT_HEX_PAD_3, /* 7bit encoding, 3 bits padding */ + STR_ENCODING_7BIT_HEX_PAD_4, /* 7bit encoding, 4 bits padding */ + STR_ENCODING_7BIT_HEX_PAD_5, /* 7bit encoding, 5 bits padding */ + STR_ENCODING_7BIT_HEX_PAD_6, /* 7bit encoding, 6 bits padding */ // STR_ENCODING_8BIT, /* 8bit */ // STR_ENCODING_UCS2, /* UCS2 */ STR_ENCODING_UNKNOWN, /* still unknown */ diff --git a/pdu.c b/pdu.c index a8248ebf..f3511bde 100644 --- a/pdu.c +++ b/pdu.c @@ -285,8 +285,8 @@ /* bit 5 */ #define PDU_DCS_COMPRESSION_SHIFT 5 -#define PDU_DCS_NOT_COMPESSED (0x00 << PDU_DCS_COMPRESSION_SHIFT) -#define PDU_DCS_COMPESSED (0x01 << PDU_DCS_COMPRESSION_SHIFT) +#define PDU_DCS_NOT_COMPRESSED (0x00 << PDU_DCS_COMPRESSION_SHIFT) +#define PDU_DCS_COMPRESSED (0x01 << PDU_DCS_COMPRESSION_SHIFT) #define PDU_DCS_COMPRESSION_MASK (0x01 << PDU_DCS_COMPRESSION_SHIFT) #define PDU_DCS_COMPRESSION(dcs) ((dcs) & PDU_DCS_COMPRESSION_MASK) @@ -375,8 +375,10 @@ static char pdu_code2digit(char code) case 'E': code = 'C'; break; + case 'f': case 'F': - return 0; + code = 0; + break; default: return -1; } @@ -469,50 +471,54 @@ failed parse 07 91 97 30 07 11 11 F1 04 14 D0 D9B09B5CC637DFEE721E00081170206 static int pdu_parse_number(char ** pdu, size_t * pdu_length, unsigned digits, int * toa, char * number, size_t num_len) { const char * begin; + unsigned syms; + char digit; - if(num_len < digits + 1) + if (num_len < digits + 1) { return -ENOMEM; + } begin = *pdu; *toa = pdu_parse_byte(pdu, pdu_length); - if (*toa >= 0) - { - unsigned syms = ROUND_UP2(digits); - if (syms <= *pdu_length) - { - char digit; - 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; - *number++ = digit; + if (*toa < 0) { + return -EINVAL; + } + + syms = ROUND_UP2(digits); + if (syms > *pdu_length) { + return -EINVAL; + } - digit = pdu_code2digit(pdu[0][0]); - if ((signed char)digit < 0 || (digit == 0 && (syms != 2 || (digits & 0x1) == 0))) - return -1; + 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; + } - *number++ = digit; - } - if ((digits & 0x1) == 0) - *number = 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; } + *number++ = digit; + + digit = pdu_code2digit(pdu[0][0]); + if ((signed char)digit < 0 || (digit == 0 && (syms != 2 || (digits & 0x1) == 0))) { + return -1; + } + + *number++ = digit; + } + if ((digits & 0x1) == 0) { + *number = 0; } - return -EINVAL; + return *pdu - begin; } @@ -553,7 +559,7 @@ static int pdu_parse_timestamp(char ** pdu, size_t * length) static int check_encoding(const char* msg, unsigned length) { str_encoding_t possible_enc = get_encoding(RECODE_ENCODE, msg, length); - if(possible_enc == STR_ENCODING_7BIT_HEX) + if(possible_enc == STR_ENCODING_7BIT_HEX_PAD_0) return PDU_DCS_ALPABET_7BIT; return PDU_DCS_ALPABET_UCS2; } @@ -639,7 +645,7 @@ EXPORT_DEF int pdu_build(char* buffer, size_t length, const char* sca, const cha len += pdu_store_number(buffer + len, dst, dst_len); /* forward TP-User-Data */ - data_len = str_recode(RECODE_ENCODE, dcs == PDU_DCS_ALPABET_UCS2 ? STR_ENCODING_UCS2_HEX : STR_ENCODING_7BIT_HEX, msg, msg_len, buffer + len + 8, length - len - 11); + data_len = str_recode(RECODE_ENCODE, dcs == PDU_DCS_ALPABET_UCS2 ? STR_ENCODING_UCS2_HEX : STR_ENCODING_7BIT_HEX_PAD_0, msg, msg_len, buffer + len + 8, length - len - 11); if(data_len < 0) { return -EINVAL; @@ -682,7 +688,7 @@ static str_encoding_t pdu_dcs_alpabet2encoding(int alpabet) switch(alpabet) { case (PDU_DCS_ALPABET_7BIT >> PDU_DCS_ALPABET_SHIFT): - rv = STR_ENCODING_7BIT_HEX; + rv = STR_ENCODING_7BIT_HEX_PAD_0; break; case (PDU_DCS_ALPABET_8BIT >> PDU_DCS_ALPABET_SHIFT): rv = STR_ENCODING_8BIT_HEX; @@ -716,10 +722,13 @@ EXPORT_DEF const char * pdu_parse(char ** pdu, size_t tpdu_length, char * oa, si return "Can't parse SCA"; } - if (tpdu_length * 2 != pdu_length) { + if (tpdu_length * 2 > pdu_length) { return "TPDU length not matched with actual length"; } + /* 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"; @@ -764,14 +773,14 @@ EXPORT_DEF const char * pdu_parse(char ** pdu, size_t tpdu_length, char * oa, si } field_len = pdu_parse_number(pdu, &pdu_length, oa_digits, &oa_toa, oa, oa_len); - if (field_len <= 0) { + if (field_len < 0) { return "Can't parse OA"; } pid = pdu_parse_byte(pdu, &pdu_length); *oa_enc = STR_ENCODING_7BIT; if ((oa_toa & TP_A_TON) == TP_A_TON_ALPHANUMERIC) { - *oa_enc = STR_ENCODING_7BIT_HEX; + *oa_enc = STR_ENCODING_7BIT_HEX_PAD_0; } if (pid < 0) { @@ -779,7 +788,7 @@ EXPORT_DEF const char * pdu_parse(char ** pdu, size_t tpdu_length, char * oa, si } /* TODO: support other types of messages */ - if (pid != PDU_PID_SMS && !(pid & PDU_PID_SMS_REPLACE_MASK)) { + if (pid != PDU_PID_SMS && !(0x41 <= pid && pid <= 0x47) /* PDU_PID_SMS_REPLACE_MASK */) { return "Unhandled PID value, only SMS supported"; } @@ -791,7 +800,7 @@ EXPORT_DEF const char * pdu_parse(char ** pdu, size_t tpdu_length, char * oa, si /* TODO: support compression */ if (!(PDU_DCS_76(dcs) == PDU_DCS_76_00 && - PDU_DCS_COMPRESSION(dcs) == PDU_DCS_NOT_COMPESSED + PDU_DCS_COMPRESSION(dcs) == PDU_DCS_NOT_COMPRESSED && ( PDU_DCS_ALPABET(dcs) == PDU_DCS_ALPABET_7BIT @@ -824,30 +833,81 @@ EXPORT_DEF const char * pdu_parse(char ** pdu, size_t tpdu_length, char * oa, si return "UDL not match with UD length"; } - /* save message */ - *msg = *pdu; - if (PDUTYPE_UDHI(pdu_type) != PDUTYPE_UDHI_HAS_HEADER) { + /* save message */ + *msg = *pdu; return NULL; } - /* TODO: implement header parse */ udhl = pdu_parse_byte(pdu, &pdu_length); if (udhl < 0) { return "Can't parse UDHL"; } + /* adjust 7-bit padding */ + if (*msg_enc == STR_ENCODING_7BIT_HEX_PAD_0) { + switch (6 - (udhl % 7)) { + case 1: + *msg_enc = STR_ENCODING_7BIT_HEX_PAD_1; + break; + case 2: + *msg_enc = STR_ENCODING_7BIT_HEX_PAD_2; + break; + case 3: + *msg_enc = STR_ENCODING_7BIT_HEX_PAD_3; + break; + case 4: + *msg_enc = STR_ENCODING_7BIT_HEX_PAD_4; + break; + case 5: + *msg_enc = STR_ENCODING_7BIT_HEX_PAD_5; + break; + case 6: + *msg_enc = STR_ENCODING_7BIT_HEX_PAD_6; + break; + default: + /* no change */ + break; + } + } + /* NOTE: UDHL count octets no need calculation */ if (pdu_length < (size_t)(udhl * 2)) { return "Invalid UDH"; } - /* skip UDH */ + while (udhl >= 2) { + int iei_len; + + /* get type byte */ + (void)pdu_parse_byte(pdu, &pdu_length); /* iei_type */ + + + /* 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) { + /* skip rest of IEI */ + *pdu += iei_len * 2; + pdu_length -= iei_len * 2; + udhl -= iei_len; + } + else + { + return "Invalid IEI len"; + } + } + + /* skip rest of UDH, if any */ *pdu += udhl * 2; pdu_length -= udhl * 2; - /* TODO: 7-bit alphabetet coding scheme doesn't properly skip past the udh! - * see github.com/wdoekes/asterisk-chan-dongle/issues/13 */ + /* save message */ + *msg = *pdu; return NULL; } diff --git a/test/parse.c b/test/parse.c index 850892c9..36bf601d 100644 --- a/test/parse.c +++ b/test/parse.c @@ -216,7 +216,7 @@ void test_parse_cmgr() "+11111111112", STR_ENCODING_7BIT, "B1582C168BC562B1984C2693C96432994C369BCD66B3D96C369BD168341A8D46A3D168B55AAD56ABD56AB59ACD66B3D96C369BCD76BBDD6EB7DBED76BBE170381C0E87C3E170B95C2E97CBE572B91C0C0683C16030180C", - STR_ENCODING_7BIT_HEX, + STR_ENCODING_7BIT_HEX_PAD_0, "111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999000000000" } }, @@ -226,7 +226,6 @@ void test_parse_cmgr() "0031003100310031003100310031003100310031003200320032003200320032003200320032003200330033003300330033003300330033003300330034003400340034003400340034003400340034003500350035003500350035003500350035003500360036003600360036003600360036003600360037003700370037003700370037", "+22222222022", STR_ENCODING_7BIT, - "050003000301" /* This is including a 6 octet UDH; see .str above for the 'text' */ "0031003100310031003100310031003100310031003200320032003200320032003200320032003200330033003300330033003300330033003300330034003400340034003400340034003400340034003500350035003500350035003500350035003500360036003600360036003600360036003600360037003700370037003700370037", STR_ENCODING_UCS2_HEX, "1111111111222222222233333333334444444444555555555566666666667777777" @@ -238,10 +237,9 @@ void test_parse_cmgr() "82E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3", "+33600000000", STR_ENCODING_7BIT, - "050003CA020182E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3", - STR_ENCODING_7BIT_HEX, - //"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" - "[broken! see github.com/wdoekes/asterisk-chan-dongle/issues/13]" + "82E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3", + STR_ENCODING_7BIT_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", @@ -250,10 +248,31 @@ void test_parse_cmgr() "C26150301C0E8741C170381C0605C3E17018", "+33600000000", STR_ENCODING_7BIT, - "050003CA0202C26150301C0E8741C170381C0605C3E17018", - STR_ENCODING_7BIT_HEX, - //"aa Aaaaa Aaaaa Aaaaa" - "[broken! see github.com/wdoekes/asterisk-chan-dongle/issues/13]" + "C26150301C0E8741C170381C0605C3E17018", + STR_ENCODING_7BIT_HEX_PAD_1, + "aa Aaaaa Aaaaa Aaaaa" + } + }, + { "+CMGR: 0,,158\r\n07916407970970F6400A912222222222000041903021825180A0050003000301A9E5391D14060941439015240409414290102404094142901024040941429010240409414290106405594142901564055941429012A40429AD4AABD22A7481AC56101264455A915624C80AB282AC20A1D06A0559415610D20A4282AC2024C80AB282AC202BC80AB282AC2E9012B4042D414A90D2055282942E90D20502819420254809528294", + { + NULL, + "A9E5391D14060941439015240409414290102404094142901024040941429010240409414290106405594142901564055941429012A40429AD4AABD22A7481AC56101264455A915624C80AB282AC20A1D06A0559415610D20A4282AC2024C80AB282AC202BC80AB282AC2E9012B4042D414A90D2055282942E90D20502819420254809528294", + "+2222222222", + STR_ENCODING_7BIT, + "A9E5391D14060941439015240409414290102404094142901024040941429010240409414290106405594142901564055941429012A40429AD4AABD22A7481AC56101264455A915624C80AB282AC20A1D06A0559415610D20A4282AC2024C80AB282AC202BC80AB282AC2E9012B4042D414A90D2055282942E90D20502819420254809528294", + STR_ENCODING_7BIT_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, + "A09976D7E9E5390B640FB3D364103DCD668364B3562CD692C1623417", + "Ufone", /* 55F3DB5D062 */ + STR_ENCODING_7BIT_HEX_PAD_0, + "A09976D7E9E5390B640FB3D364103DCD668364B3562CD692C1623417", + STR_ENCODING_7BIT_HEX_PAD_5, + "Minutes, valid till 23-11-2014." } }, #if 0 @@ -283,7 +302,6 @@ void test_parse_cmgr() result.oa = oa; for (; idx < ITEMS_OF(cases); ++idx) { char buf[4096]; - int res; int failidx = 0; result.str = input = strdup(cases[idx].input); result.msg_utf8 = buf; @@ -294,19 +312,20 @@ void test_parse_cmgr() &result.msg, &result.msg_enc); /* convert to utf8 representation */ + if (!result.res && result.oa_enc != STR_ENCODING_UNKNOWN) { + char tmp_oa[200]; + if (str_recode(RECODE_DECODE, result.oa_enc, + result.oa, strlen(result.oa), + tmp_oa, sizeof(tmp_oa)) >= 0) { + strcpy(result.oa, tmp_oa); + } + } result.msg_utf8 = NULL; - if (!result.res) { - if (result.str != result.msg && - result.msg_enc == STR_ENCODING_7BIT_HEX) { - strcpy(buf, "[broken! see github.com/wdoekes/asterisk-chan-dongle/issues/13]"); + if (!result.res && result.msg_enc != STR_ENCODING_UNKNOWN) { + if (str_recode(RECODE_DECODE, result.msg_enc, + result.str, strlen(result.str), + buf, sizeof(buf)) >= 0) { result.msg_utf8 = buf; - } else if (result.msg_enc != STR_ENCODING_UNKNOWN) { - if (str_recode( - RECODE_DECODE, result.msg_enc, - result.str, strlen(result.str), - buf, sizeof(buf)) >= 0) { - result.msg_utf8 = buf; - } } } @@ -316,8 +335,7 @@ void test_parse_cmgr() ++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 && - ++failidx) + ++failidx && safe_strcmp(result.msg_utf8, cases[idx].result.msg_utf8) == 0) { msg = "OK"; ok++; From 9c1008154a9289a680024ffd5388065c9992b40f Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Sun, 16 Apr 2017 22:03:29 +0200 Subject: [PATCH 038/117] pdu/parsing: Fix unsupported DCS value 0xF0. Closes #28. --- pdu.c | 141 ++++++++++++++++++++++++++------------------------- test/parse.c | 14 ++--- 2 files changed, 78 insertions(+), 77 deletions(-) diff --git a/pdu.c b/pdu.c index f3511bde..9db8a48f 100644 --- a/pdu.c +++ b/pdu.c @@ -258,43 +258,13 @@ #define PDU_PID_EMAIL 0x32 /* bit5 Telematic interworking, bits 4..0 0x 12 = email */ #define PDU_PID_SMS_REPLACE_MASK 0x40 /* bit7 Replace Short Message function activated (TP-PID = 0x41 to 0x47) */ -/* DCS */ -/* bits 1..0 Class */ -#define PDU_DCS_CLASS_SHIFT 0 -#define PDU_DCS_CLASS0 (0x00 << PDU_DCS_CLASS_SHIFT) /* Class 0, provides display and responds SC */ -#define PDU_DCS_CLASS1 (0x01 << PDU_DCS_CLASS_SHIFT) /* Class 1, saves to MS (NV); or saves to the UIM card when MS is full */ -#define PDU_DCS_CLASS2 (0x02 << PDU_DCS_CLASS_SHIFT) /* Class 2, dedicated for the UIM card The storage status is reported to SC after storage If the UIM card is full, failure and reason are reported to SC */ -#define PDU_DCS_CLASS3 (0x03 << PDU_DCS_CLASS_SHIFT) /* Class 3, stored to TE, MS receives messages and does not sent to TE, but responds to SC */ -#define PDU_DCS_CLASS_MASK (0x03 << PDU_DCS_CLASS_SHIFT) -#define PDU_DCS_CLASS(dcs) ((dcs) & PDU_DCS_CLASS_MASK) - -/* bits 3..2 Alpabet */ -#define PDU_DCS_ALPABET_SHIFT 2 -#define PDU_DCS_ALPABET_7BIT (0x00 << PDU_DCS_ALPABET_SHIFT) -#define PDU_DCS_ALPABET_8BIT (0x01 << PDU_DCS_ALPABET_SHIFT) -#define PDU_DCS_ALPABET_UCS2 (0x02 << PDU_DCS_ALPABET_SHIFT) -#define PDU_DCS_ALPABET_MASK (0x03 << PDU_DCS_ALPABET_SHIFT) -#define PDU_DCS_ALPABET(dcs) ((dcs) & PDU_DCS_ALPABET_MASK) - -/* bit 4 */ -#define PDU_DCS_BITS10_CTRL_SHIFT 4 -#define PDU_DCS_BITS10_RETAIN (0x00 << PDU_DCS_BITS10_CTRL_SHIFT) -#define PDU_DCS_BITS10_INUSE (0x01 << PDU_DCS_BITS10_CTRL_SHIFT) -#define PDU_DCS_BITS10_CTRL_MASK (0x01 << PDU_DCS_BITS10_CTRL_SHIFT) -#define PDU_DCS_BITS10_CTRL(dcs) ((dcs) & PDU_DCS_BITS10_CTRL_MASK) - -/* bit 5 */ -#define PDU_DCS_COMPRESSION_SHIFT 5 -#define PDU_DCS_NOT_COMPRESSED (0x00 << PDU_DCS_COMPRESSION_SHIFT) -#define PDU_DCS_COMPRESSED (0x01 << PDU_DCS_COMPRESSION_SHIFT) -#define PDU_DCS_COMPRESSION_MASK (0x01 << PDU_DCS_COMPRESSION_SHIFT) -#define PDU_DCS_COMPRESSION(dcs) ((dcs) & PDU_DCS_COMPRESSION_MASK) - -/* bit 7..6 */ -#define PDU_DCS_76_SHIFT 6 -#define PDU_DCS_76_00 (0x00 << PDU_DCS_76_SHIFT) -#define PDU_DCS_76_MASK (0x03 << PDU_DCS_76_SHIFT) -#define PDU_DCS_76(dcs) ((dcs) & PDU_DCS_76_MASK) +/* bits 3..2 */ +#define PDU_DCS_ALPHABET_SHIFT 2 +#define PDU_DCS_ALPHABET_7BIT (0x00 << PDU_DCS_ALPHABET_SHIFT) +#define PDU_DCS_ALPHABET_8BIT (0x01 << PDU_DCS_ALPHABET_SHIFT) +#define PDU_DCS_ALPHABET_UCS2 (0x02 << PDU_DCS_ALPHABET_SHIFT) +#define PDU_DCS_ALPHABET_MASK (0x03 << PDU_DCS_ALPHABET_SHIFT) +#define PDU_DCS_ALPHABET(dcs) ((dcs) & PDU_DCS_ALPHABET_MASK) #define ROUND_UP2(x) (((x) + 1) & (0xFFFFFFFF << 1)) #define LENGTH2OCTETS(x) (((x) + 1)/2) @@ -560,8 +530,8 @@ static int check_encoding(const char* msg, unsigned length) { str_encoding_t possible_enc = get_encoding(RECODE_ENCODE, msg, length); if(possible_enc == STR_ENCODING_7BIT_HEX_PAD_0) - return PDU_DCS_ALPABET_7BIT; - return PDU_DCS_ALPABET_UCS2; + return PDU_DCS_ALPHABET_7BIT; + return PDU_DCS_ALPHABET_UCS2; } /*! @@ -597,12 +567,13 @@ EXPORT_DEF int pdu_build(char* buffer, size_t length, const char* sca, const cha dcs = check_encoding(msg, msg_len); /* cannot exceed 140 octets for not compressed or cannot exceed 160 septets for compressed */ -/* - if(((PDU_DCS_ALPABET(dcs) == PDU_DCS_ALPABET_UCS2) && msg_len > 70) || (PDU_DCS_ALPABET(dcs) == PDU_DCS_ALPABET_8BIT && msg_len > 140) || msg_len > 160) - { +#if 0 + if (((PDU_DCS_ALPHABET(dcs) == PDU_DCS_ALPHABET_UCS2) && msg_len > 70) || + (PDU_DCS_ALPHABET(dcs) == PDU_DCS_ALPHABET_8BIT && msg_len > 140) || + msg_len > 160) { return -E2BIG; } -*/ +#endif if(sca[0] == '+') sca++; @@ -645,7 +616,10 @@ EXPORT_DEF int pdu_build(char* buffer, size_t length, const char* sca, const cha len += pdu_store_number(buffer + len, dst, dst_len); /* forward TP-User-Data */ - data_len = str_recode(RECODE_ENCODE, dcs == PDU_DCS_ALPABET_UCS2 ? STR_ENCODING_UCS2_HEX : STR_ENCODING_7BIT_HEX_PAD_0, msg, msg_len, buffer + len + 8, length - len - 11); + data_len = str_recode( + RECODE_ENCODE, + (dcs == PDU_DCS_ALPHABET_UCS2 ? STR_ENCODING_UCS2_HEX : STR_ENCODING_7BIT_HEX_PAD_0), + msg, msg_len, buffer + len + 8, length - len - 11); if(data_len < 0) { return -EINVAL; @@ -656,7 +630,7 @@ EXPORT_DEF int pdu_build(char* buffer, size_t length, const char* sca, const cha } /* calc UDL */ - if(dcs == PDU_DCS_ALPABET_UCS2) + if(dcs == PDU_DCS_ALPHABET_UCS2) msg_len = data_len / 2; /* TP-PID. Protocol identifier */ @@ -680,20 +654,20 @@ EXPORT_DEF int pdu_build(char* buffer, size_t length, const char* sca, const cha #/* */ -static str_encoding_t pdu_dcs_alpabet2encoding(int alpabet) +static str_encoding_t pdu_dcs_alphabet2encoding(int alphabet) { str_encoding_t rv = STR_ENCODING_UNKNOWN; - alpabet >>= PDU_DCS_ALPABET_SHIFT; - switch(alpabet) + alphabet >>= PDU_DCS_ALPHABET_SHIFT; + switch(alphabet) { - case (PDU_DCS_ALPABET_7BIT >> PDU_DCS_ALPABET_SHIFT): + case (PDU_DCS_ALPHABET_7BIT >> PDU_DCS_ALPHABET_SHIFT): rv = STR_ENCODING_7BIT_HEX_PAD_0; break; - case (PDU_DCS_ALPABET_8BIT >> PDU_DCS_ALPABET_SHIFT): + case (PDU_DCS_ALPHABET_8BIT >> PDU_DCS_ALPHABET_SHIFT): rv = STR_ENCODING_8BIT_HEX; break; - case (PDU_DCS_ALPABET_UCS2 >> PDU_DCS_ALPABET_SHIFT): + case (PDU_DCS_ALPHABET_UCS2 >> PDU_DCS_ALPHABET_SHIFT): rv = STR_ENCODING_UCS2_HEX; break; } @@ -711,7 +685,7 @@ static str_encoding_t pdu_dcs_alpabet2encoding(int alpabet) 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) { size_t pdu_length = strlen(*pdu); - int field_len, pdu_type, oa_digits, oa_toa, pid, dcs, ts, udl, udhl; + int field_len, pdu_type, oa_digits, oa_toa, pid, dcs, alphabet, ts, udl, udhl; /* set msg as NULL until the end */ *msg = NULL; @@ -796,26 +770,57 @@ EXPORT_DEF const char * pdu_parse(char ** pdu, size_t tpdu_length, char * oa, si if (dcs < 0) { return "Can't parse DSC"; } - - /* TODO: support compression */ - if (!(PDU_DCS_76(dcs) == PDU_DCS_76_00 - && - PDU_DCS_COMPRESSION(dcs) == PDU_DCS_NOT_COMPRESSED - && - ( - PDU_DCS_ALPABET(dcs) == PDU_DCS_ALPABET_7BIT - || - PDU_DCS_ALPABET(dcs) == PDU_DCS_ALPABET_8BIT - || - PDU_DCS_ALPABET(dcs) == PDU_DCS_ALPABET_UCS2 - ))) { + /* 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 + * indicate a message class. The octet is used according to a + * coding group which is indicated in bits 7..4. The octet is + * then coded as follows: */ + { + int dcs_hi = dcs >> 4; + int dcs_lo = dcs & 0xF; + int reserved = 0; + alphabet = -1; /* 7bit, 8bit, ucs2 */ + + switch (dcs_hi) { + case 0x0: /* HIGH 0000: Regular message */ + case 0xF: /* HIGH 1111: Data coding/message class */ + /* apparently bits 0..3 are not reserved anymore: + * bits 3..2: {7bit, 8bit, ucs2, undef} */ + alphabet = PDU_DCS_ALPHABET(dcs); + if (alphabet == PDU_DCS_ALPHABET_MASK) { + reserved = 1; + } + /* if 0xF then (dsc_lo & 3): { + * class0, class1-ME-specific, + * class2-SIM-specific, + * class3-TE-specific (GSM TS 07.05)} */ + break; + case 0xC: /* HIGH 1100: "Discard" MWI */ + case 0xD: /* HIGH 1101: "Store" MWI */ + /* if 0xC then the recipient may discard message + * contents, and only show notification */ + /*inactive_active = (dcs_lo & 8);*/ + reserved = (dcs_lo & 4); /* bit 2 reserved */ + /* (dsc_lo & 3): {VM, Fax, E-mail, Other} */ + break; + default: + reserved = 1; + break; + } + if (reserved) { + *pdu -= 2; + return "Reserved DCS value"; + } + } + if (alphabet == -1) { *pdu -= 2; return "Unsupported DCS value"; } ts = pdu_parse_timestamp(pdu, &pdu_length); - *msg_enc = pdu_dcs_alpabet2encoding(PDU_DCS_ALPABET(dcs)); - if(ts < 0) { + *msg_enc = pdu_dcs_alphabet2encoding(alphabet); + if (ts < 0) { return "Can't parse Timestamp"; } @@ -825,7 +830,7 @@ EXPORT_DEF const char * pdu_parse(char ** pdu, size_t tpdu_length, char * oa, si } /* calculate number of octets in UD */ - if (PDU_DCS_ALPABET(dcs) == PDU_DCS_ALPABET_7BIT) { + if (alphabet == PDU_DCS_ALPHABET_7BIT) { udl = ((udl + 1) * 7) >> 3; } if ((size_t)udl * 2 != pdu_length) { diff --git a/test/parse.c b/test/parse.c index 36bf601d..18fab59e 100644 --- a/test/parse.c +++ b/test/parse.c @@ -275,22 +275,18 @@ void test_parse_cmgr() "Minutes, valid till 23-11-2014." } }, -#if 0 { "+CMGR: 0,,137\r\n07919333851805320409D034186C360300F0713032810105408849A7F1099A36A720D9EC059BB140319C2E06D38186EF39FD0D1AA3D3E176981E06155D20184B467381926CD0585E26A7E96F1001547481683816ACE60241CB7250DA6D7E83E67550D95E76D3EB61761AF486EBD36F771A14A6D3D3F632A80C12BFDDF539485E9EA7C9F534688C4E87DB61100D968BD95C", - /* 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. */ { NULL, "49A7F1099A36A720D9EC059BB140319C2E06D38186EF39FD0D1AA3D3E176981E06155D20184B467381926CD0585E26A7E96F1001547481683816ACE60241CB7250DA6D7E83E67550D95E76D3EB61761AF486EBD36F771A14A6D3D3F632A80C12BFDDF539485E9EA7C9F534688C4E87DB61100D968BD95C", - "09D034186C3603", /* from 40033 .. ? */ - STR_ENCODING_7BIT, /* ? */ + "40033", /* 09D034186C3603 */ + STR_ENCODING_7BIT_HEX_PAD_0, "49A7F1099A36A720D9EC059BB140319C2E06D38186EF39FD0D1AA3D3E176981E06155D20184B467381926CD0585E26A7E96F1001547481683816ACE60241CB7250DA6D7E83E67550D95E76D3EB61761AF486EBD36F771A14A6D3D3F632A80C12BFDDF539485E9EA7C9F534688C4E87DB61100D968BD95C", - STR_ENCODING_UCS2_HEX /* ? */ + STR_ENCODING_7BIT_HEX_PAD_0, + /* FIXME: The \x04 here should be 'é'. */ + "INFO SMS 23/03, 18:10: Costo chiamata E. 0,24. Il credito \x04 E. 48,05. Per info su eventuali opzioni attive e bonus residui chiama 40916." } }, -#endif }; unsigned idx = 0; From 212ea86afe0511428dadee140951a48b73f47448 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Sun, 16 Apr 2017 22:08:17 +0200 Subject: [PATCH 039/117] docs: Add note about avoiding "SMS-injection". Closes #24. Thanks Lynxie (@lnx13) for the report. --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index b02cb8df..fd56834b 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,10 @@ optionally pass `--with-asterisk=PATH/TO/INCLUDE`. Here is an example for the dialplan: ------------------------------------ +**WARNING**: *This example uses the raw SMS message passed to System() directly. +No sane person would do that with untrusted data without escaping/removing the +single quotes.* + [dongle-incoming] exten => sms,1,Verbose(Incoming SMS from ${CALLERID(num)} ${BASE64_DECODE(${SMS_BASE64})}) exten => sms,n,System(echo '${STRFTIME(${EPOCH},,%Y-%m-%d %H:%M:%S)} - ${DONGLENAME} - ${CALLERID(num)}: ${BASE64_DECODE(${SMS_BASE64})}' >> /var/log/asterisk/sms.txt) From e324cfed12873211cf0cf2478db4162b0ccad05a Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Sun, 16 Apr 2017 22:19:03 +0200 Subject: [PATCH 040/117] logging: Add missing LFs to WARNINGs/ERRORs. --- channel.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/channel.c b/channel.c index d1c32a2d..dfeb1860 100644 --- a/channel.c +++ b/channel.c @@ -1491,7 +1491,8 @@ static int channel_func_write(struct ast_channel* channel, const char* function, } else { - ast_log(LOG_WARNING, "Invalid value for %s(callstate).", function); + ast_log(LOG_WARNING, "Invalid value for %s(callstate).\n", + function); return -1; } @@ -1508,12 +1509,16 @@ static int channel_func_write(struct ast_channel* channel, const char* function, if(at_enque_activate(cpvt)) { /* TODO: handle error */ - ast_log(LOG_ERROR, "Error state to active for call idx %d in %s(callstate).", cpvt->call_idx, function); + ast_log(LOG_ERROR, + "Error state to active for call idx %d in %s(callstate).\n", + cpvt->call_idx, function); } } else { - ast_log(LOG_WARNING, "allow change state to 'active' only from 'held' in %s(callstate).", function); + ast_log(LOG_WARNING, + "allow change state to 'active' only from 'held' in %s(callstate).\n", + function); ret = -1; } ast_mutex_unlock(&cpvt->pvt->lock); @@ -1538,7 +1543,7 @@ static int channel_func_write(struct ast_channel* channel, const char* function, } else { - ast_log(LOG_WARNING, "Invalid value for %s(dtmf).", function); + ast_log(LOG_WARNING, "Invalid value for %s(dtmf).\n", function); return -1; } } From 704deed4b54db6272b368270491277990597f0a8 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Sun, 16 Apr 2017 22:33:35 +0200 Subject: [PATCH 041/117] travis: Make check after the build. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 7072da2b..43d06ad6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,3 +31,4 @@ before_script: script: - make clean all + - make check From b0a2dec32075b0922e909c6b5a62077cc18b68ef Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Sun, 16 Apr 2017 23:10:11 +0200 Subject: [PATCH 042/117] decoding: Decode 7bit alphabet to unicode. A better fix than the previous @ replacement in 897b27c27. --- char_conv.c | 37 ++++++++++++++++++++++++++++++++----- test/parse.c | 3 +-- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/char_conv.c b/char_conv.c index ab9c8e52..e77639f5 100644 --- a/char_conv.c +++ b/char_conv.c @@ -246,6 +246,23 @@ static ssize_t char_to_hexstr_7bit_pad_6(const char* in, size_t in_length, char* return char_to_hexstr_7bit_padded(in, in_length, out, out_size, 6); } +/* GSM 03.38 7bit alphabet */ +static const char *const alphabet_7bit[128] = { + "@", "£", "$", "¥", "è", "é", "ù", "ì", "ò", "Ç", "\n", "Ø", + "ø", "\r", "Å", "å", "∆", "_", "Φ", "Γ", "Λ", "Ω", "Π", "Ψ", + "Σ", "Θ", "Ξ", "\x1b" /* ESC */, "Æ", "æ", "ß", "É", " ", "!", + "\"", "#", "¤", "%", "&", "'", "(", ")", "*", "+", ",", "-", + ".", "/", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", + ":", ";", "<", "=", ">", "?", "¡", "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", "Ä", "Ö", "Ñ", + "Ü", "§", "¿", "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", "ä", "ö", "ñ", "ü", "à" + /* TODO: ESC could unlock the basic charset extension, + * interpeting the following char differently. */ +}; + static ssize_t hexstr_7bit_to_char_padded(const char* in, size_t in_length, char* out, size_t out_size, unsigned in_padding) { @@ -258,18 +275,21 @@ static ssize_t hexstr_7bit_to_char_padded(const char* in, size_t in_length, char /* compute number of bytes */ in_length /= 2; + if (out_size == 0) { + return -1; + } + /* check if string is empty */ if (in_length == 0) { out[0] = '\0'; return (0); } +#if 0 /* compute number of characters */ x = (((in_length * 8) - in_padding) / 7) + 1 /* terminating zero */; - - /* check if final string fits within buffer */ - if (x > out_size) - return -1; +#endif + out_size -= 1; /* reserve room for terminating zero */ /* account for the bit padding */ in_padding = 7 - in_padding; @@ -279,6 +299,8 @@ static ssize_t hexstr_7bit_to_char_padded(const char* in, size_t in_length, char /* parse the hexstring */ for (x = i = 0; i != in_length; i++) { + if (x >= out_size) + return -1; memcpy (buf, in + i * 2, 2); if (sscanf (buf, "%x", &hexval) != 1) return -1; @@ -288,7 +310,12 @@ static ssize_t hexstr_7bit_to_char_padded(const char* in, size_t in_length, char while (in_padding >= (2 * 7)) { in_padding -= 7; value >>= 7; - out[x++] = value & 0x7F; + { + const char *val = alphabet_7bit[value & 0x7F]; + do { + out[x++] = *val++; + } while (*val && x < out_size); + } } } diff --git a/test/parse.c b/test/parse.c index 18fab59e..42fbf2b1 100644 --- a/test/parse.c +++ b/test/parse.c @@ -283,8 +283,7 @@ void test_parse_cmgr() STR_ENCODING_7BIT_HEX_PAD_0, "49A7F1099A36A720D9EC059BB140319C2E06D38186EF39FD0D1AA3D3E176981E06155D20184B467381926CD0585E26A7E96F1001547481683816ACE60241CB7250DA6D7E83E67550D95E76D3EB61761AF486EBD36F771A14A6D3D3F632A80C12BFDDF539485E9EA7C9F534688C4E87DB61100D968BD95C", STR_ENCODING_7BIT_HEX_PAD_0, - /* FIXME: The \x04 here should be 'é'. */ - "INFO SMS 23/03, 18:10: Costo chiamata E. 0,24. Il credito \x04 E. 48,05. Per info su eventuali opzioni attive e bonus residui chiama 40916." + "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." } }, }; From 7c8e6b8210aeeb4d2c622e27650b390d0128dd25 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Mon, 17 Apr 2017 17:06:36 +0200 Subject: [PATCH 043/117] cleanup: Fix a few typo's. --- at_queue.h | 2 +- at_read.c | 2 +- chan_dongle.c | 7 +++++-- chan_dongle.h | 12 ++++++------ channel.c | 2 +- cli.c | 2 +- cpvt.h | 4 ++-- ringbuffer.h | 2 +- tools/discovery.c | 10 +++++----- 9 files changed, 23 insertions(+), 20 deletions(-) diff --git a/at_queue.h b/at_queue.h index 2090529d..f57e5aa9 100644 --- a/at_queue.h +++ b/at_queue.h @@ -17,7 +17,7 @@ typedef struct at_queue_cmd { at_cmd_t cmd; /*!< command code */ - at_res_t res; /*!< expected responce code, can be RES_OK, RES_CMGR, RES_SMS_PROMPT */ + at_res_t res; /*!< expected response code, can be RES_OK, RES_CMGR, RES_SMS_PROMPT */ unsigned flags; /*!< flags */ #define ATQ_CMD_FLAG_DEFAULT 0x00 /*!< empty flags */ diff --git a/at_read.c b/at_read.c index 2109a50a..29cb8902 100644 --- a/at_read.c +++ b/at_read.c @@ -48,7 +48,7 @@ EXPORT_DEF int at_wait (int fd, int* ms) return outfd; } -#/* return number of bytes readed */ +#/* return number of bytes read */ EXPORT_DEF ssize_t at_read (int fd, const char * dev, struct ringbuffer* rb) { struct iovec iov[2]; diff --git a/chan_dongle.c b/chan_dongle.c index 25fdf87c..0b3b904e 100644 --- a/chan_dongle.c +++ b/chan_dongle.c @@ -360,7 +360,7 @@ EXPORT_DEF void clean_read_data(const char * devname, int fd) { iovcnt = at_read (fd, devname, &rb); ast_debug (4, "[%s] drop %u bytes of pending data before initialization\n", devname, (unsigned)rb_used(&rb)); - /* drop readed */ + /* drop read */ rb_init (&rb, buf, sizeof (buf)); if (iovcnt == 0) break; @@ -452,13 +452,16 @@ static void* do_monitor_phone (void* data) break; } + ast_mutex_lock (&pvt->lock); PVT_STAT(pvt, d_read_bytes) += iovcnt; + ast_mutex_unlock (&pvt->lock); + while ((iovcnt = at_read_result_iov (dev, &read_result, &rb, iov)) > 0) { at_res = at_read_result_classification (&rb, iov[0].iov_len + iov[1].iov_len); ast_mutex_lock (&pvt->lock); - PVT_STAT(pvt, at_responces) ++; + PVT_STAT(pvt, at_responses) ++; if (at_response (pvt, iov, iovcnt, at_res) || at_queue_run(pvt)) { goto e_cleanup; diff --git a/chan_dongle.h b/chan_dongle.h index 44a96282..bdac73ec 100644 --- a/chan_dongle.h +++ b/chan_dongle.h @@ -69,16 +69,16 @@ typedef struct pvt_stat { uint32_t at_tasks; /*!< number of tasks added to queue */ uint32_t at_cmds; /*!< number of commands added to queue */ - uint32_t at_responces; /*!< number of responses handled */ + uint32_t at_responses; /*!< number of responses handled */ - uint32_t d_read_bytes; /*!< number of bytes of commands actually readed from device */ + uint32_t d_read_bytes; /*!< number of bytes of commands actually read from device */ uint32_t d_write_bytes; /*!< number of bytes of commands actually written to device */ - uint64_t a_read_bytes; /*!< number of bytes of audio readed from device */ + uint64_t a_read_bytes; /*!< number of bytes of audio read from device */ uint64_t a_write_bytes; /*!< number of bytes of audio written to device */ - uint32_t read_frames; /*!< number of frames readed from device */ - uint32_t read_sframes; /*!< number of truncated frames readed from device */ + uint32_t read_frames; /*!< number of frames read from device */ + uint32_t read_sframes; /*!< number of truncated frames read from device */ uint32_t write_frames; /*!< number of tries to frame write */ uint32_t write_tframes; /*!< number of truncated frames to write */ @@ -129,7 +129,7 @@ typedef struct pvt // struct ringbuffer a_write_rb; /*!< audio ring buffer */ // char a_read_buf[FRAME_SIZE + AST_FRIENDLY_OFFSET]; /*!< audio read buffer */ -// struct ast_frame a_read_frame; /*!< readed frame buffer */ +// struct ast_frame a_read_frame; /*!< read frame buffer */ char dtmf_digit; /*!< last DTMF digit */ diff --git a/channel.c b/channel.c index dfeb1860..f4e07645 100644 --- a/channel.c +++ b/channel.c @@ -698,7 +698,7 @@ static struct ast_frame* channel_read (struct ast_channel* channel) } /* ast_debug (7, "[%s] call idx %d read %u\n", PVT_ID(pvt), cpvt->call_idx, (unsigned)res); - ast_debug (6, "[%s] read | call idx %d fd %d readed %d bytes\n", PVT_ID(pvt), cpvt->call_idx, pvt->audio_fd, res); + ast_debug (6, "[%s] read | call idx %d fd %d read %d bytes\n", PVT_ID(pvt), cpvt->call_idx, pvt->audio_fd, res); */ if(CPVT_IS_MASTER(cpvt)) diff --git a/cli.c b/cli.c index 24859ed9..4f95c0bd 100644 --- a/cli.c +++ b/cli.c @@ -306,7 +306,7 @@ static char* cli_show_device_statistics (struct ast_cli_entry* e, int cmd, struc ast_cli (a->fd, " Device : %s\n", PVT_ID(pvt)); ast_cli (a->fd, " Queue tasks : %u\n", PVT_STAT(pvt, at_tasks)); ast_cli (a->fd, " Queue commands : %u\n", PVT_STAT(pvt, at_cmds)); - ast_cli (a->fd, " Responses : %u\n", PVT_STAT(pvt, at_responces)); + ast_cli (a->fd, " Responses : %u\n", PVT_STAT(pvt, at_responses)); ast_cli (a->fd, " Bytes of read responses : %u\n", PVT_STAT(pvt, d_read_bytes)); ast_cli (a->fd, " Bytes of written commands : %u\n", PVT_STAT(pvt, d_write_bytes)); ast_cli (a->fd, " Bytes of read audio : %llu\n", (unsigned long long int)PVT_STAT(pvt, a_read_bytes)); diff --git a/cpvt.h b/cpvt.h index cfdae646..1ceefbb4 100644 --- a/cpvt.h +++ b/cpvt.h @@ -65,13 +65,13 @@ typedef struct cpvt { #define CALL_DIR_OUTGOING 0 #define CALL_DIR_INCOMING 1 - int rd_pipe[2]; /*!< pipe for split readed from device */ + int rd_pipe[2]; /*!< pipe for split read from device */ #define PIPE_READ 0 #define PIPE_WRITE 1 struct mixstream mixstream; /*!< mix stream */ char a_read_buf[FRAME_SIZE + AST_FRIENDLY_OFFSET];/*!< audio read buffer */ - struct ast_frame a_read_frame; /*!< readed frame buffer */ + struct ast_frame a_read_frame; /*!< read frame buffer */ // size_t write; /*!< write position in pvt->a_write_buf */ // size_t used; /*!< bytes used in pvt->a_write_buf */ diff --git a/ringbuffer.h b/ringbuffer.h index 8e51c3bd..98170fc2 100644 --- a/ringbuffer.h +++ b/ringbuffer.h @@ -46,7 +46,7 @@ INLINE_DECL size_t rb_free (const struct ringbuffer* rb) EXPORT_DECL int rb_memcmp (const struct ringbuffer*, const char*, size_t); -/*!< fill io vectors array with readed data (situable for writev()) and return number of io vectors updated */ +/*!< fill io vectors array with read data (situable for writev()) and return number of io vectors updated */ EXPORT_DECL int rb_read_all_iov (const struct ringbuffer* rb, struct iovec iov[2]); /*!< fill io vectors array and return number of io vectors updated for reading len bytes */ diff --git a/tools/discovery.c b/tools/discovery.c index ba950a16..b2d3294c 100644 --- a/tools/discovery.c +++ b/tools/discovery.c @@ -32,14 +32,14 @@ char * read_result(int fd) unsigned total = 0; char buf[4096]; char * found; - int readed; + int read_bytes; while(1) { - readed = read(fd, buf + total, sizeof(buf) - total); - if(readed <= 0) + read_bytes = read(fd, buf + total, sizeof(buf) - total); + if(read_bytes <= 0) return NULL; - total += readed; -// fprintf(stdout, "%*s", readed, buf); + total += read_bytes; +// fprintf(stdout, "%*s", read_bytes, buf); if((found = memmem(buf, total, "\r\nOK\r\n", 6)) != NULL) { found[0] = 0; return strdup(buf); From 14a966d610688ddfe01883741c7c9d0303dc453c Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Mon, 17 Apr 2017 17:34:28 +0200 Subject: [PATCH 044/117] cleanup: Fix some more typo's. The locks all seem okay. --- chan_dongle.c | 13 +++++++------ channel.c | 3 ++- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/chan_dongle.c b/chan_dongle.c index 0b3b904e..eee6270d 100644 --- a/chan_dongle.c +++ b/chan_dongle.c @@ -901,6 +901,7 @@ EXPORT_DEF struct pvt * find_device_ex(struct public_state * state, const char * break; } ast_mutex_unlock (&pvt->lock); + pvt = NULL; } AST_RWLIST_UNLOCK(&state->devices); @@ -976,7 +977,7 @@ EXPORT_DEF struct pvt * find_device_by_resource_ex(struct public_state * state, { // ast_mutex_lock(&state->round_robin_mtx); - /* Generate a list of all availible devices */ + /* Generate a list of all available devices */ j = ITEMS_OF (round_robin); c = 0; last_used = 0; AST_RWLIST_TRAVERSE(&state->devices, pvt, entry) @@ -1002,7 +1003,7 @@ EXPORT_DEF struct pvt * find_device_by_resource_ex(struct public_state * state, ast_mutex_unlock (&pvt->lock); } - /* Search for a availible device starting at the last used device */ + /* Search for a available device starting at the last used device */ for (i = 0, j = last_used + 1; i < c; i++, j++) { if (j == c) @@ -1030,7 +1031,7 @@ EXPORT_DEF struct pvt * find_device_by_resource_ex(struct public_state * state, { // ast_mutex_lock(&state->round_robin_mtx); - /* Generate a list of all availible devices */ + /* Generate a list of all available devices */ j = ITEMS_OF(round_robin); c = 0; last_used = 0; AST_RWLIST_TRAVERSE(&state->devices, pvt, entry) @@ -1056,7 +1057,7 @@ EXPORT_DEF struct pvt * find_device_by_resource_ex(struct public_state * state, ast_mutex_unlock (&pvt->lock); } - /* Search for a availible device starting at the last used device */ + /* Search for a available device starting at the last used device */ for (i = 0, j = last_used + 1; i < c; i++, j++) { if (j == c) @@ -1083,7 +1084,7 @@ EXPORT_DEF struct pvt * find_device_by_resource_ex(struct public_state * state, { // ast_mutex_lock(&state->round_robin_mtx); - /* Generate a list of all availible devices */ + /* Generate a list of all available devices */ j = ITEMS_OF(round_robin); c = 0; last_used = 0; i = strlen (&resource[2]); @@ -1111,7 +1112,7 @@ EXPORT_DEF struct pvt * find_device_by_resource_ex(struct public_state * state, ast_mutex_unlock (&pvt->lock); } - /* Search for a availible device starting at the last used device */ + /* Search for a available device starting at the last used device */ for (i = 0, j = last_used + 1; i < c; i++, j++) { if (j == c) diff --git a/channel.c b/channel.c index f4e07645..b849e6f1 100644 --- a/channel.c +++ b/channel.c @@ -1454,11 +1454,12 @@ static int channel_func_read(struct ast_channel* channel, attribute_unused const */ else if (!strcasecmp(data, "dtmf")) { + const char* dtmf; while (ast_mutex_trylock (&pvt->lock)) { CHANNEL_DEADLOCK_AVOIDANCE (channel); } - const char * dtmf = dc_dtmf_setting2str(pvt->real_dtmf); + dtmf = dc_dtmf_setting2str(pvt->real_dtmf); ast_mutex_unlock(&pvt->lock); ast_copy_string(buf, dtmf, len); From 45d8fbc6996429fc8d10183ab560fdb379310164 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Tue, 18 Apr 2017 17:53:46 +0200 Subject: [PATCH 045/117] cleanup/fix: Undo pvt=NULL "fix" which was bad. Fixes #30 I think. --- chan_dongle.c | 1 - 1 file changed, 1 deletion(-) diff --git a/chan_dongle.c b/chan_dongle.c index eee6270d..29a13cdd 100644 --- a/chan_dongle.c +++ b/chan_dongle.c @@ -901,7 +901,6 @@ EXPORT_DEF struct pvt * find_device_ex(struct public_state * state, const char * break; } ast_mutex_unlock (&pvt->lock); - pvt = NULL; } AST_RWLIST_UNLOCK(&state->devices); From d6c29828df5cc2d55e5ef8455e04172dd01d2a1b Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Fri, 28 Apr 2017 10:02:29 +0200 Subject: [PATCH 046/117] cleanup: Remove leftover round_robin_mtx comments. It appears there previously was a global round_robin struct that had to be mutex protected, but it has been refactored to use locally scoped vars instead. --- chan_dongle.c | 15 --------------- chan_dongle.h | 2 -- 2 files changed, 17 deletions(-) diff --git a/chan_dongle.c b/chan_dongle.c index 29a13cdd..e64f759f 100644 --- a/chan_dongle.c +++ b/chan_dongle.c @@ -974,8 +974,6 @@ EXPORT_DEF struct pvt * find_device_by_resource_ex(struct public_state * state, group = (int) strtol (&resource[1], (char**) NULL, 10); if (errno != EINVAL) { -// ast_mutex_lock(&state->round_robin_mtx); - /* Generate a list of all available devices */ j = ITEMS_OF (round_robin); c = 0; last_used = 0; @@ -1022,14 +1020,10 @@ EXPORT_DEF struct pvt * find_device_by_resource_ex(struct public_state * state, } ast_mutex_unlock (&pvt->lock); } - -// ast_mutex_unlock(&state->round_robin_mtx); } } else if (((resource[0] == 'p') || (resource[0] == 'P')) && resource[1] == ':') { -// ast_mutex_lock(&state->round_robin_mtx); - /* Generate a list of all available devices */ j = ITEMS_OF(round_robin); c = 0; last_used = 0; @@ -1076,13 +1070,9 @@ EXPORT_DEF struct pvt * find_device_by_resource_ex(struct public_state * state, } ast_mutex_unlock (&pvt->lock); } - -// ast_mutex_unlock(&state->round_robin_mtx); } else if (((resource[0] == 's') || (resource[0] == 'S')) && resource[1] == ':') { -// ast_mutex_lock(&state->round_robin_mtx); - /* Generate a list of all available devices */ j = ITEMS_OF(round_robin); c = 0; last_used = 0; @@ -1131,8 +1121,6 @@ EXPORT_DEF struct pvt * find_device_by_resource_ex(struct public_state * state, } ast_mutex_unlock (&pvt->lock); } - -// ast_mutex_unlock(&state->round_robin_mtx); } else if (((resource[0] == 'i') || (resource[0] == 'I')) && resource[1] == ':') { @@ -1650,7 +1638,6 @@ static int public_state_init(struct public_state * state) ast_mutex_init(&state->discovery_lock); state->discovery_thread = AST_PTHREADT_NULL; -// ast_mutex_init(&state->round_robin_mtx); if(reload_config(state, 0, RESTATE_TIME_NOW, NULL) == 0) { @@ -1712,7 +1699,6 @@ static int public_state_init(struct public_state * state) ast_log (LOG_ERROR, "Errors reading config file " CONFIG_FILE ", Not loading module\n"); } -// ast_mutex_destroy(&state->round_robin_mtx); ast_mutex_destroy(&state->discovery_lock); AST_RWLIST_HEAD_DESTROY(&state->devices); @@ -1742,7 +1728,6 @@ static void public_state_fini(struct public_state * state) discovery_stop(state); devices_destroy(state); -// ast_mutex_destroy(&state->round_robin_mtx); ast_mutex_destroy(&state->discovery_lock); AST_RWLIST_HEAD_DESTROY(&state->devices); } diff --git a/chan_dongle.h b/chan_dongle.h index bdac73ec..2c71663d 100644 --- a/chan_dongle.h +++ b/chan_dongle.h @@ -217,8 +217,6 @@ typedef struct public_state ast_mutex_t discovery_lock; pthread_t discovery_thread; /* The discovery thread handler */ volatile int unloading_flag; /* no need mutex or other locking for protect this variable because no concurent r/w and set non-0 atomically */ -// ast_mutex_t round_robin_mtx; -// struct pvt * round_robin[MAXDONGLEDEVICES]; // TODO: remove and make local variable of find_device_by_resource_ex() struct dc_gconfig global_settings; } public_state_t; From 78a2e97b7106f27fbbfb93832094c9b5c6c16967 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Fri, 28 Apr 2017 10:11:25 +0200 Subject: [PATCH 047/117] cleanup: Unindent/flatten pvt_start() in chan_dongle.c. --- chan_dongle.c | 62 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 26 deletions(-) diff --git a/chan_dongle.c b/chan_dongle.c index e64f759f..0f3fe175 100644 --- a/chan_dongle.c +++ b/chan_dongle.c @@ -585,35 +585,45 @@ static int pvt_discovery(struct pvt * pvt) static void pvt_start(struct pvt * pvt) { /* prevent start_monitor() multiple times and on turned off devices */ - if (!pvt->connected && pvt->desired_state == DEV_STATE_STARTED) -// && (pvt->monitor_thread == AST_PTHREADT_NULL || (pthread_kill(pvt->monitor_thread, 0) != 0 && errno == ESRCH))) - { - pvt_stop(pvt); + if (pvt->connected || pvt->desired_state != DEV_STATE_STARTED) { + // || (pvt->monitor_thread != AST_PTHREADT_NULL && + // (pthread_kill(pvt->monitor_thread, 0) == 0 || errno != ESRCH)) + return; + } - if(pvt_discovery(pvt)) - return; - ast_verb (3, "[%s] Trying to connect on %s...\n", PVT_ID(pvt), PVT_STATE(pvt, data_tty)); + pvt_stop(pvt); - pvt->data_fd = opentty(PVT_STATE(pvt, data_tty), &pvt->dlock); - if (pvt->data_fd >= 0) - { - // TODO: delay until device activate voice call or at pvt_on_create_1st_channel() - pvt->audio_fd = opentty(PVT_STATE(pvt, audio_tty), &pvt->alock); - if (pvt->audio_fd >= 0) - { - if (start_monitor (pvt)) - { - pvt->connected = 1; - pvt->current_state = DEV_STATE_STARTED; - manager_event_device_status(PVT_ID(pvt), "Connect"); - ast_verb (3, "[%s] Dongle has connected, initializing...\n", PVT_ID(pvt)); - return; - } - closetty(pvt->audio_fd, &pvt->alock); - } - closetty(pvt->data_fd, &pvt->dlock); - } + if (pvt_discovery(pvt)) { + return; } + + ast_verb(3, "[%s] Trying to connect on %s...\n", PVT_ID(pvt), PVT_STATE(pvt, data_tty)); + + pvt->data_fd = opentty(PVT_STATE(pvt, data_tty), &pvt->dlock); + if (pvt->data_fd < 0) { + return; + } + + // TODO: delay until device activate voice call or at pvt_on_create_1st_channel() + pvt->audio_fd = opentty(PVT_STATE(pvt, audio_tty), &pvt->alock); + if (pvt->audio_fd < 0) { + goto cleanup_datafd; + } + + if (!start_monitor(pvt)) { + goto cleanup_audiofd; + } + + pvt->connected = 1; + pvt->current_state = DEV_STATE_STARTED; + manager_event_device_status(PVT_ID(pvt), "Connect"); + ast_verb(3, "[%s] Dongle has connected, initializing...\n", PVT_ID(pvt)); + return; + +cleanup_audiofd: + closetty(pvt->audio_fd, &pvt->alock); +cleanup_datafd: + closetty(pvt->data_fd, &pvt->dlock); } #/* */ From c929b3574702ccc59652d1bfc915b780deeeb3a6 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Fri, 28 Apr 2017 10:14:14 +0200 Subject: [PATCH 048/117] fix: Fix incidental deadlock with Jitterbuffer or Asterisk 12+. Closes #19, reported by @vitasgul, @multijohn and Garrone Joseph (@garronej). Cause found through diligent bug reporting and testing by Garrone. --- chan_dongle.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/chan_dongle.c b/chan_dongle.c index 0f3fe175..78013ff5 100644 --- a/chan_dongle.c +++ b/chan_dongle.c @@ -584,6 +584,8 @@ static int pvt_discovery(struct pvt * pvt) #/* */ static void pvt_start(struct pvt * pvt) { + long flags; + /* prevent start_monitor() multiple times and on turned off devices */ if (pvt->connected || pvt->desired_state != DEV_STATE_STARTED) { // || (pvt->monitor_thread != AST_PTHREADT_NULL && @@ -614,6 +616,16 @@ static void pvt_start(struct pvt * pvt) goto cleanup_audiofd; } + /* Set data_fd and audio_fd to non-blocking. This appears to fix + * incidental deadlocks occurring with Asterisk 12+ or with + * jitterbuffer enabled. Apparently Asterisk can call the + * (audio) read function for sockets that don't have data to + * read(). */ + flags = fcntl(pvt->data_fd, F_GETFL); + fcntl(pvt->data_fd, F_SETFL, flags | O_NONBLOCK); + flags = fcntl(pvt->audio_fd, F_GETFL); + fcntl(pvt->audio_fd, F_SETFL, flags | O_NONBLOCK); + pvt->connected = 1; pvt->current_state = DEV_STATE_STARTED; manager_event_device_status(PVT_ID(pvt), "Connect"); From b6c7fdfa0fa66d907df8b01eb1f06a8dc3abb869 Mon Sep 17 00:00:00 2001 From: garronej Date: Thu, 1 Jun 2017 15:25:52 +0000 Subject: [PATCH 049/117] adding instructions for applying Jitter buffer and automatic gain control --- README.md | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index fd56834b..0e058549 100644 --- a/README.md +++ b/README.md @@ -153,6 +153,71 @@ Other CLI commands: For reading installation notes please look to INSTALL file. + +Gain control and Jitter buffer +-------------------------------- + + + + + +In order to perform good quality calls you will need to take care of: + +* **Automatic gain control**: + +chan_dongle does not control the gain of the audio stream it receive. +This result of Alice hearing Bob's voice loud and noisy. +It is possible to manually manage the gain in *dongle.conf* but +the better option is by far to apply automatic gain control with +the dialplan function AGC. + + +* **Jitter buffer**: + +Since asterisk 12 it is no longer possible to enable Jitter buffer +in dongle.conf it has to be applied in the dialplan. +The lack of Jitter buffer result in severe loss in the transport +of the voice from Bob to Alice. + + +#### Dialplan example + +To set JITTERBUFFER and AGC in the dialplan on the appropriate channel +regardless of who is initiating the call we will have to use +the "b" option of Dial: + +b( context^exten^priority ) + +Before initiating an outgoing call, Gosub to the specified +location using the newly created channel. + +The Gosub will be executed for each destination channel." + + [from-dongle] + ; This will be executed by an indbound Dongle channel ( call initiated on the dongle side ) + exten => _[+0-9].,1,Dial(SIP/bob,b(from-dongle^outbound^1)) ; + + ; This will be executed by an outbound SIP channel ( channel generated by dial ) + exten => outbound,1,Set(JITTERBUFFER(adaptive)=default) + same => n,Set(AGC(rx)=4000) + same => n,Return() + + [from-sip] + ; This will be executed by an inbound SIP channel ( call initiated on the SIP side ) + exten => _[+0-9].,1,Set(JITTERBUFFER(adaptive)=default) + same => n,Set(AGC(rx)=4000) + same => n,Dial(Dongle/i:${IMEI_OF_MY_DONGLE}/${NUMBER_OF_BOB}) + + +Note: To use automatic gain control dialplan function (AGC) you will need +to compile Asterisk with func_speex ( see in menuselect ). +On raspberry Pi you will need to compile and install speex and speexdsp yourself, +the version of speex provided by the depos does not support AGC. +(because compiled with fixed point instead of floating point) +see: [HOWTO](https://gist.github.com/garronej/01f0dac45efe9161969a83890c019efa) + + For additional information about Huawei dongle usage look to chan\_dongle Wiki at http://wiki.e1550.mobi and chan\_dongle project home at -https://github.com/wdoekes/asterisk-chan-dongle/ +https://github.com/wdoekes/asterisk-chan-dongle/ \ No newline at end of file From c57ef0d4a9e9ba500b2e5c8b4c4db24ebcf1e082 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Mon, 24 Jul 2017 16:00:22 +0200 Subject: [PATCH 050/117] Ignore """+CMTI: "SM",-1""" with negative index. Instead of disconnecting a standing call, we ignore the message. Closes #35. Requested by @vitasgul. --- at_response.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/at_response.c b/at_response.c index 4427ed1f..90c4d38f 100644 --- a/at_response.c +++ b/at_response.c @@ -1170,8 +1170,10 @@ static int at_response_cmti (struct pvt* pvt, const char* str) } else { - ast_log (LOG_ERROR, "[%s] Error parsing incoming sms message alert '%s', disconnecting\n", PVT_ID(pvt), str); - return -1; + /* 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; From 1e452f7598266be0970cec582ba32dbc5568868d Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Mon, 24 Jul 2017 16:10:05 +0200 Subject: [PATCH 051/117] Attempt to open the dongle in exclusive mode using TIOCEXCL. Closes #36. Requested by @Andrik45719. --- chan_dongle.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/chan_dongle.c b/chan_dongle.c index 78013ff5..760b178f 100644 --- a/chan_dongle.c +++ b/chan_dongle.c @@ -212,7 +212,7 @@ EXPORT_DEF int opentty (const char* dev, char ** lockfile) return -1; } - fd = open (dev, O_RDWR | O_NOCTTY); + fd = open(dev, O_RDWR | O_NOCTTY); if (fd < 0) { flags = errno; @@ -222,9 +222,14 @@ EXPORT_DEF int opentty (const char* dev, char ** lockfile) ast_log (LOG_WARNING, "unable to open %s: %s\n", dev, strerror(flags)); return -1; } + /* Put the terminal into exclusive mode. All other open(2)s by + * non-root will fail with EBUSY. */ + if (ioctl(fd, TIOCEXCL) != 0) { + ast_log(LOG_WARNING, "ioctl(TIOCEXCL) failed for %s: %s\n", dev, strerror(errno)); + } flags = fcntl(fd, F_GETFD); - if(flags == -1 || fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) + if (flags == -1 || fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) { flags = errno; closetty(fd, lockfile); From 53e83a4f3cafed49f7562cb341cc8f117bbb92b0 Mon Sep 17 00:00:00 2001 From: micmac1 Date: Sun, 3 Sep 2017 11:54:45 +0200 Subject: [PATCH 052/117] Fix audio endianess problem on big endian systems. This fixes broken audio from asterisk toward the GSM network. Issue was originally reported here: https://github.com/openwrt/telephony/issues/7. Github user @ljakob provided this patch and reports success: (Sep 9, 2015) > Hi, I've used the patch over the last month on my box and it works > perfectly. Audio gain from my old x86 installation had to be copied but > that was expected. There have been at least 100 calls without any > problems. Signed-off-by: Sebastian Kemper Code cleaned up by wdoekes. Closes #42. --- channel.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/channel.c b/channel.c index b849e6f1..e689e9f8 100644 --- a/channel.c +++ b/channel.c @@ -539,8 +539,18 @@ static void iov_write(struct pvt* pvt, int fd, struct iovec * iov, int iovcnt) } } +static inline void change_audio_endianness_to_le( + attribute_unused struct iovec *iov, attribute_unused int iovcnt) +{ +#if __BYTE_ORDER == __BIG_ENDIAN + for (; iovcnt-- > 0; ++iov) { + ast_swapcopy_samples(iov->iov_base, iov->iov_base, iov->iov_len / 2); + } +#endif +} + #/* */ -static void timing_write (struct pvt* pvt) +static void timing_write(struct pvt* pvt) { size_t used; int iovcnt; @@ -566,6 +576,7 @@ static void timing_write (struct pvt* pvt) iovcnt = mixb_read_n_iov (&pvt->a_write_mixb, iov, FRAME_SIZE); mixb_read_n_iov (&pvt->a_write_mixb, iov, FRAME_SIZE); mixb_read_upd (&pvt->a_write_mixb, FRAME_SIZE); + change_audio_endianness_to_le(iov, iovcnt); } else if (used > 0) { @@ -579,6 +590,7 @@ static void timing_write (struct pvt* pvt) iov[iovcnt].iov_base = silence_frame; iov[iovcnt].iov_len = FRAME_SIZE - used; iovcnt++; + change_audio_endianness_to_le(iov, iovcnt); } else { @@ -588,6 +600,7 @@ static void timing_write (struct pvt* pvt) iov[0].iov_base = silence_frame; iov[0].iov_len = FRAME_SIZE; iovcnt = 1; + // no need to change_audio_endianness_to_le for zeroes // continue; } From d1a86f64a336224b9b1cac499442c31eba9efef7 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Sun, 3 Sep 2017 12:08:59 +0200 Subject: [PATCH 053/117] Add Asterisk 14/15 Travis checks. --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 43d06ad6..a9c3bc8e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,6 +13,8 @@ env: # Extend build matrix matrix: include: + - env: ASTVER=15 + - env: ASTVER=14 - env: ASTVER=13 - env: ASTVER=12 - env: ASTVER=11 From 4ef5ad7eea7245a031101875be08b924aa1e151b Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Wed, 13 Sep 2017 16:43:52 +0200 Subject: [PATCH 054/117] Revert "fix Sparc loud noise problem #201" This reverts commit ab939532c71b991de2f66582d7ee2c553b6a918e. Apparently there was too much byteswapping (on big-endian machines) going on with both ab93953 and 53e83a4. Reverting ab93953 appears to work for @Infactum. Closes #44. --- channel.c | 1 - 1 file changed, 1 deletion(-) diff --git a/channel.c b/channel.c index e689e9f8..a26e0a1f 100644 --- a/channel.c +++ b/channel.c @@ -910,7 +910,6 @@ static int channel_write (struct ast_channel* channel, struct ast_frame* f) } } - ast_frame_byteswap_le(f); if (pvt->a_timer) { From 8c14e40e1f57ffda0eec1b745d289ec71a83ca15 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Mon, 5 Mar 2018 09:49:10 +0100 Subject: [PATCH 055/117] Lower default timeout of 2sec to 5sec and 15sec to 40sec This should probably fix call drop issues with E3131A modem (same as E303). I cannot quickly find documentation about what the timeout "should" be, but using 3 timeout classes with short/medium/long makes more sense to me than having the classes defined by the duration. Please report any issues that arise from this change. Suggested-by: @mthqwork Closes: #50 --- at_command.c | 18 +++++++++--------- at_queue.h | 21 +++++++++------------ 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/at_command.c b/at_command.c index 7d7bdb4a..1f767314 100644 --- a/at_command.c +++ b/at_command.c @@ -244,8 +244,8 @@ EXPORT_DEF int at_enque_pdu(struct cpvt * cpvt, const char * pdu, attribute_unus char * ptr = (char *) pdu; char buf[8+25+1]; at_queue_cmd_t at_cmd[] = { - { CMD_AT_CMGS, RES_SMS_PROMPT, ATQ_CMD_FLAG_DEFAULT, { ATQ_CMD_TIMEOUT_2S, 0} , NULL, 0 }, - { CMD_AT_SMSTEXT, RES_OK, ATQ_CMD_FLAG_DEFAULT, { ATQ_CMD_TIMEOUT_40S, 0} , NULL, 0 } + { CMD_AT_CMGS, RES_SMS_PROMPT, ATQ_CMD_FLAG_DEFAULT, { ATQ_CMD_TIMEOUT_MEDIUM, 0}, NULL, 0 }, + { CMD_AT_SMSTEXT, RES_OK, ATQ_CMD_FLAG_DEFAULT, { ATQ_CMD_TIMEOUT_LONG, 0}, NULL, 0 } }; size_t length = strlen(pdu); @@ -299,8 +299,8 @@ EXPORT_DEF int at_enque_sms (struct cpvt* cpvt, const char* destination, const c pvt_t* pvt = cpvt->pvt; at_queue_cmd_t at_cmd[] = { - { CMD_AT_CMGS, RES_SMS_PROMPT, ATQ_CMD_FLAG_DEFAULT, { ATQ_CMD_TIMEOUT_2S, 0} , NULL, 0 }, - { CMD_AT_SMSTEXT, RES_OK, ATQ_CMD_FLAG_DEFAULT, { ATQ_CMD_TIMEOUT_40S, 0} , NULL, 0 } + { CMD_AT_CMGS, RES_SMS_PROMPT, ATQ_CMD_FLAG_DEFAULT, { ATQ_CMD_TIMEOUT_MEDIUM, 0}, NULL, 0 }, + { CMD_AT_SMSTEXT, RES_OK, ATQ_CMD_FLAG_DEFAULT, { ATQ_CMD_TIMEOUT_LONG, 0}, NULL, 0 } }; if(pvt->use_pdu) @@ -493,10 +493,10 @@ EXPORT_DEF int at_enque_set_ccwa (struct cpvt* cpvt, attribute_unused const char int err; call_waiting_t value; at_queue_cmd_t cmds[] = { - /* 5 seconds timeout */ - ATQ_CMD_DECLARE_DYNIT(CMD_AT_CCWA_SET, ATQ_CMD_TIMEOUT_15S, 0), /* Set Call-Waiting On/Off */ - ATQ_CMD_DECLARE_STIT(CMD_AT_CCWA_STATUS, cmd_ccwa_get, ATQ_CMD_TIMEOUT_15S, 0), /* Query CCWA Status for Voice Call */ - + /* Set Call-Waiting On/Off */ + ATQ_CMD_DECLARE_DYNIT(CMD_AT_CCWA_SET, ATQ_CMD_TIMEOUT_MEDIUM, 0), + /* Query CCWA Status for Voice Call */ + ATQ_CMD_DECLARE_STIT(CMD_AT_CCWA_STATUS, cmd_ccwa_get, ATQ_CMD_TIMEOUT_MEDIUM, 0), }; at_queue_cmd_t * pcmd = cmds; unsigned count = ITEMS_OF(cmds); @@ -687,7 +687,7 @@ EXPORT_DEF int at_enque_flip_hold (struct cpvt* cpvt) EXPORT_DEF int at_enque_ping (struct cpvt * cpvt) { static const at_queue_cmd_t cmds[] = { - ATQ_CMD_DECLARE_STIT(CMD_AT, cmd_at, ATQ_CMD_TIMEOUT_1S, 0), /* 1 second timeout */ + ATQ_CMD_DECLARE_STIT(CMD_AT, cmd_at, ATQ_CMD_TIMEOUT_SHORT, 0), }; return at_queue_insert_const(cpvt, cmds, ITEMS_OF(cmds), 1); diff --git a/at_queue.h b/at_queue.h index f57e5aa9..1ea25277 100644 --- a/at_queue.h +++ b/at_queue.h @@ -25,12 +25,9 @@ typedef struct at_queue_cmd #define ATQ_CMD_FLAG_IGNORE 0x02 /*!< ignore response non match condition */ struct timeval timeout; /*!< timeout value, started at time when command actually written on device */ -#define ATQ_CMD_TIMEOUT_1S 1 /*!< timeout value 1 sec */ -#define ATQ_CMD_TIMEOUT_2S 2 /*!< timeout value 2 sec */ -#define ATQ_CMD_TIMEOUT_5S 5 /*!< timeout value 5 sec */ -#define ATQ_CMD_TIMEOUT_10S 10 /*!< timeout value 10 sec */ -#define ATQ_CMD_TIMEOUT_15S 15 /*!< timeout value 15 ses */ -#define ATQ_CMD_TIMEOUT_40S 40 /*!< timeout value 40 ses */ +#define ATQ_CMD_TIMEOUT_SHORT 1 /*!< timeout value 1 sec */ +#define ATQ_CMD_TIMEOUT_MEDIUM 5 /*!< timeout value 5 sec */ +#define ATQ_CMD_TIMEOUT_LONG 40 /*!< timeout value 40 sec */ char* data; /*!< command and data to send in device */ unsigned length; /*!< data length */ @@ -41,7 +38,7 @@ typedef struct at_queue_cmd (e).cmd = (icmd); \ (e).res = RES_OK; \ (e).flags = iflags | ATQ_CMD_FLAG_STATIC; \ - (e).timeout.tv_sec = ATQ_CMD_TIMEOUT_2S; \ + (e).timeout.tv_sec = ATQ_CMD_TIMEOUT_MEDIUM; \ (e).timeout.tv_usec = 0; \ (e).data = (char*)(idata); \ (e).length = STRLEN(idata); \ @@ -52,7 +49,7 @@ typedef struct at_queue_cmd (e).cmd = (icmd); \ (e).res = RES_OK; \ (e).flags = iflags & ~ATQ_CMD_FLAG_STATIC; \ - (e).timeout.tv_sec = ATQ_CMD_TIMEOUT_2S; \ + (e).timeout.tv_sec = ATQ_CMD_TIMEOUT_MEDIUM; \ (e).timeout.tv_usec = 0; \ } while(0) #define ATQ_CMD_INIT_DYN(e,icmd) ATQ_CMD_INIT_DYNF(e, icmd, ATQ_CMD_FLAG_DEFAULT) @@ -60,15 +57,15 @@ typedef struct at_queue_cmd /* static initializers */ #define ATQ_CMD_DECLARE_STFT(cmd,res,data,flags,s,u) { (cmd), (res), ATQ_CMD_FLAG_STATIC|flags, {(s), (u)}, (char*)(data), STRLEN(data) } -#define ATQ_CMD_DECLARE_STF(cmd,res,data,flags) ATQ_CMD_DECLARE_STFT(cmd,res,data,flags,ATQ_CMD_TIMEOUT_2S,0) -//#define ATQ_CMD_DECLARE_STF(cmd,res,data,flags) { (cmd), (res), ATQ_CMD_FLAG_STATIC|flags, {ATQ_CMD_TIMEOUT_2S, 0}, (char*)(data), STRLEN(data) } +#define ATQ_CMD_DECLARE_STF(cmd,res,data,flags) ATQ_CMD_DECLARE_STFT(cmd, res, data, flags, ATQ_CMD_TIMEOUT_MEDIUM, 0) +//#define ATQ_CMD_DECLARE_STF(cmd,res,data,flags) { (cmd), (res), ATQ_CMD_FLAG_STATIC|flags, {ATQ_CMD_TIMEOUT_MEDIUM, 0}, (char*)(data), STRLEN(data) } #define ATQ_CMD_DECLARE_ST(cmd,data) ATQ_CMD_DECLARE_STF(cmd, RES_OK, data, ATQ_CMD_FLAG_DEFAULT) #define ATQ_CMD_DECLARE_STI(cmd,data) ATQ_CMD_DECLARE_STF(cmd, RES_OK, data, ATQ_CMD_FLAG_IGNORE) #define ATQ_CMD_DECLARE_STIT(cmd,data,s,u) ATQ_CMD_DECLARE_STFT(cmd, RES_OK, data, ATQ_CMD_FLAG_IGNORE,s,u) #define ATQ_CMD_DECLARE_DYNFT(cmd,res,flags,s,u) { (cmd), (res), flags & ~ATQ_CMD_FLAG_STATIC, {(s), (u)}, 0, 0 } -#define ATQ_CMD_DECLARE_DYNF(cmd,res,flags) ATQ_CMD_DECLARE_DYNFT(cmd,res,flags,ATQ_CMD_TIMEOUT_2S,0) -//#define ATQ_CMD_DECLARE_DYNF(cmd,res,flags) { (cmd), (res), flags & ~ATQ_CMD_FLAG_STATIC, {ATQ_CMD_TIMEOUT_2S, 0}, 0, 0 } +#define ATQ_CMD_DECLARE_DYNF(cmd,res,flags) ATQ_CMD_DECLARE_DYNFT(cmd,res,flags,ATQ_CMD_TIMEOUT_MEDIUM, 0) +//#define ATQ_CMD_DECLARE_DYNF(cmd,res,flags) { (cmd), (res), flags & ~ATQ_CMD_FLAG_STATIC, {ATQ_CMD_TIMEOUT_MEDIUM, 0}, 0, 0 } #define ATQ_CMD_DECLARE_DYN(cmd) ATQ_CMD_DECLARE_DYNF(cmd, RES_OK, ATQ_CMD_FLAG_DEFAULT) #define ATQ_CMD_DECLARE_DYNI(cmd) ATQ_CMD_DECLARE_DYNF(cmd, RES_OK, ATQ_CMD_FLAG_IGNORE) #define ATQ_CMD_DECLARE_DYNIT(cmd,s,u) ATQ_CMD_DECLARE_DYNFT(cmd, RES_OK, ATQ_CMD_FLAG_IGNORE,s,u) From cfab716bf0f86a523b28e97d16c5a22149ae5b90 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Mon, 5 Mar 2018 10:15:59 +0100 Subject: [PATCH 056/117] Improve asterisk/modules dir autodetection Previously ./configure would continue after not finding DESTDIR for the module and then fail at 'make install'. Now it requires you to set DESTDIR if autodetection fails. Also added lib64 in the default paths to try. Reported-by: @alaa2003 Closes: #49 --- Makefile.in | 10 +++++++++- bootstrap | 2 +- configure.ac | 21 +++++++++++++-------- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/Makefile.in b/Makefile.in index 1c7a6094..c5158740 100644 --- a/Makefile.in +++ b/Makefile.in @@ -52,7 +52,15 @@ all: @TARGET@ install: all $(STRIP) $(PROJM) - $(INSTALL) -m 755 $(PROJM) @DESTDIR@ +ifneq (@DESTDIR@,) + $(INSTALL) -m 644 $(PROJM) @DESTDIR@ +else + @echo >&2 + @echo "*** Asterisk modules directory was not auto-detected." >&2 + @echo "*** Please copy $(PROJM) to the appropriate modules directory yourself." >&2 + @echo >&2 + @false +endif $(PROJM): $(chan_donglem_so_OBJS) Makefile $(LD) $(LDFLAGS) $(SOLINK) -o $@ $(chan_donglem_so_OBJS) $(LIBS) diff --git a/bootstrap b/bootstrap index ac637f2b..b8ca005a 100755 --- a/bootstrap +++ b/bootstrap @@ -5,4 +5,4 @@ autoconf # We don't use Makefile.am, but we *do* use automake to add # "missing" files used by configure. -automake -a 2>&1 | grep -vF Makefile.am +automake -a 2>&1 | grep -vF Makefile.am; true diff --git a/configure.ac b/configure.ac index 8fc2bd59..9c20cecd 100644 --- a/configure.ac +++ b/configure.ac @@ -83,6 +83,7 @@ dnl Checks for header files. AC_CHECK_HEADERS([fcntl.h stdlib.h string.h sys/time.h termios.h]) AC_DEFUN([AC_HEADER_FIND], [ file=$1 + found=no for path in $2 ; do AC_MSG_CHECKING([whether $file in $path]) if test -f $path/$file ; then @@ -91,11 +92,10 @@ AC_DEFUN([AC_HEADER_FIND], [ AC_MSG_RESULT([yes]) AC_CHECK_HEADER([$file]) break; - else - AC_MSG_RESULT([no]) fi + AC_MSG_RESULT([no]) done - if test -z "$found" ; then + if test "$found" = "no"; then AC_MSG_ERROR([Can't find "$file"]) fi ] @@ -183,6 +183,7 @@ AC_CC_OPT([-MD -MT conftest.o -MF /dev/null -MP],[-MD -MT \$@ -MF .\$(subst /,_, AC_DEFUN([AC_CHECK_DESTDIR], [ if test -z "$DESTDIR" ; then + found=no for path in $1 ; do AC_MSG_CHECKING([whether DESTDIR is $path]) if test -f $path/pbx_config.so ; then @@ -190,17 +191,21 @@ AC_DEFUN([AC_CHECK_DESTDIR], [ found="yes" DESTDIR=$path break; - else - AC_MSG_RESULT([no]) fi + AC_MSG_RESULT([no]) done - if test -z "$found" ; then - AC_MSG_ERROR(DESTDIR is unknown, please explicite set like DESTDIR=/usr/lib/asterisk/modules ./configure) + if test "$found" = "no"; then + AC_MSG_ERROR([DESTDIR is unknown, please be explicit: ./configure DESTDIR=/usr/lib/asterisk/modules]) fi fi ]) -AC_CHECK_DESTDIR([/usr/lib/asterisk/modules /usr/local/lib/asterisk/modules /opt/local/lib/asterisk/modules]) +dnl This detection is kind of flaky. Don't rely on it. The module +dnl directory could be lib64 or x86_64-linux-gnu or even somewhere +dnl completely custom. +AC_CHECK_DESTDIR([ \ + /usr/lib/asterisk/modules /usr/lib64/asterisk/modules \ + /usr/local/lib/asterisk/modules /opt/local/lib/asterisk/modules]) dnl Checks for library functions. AC_FUNC_MEMCMP From fe2861efde02d0c53dafbf29f9fc71e214d6f605 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Mon, 5 Mar 2018 10:33:06 +0100 Subject: [PATCH 057/117] Hide -Wimplicit-fallthrough= false positives gcc apparently groks "/* fall through */" as well as __attribute__ ((fallthrough)) which probably is not supported on all compilers. --- at_response.c | 12 +++++++----- chan_dongle.c | 2 +- pdiscovery.c | 4 ++-- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/at_response.c b/at_response.c index 90c4d38f..e3e95ac7 100644 --- a/at_response.c +++ b/at_response.c @@ -206,10 +206,10 @@ static int at_response_ok (struct pvt* pvt, at_res_t res) case CMD_AT_D: pvt->dialing = 1; - if(task->cpvt != &pvt->sys_chan) + if (task->cpvt != &pvt->sys_chan) { pvt->last_dialed_cpvt = task->cpvt; - /* passthrow */ - + } + /* fall through */ case CMD_AT_A: case CMD_AT_CHLD_2x: /* not work, ^CONN: appear before OK for CHLD_ANSWER @@ -459,9 +459,11 @@ static int at_response_error (struct pvt* pvt, at_res_t res) break; case CMD_AT_CHLD_2: - if(!CPVT_TEST_FLAG(task->cpvt, CALL_FLAG_HOLD_OTHER) || task->cpvt->state != CALL_STATE_INIT) + if (!CPVT_TEST_FLAG(task->cpvt, CALL_FLAG_HOLD_OTHER) || + task->cpvt->state != CALL_STATE_INIT) { break; - /* passthru */ + } + /* fall through */ case CMD_AT_D: ast_log (LOG_ERROR, "[%s] Dial failed\n", PVT_ID(pvt)); queue_control_channel (task->cpvt, AST_CONTROL_CONGESTION); diff --git a/chan_dongle.c b/chan_dongle.c index 760b178f..5e6d725a 100644 --- a/chan_dongle.c +++ b/chan_dongle.c @@ -684,8 +684,8 @@ static void * do_discovery(void * arg) { case DEV_STATE_RESTARTED: pvt_stop(pvt); - /* passthru */ pvt->desired_state = DEV_STATE_STARTED; + /* fall through */ case DEV_STATE_STARTED: pvt_start(pvt); break; diff --git a/pdiscovery.c b/pdiscovery.c index f7712775..adb0b974 100644 --- a/pdiscovery.c +++ b/pdiscovery.c @@ -400,7 +400,7 @@ static int pdiscovery_interfaces(const char * devname, const char * name, int le #/* */ -static const const struct pdiscovery_device * pdiscovery_lookup_ids(const char * devname, const char * name, int len) +static const struct pdiscovery_device * pdiscovery_lookup_ids(const char * devname, const char * name, int len) { unsigned vid; unsigned pid; @@ -495,7 +495,7 @@ static char * pdiscovery_handle_cimi(const char * devname, char * str) ast_debug(4, "[%s discovery] found IMSI %s\n", devname, imsi); return imsi; } - // passthru + /* fall through */ default: state = STATE_BEGIN; } From bf0e955efbf19a68e696c22c446a01f3e7d8e502 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Mon, 12 Mar 2018 22:34:32 +0100 Subject: [PATCH 058/117] pdu/parsing: Fix unsupported DCS value 19 After looking at specs from http://www.3gpp.org/ftp//Specs/archive/23_series/23.038/ it looks like it should allow more than we did. Closes #41 (I think). --- pdu.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/pdu.c b/pdu.c index 9db8a48f..e8d255f6 100644 --- a/pdu.c +++ b/pdu.c @@ -784,18 +784,30 @@ EXPORT_DEF const char * pdu_parse(char ** pdu, size_t tpdu_length, char * oa, si switch (dcs_hi) { case 0x0: /* HIGH 0000: Regular message */ + case 0x1: /* HIGH 0001: Regular message with class */ + case 0x4: /* HIGH 0100: Marked for self-destruct */ + case 0x5: /* HIGH 0101: Marked for self-destruct with class */ case 0xF: /* HIGH 1111: Data coding/message class */ - /* apparently bits 0..3 are not reserved anymore: + /* Apparently bits 0..3 are not reserved anymore: * bits 3..2: {7bit, 8bit, ucs2, undef} */ alphabet = PDU_DCS_ALPHABET(dcs); + /* Bits 3..2 set to 11 is reserved, but + * according to 3GPP TS 23.038 v14.0.0 (2017-03) + * for HIGH 1111 bit 3 (regardless of bit 2) is + * reserved. */ if (alphabet == PDU_DCS_ALPHABET_MASK) { reserved = 1; } - /* if 0xF then (dsc_lo & 3): { + /* if 0x1 || 0xF then (dsc_lo & 3): { * class0, class1-ME-specific, * class2-SIM-specific, - * class3-TE-specific (GSM TS 07.05)} */ + * class3-TE-specific (3GPP TS 27.005)} */ break; + case 0x2: /* HIGH 0010: Compressed regular message */ + 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"; case 0xC: /* HIGH 1100: "Discard" MWI */ case 0xD: /* HIGH 1101: "Store" MWI */ /* if 0xC then the recipient may discard message From 6ba9cf2637b0d3580b6b43292c333c04ccc8e8d8 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Mon, 12 Mar 2018 23:10:05 +0100 Subject: [PATCH 059/117] pdu/parsing: Allow all values for TP-PID Closes #53, reported by @standardgbg. See: http://www.3gpp.org/ftp//Specs/archive/23_series/23.040/23040-e00.zip --- pdu.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/pdu.c b/pdu.c index e8d255f6..1be99493 100644 --- a/pdu.c +++ b/pdu.c @@ -761,9 +761,24 @@ EXPORT_DEF const char * pdu_parse(char ** pdu, size_t tpdu_length, char * oa, si return "Can't parse PID"; } - /* TODO: support other types of messages */ if (pid != PDU_PID_SMS && !(0x41 <= pid && pid <= 0x47) /* PDU_PID_SMS_REPLACE_MASK */) { - return "Unhandled PID value, only SMS supported"; + /* 3GPP TSS 23.040 v14.0.0 (2017-013) */ + /* """The MS (Mobile Station, _we_) shall interpret + * reserved, obsolete, or unsupported values as the + * value 00000000 but shall store them exactly as + * received.""" */ + /* Lots of small variations in interpretations, but none + * really useful to us. We'll just go with accepting + * everything. */ + /* A handfull from the list: */ + /* 0x20..0x3E: different "telematic" devices */ + /* 0x32: e-mail */ + /* 0x38..0x3E: various Service Center specific codes */ + /* 0x3F: gsm/umts station */ + /* 0x40: silent sms; ME should ack but not tell the user */ + /* 0x41..0x47: sms updates (replacing the previous sms #N) */ + ast_log(LOG_NOTICE, "Treating TP-PID value 0x%hhx as regular SMS\n", + (unsigned char)pid); } dcs = pdu_parse_byte(pdu, &pdu_length); From 3e2b3ca6fd4d0a996404dfdc724973e92f2683eb Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Mon, 12 Mar 2018 23:14:03 +0100 Subject: [PATCH 060/117] travis: Unbreak CI --- .travis.yml | 2 +- test/parse.c | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a9c3bc8e..cce556ba 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,7 +29,7 @@ before_script: - aclocal - autoconf - automake -a || true # do install stuff, don't create makefile - - ./configure --with-astversion=$ASTVER --with-asterisk=./asterisk-$ASTVER/include + - ./configure --with-astversion=$ASTVER --with-asterisk=./asterisk-$ASTVER/include DESTDIR=/tmp script: - make clean all diff --git a/test/parse.c b/test/parse.c index 42fbf2b1..14afa125 100644 --- a/test/parse.c +++ b/test/parse.c @@ -9,6 +9,14 @@ int ok = 0; int faults = 0; +/* We call ast_log from pdu.c, so we'll fake an implementation here. */ +void ast_log(int level, const char* fmt, ...) +{ + /* Silence compiler warnings */ + (void)level; + (void)fmt; +} + #/* */ void test_parse_cnum() { From fd544d628d134cfe9cc2df6b5315298e93698664 Mon Sep 17 00:00:00 2001 From: grray Date: Tue, 19 Jun 2018 14:43:34 +0300 Subject: [PATCH 061/117] USSD encoding fix for E3531 Huawei E3531 works fine with firmware 21.318.29.00.769, voice and sms, except USSD. Adding it to seven_bit_modems[] solves issue. (#56) --- at_response.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/at_response.c b/at_response.c index e3e95ac7..6a47736c 100644 --- a/at_response.c +++ b/at_response.c @@ -1616,7 +1616,8 @@ static int at_response_cgmm (struct pvt* pvt, const char* str) "E153", "E156B", "E1752", - "E261" + "E261", + "E3531" }; ast_copy_string (pvt->model, str, sizeof (pvt->model)); From 4e16d96618fc614a4b9bd61558fc8dada86e755c Mon Sep 17 00:00:00 2001 From: misuzu Date: Tue, 15 Oct 2019 16:30:28 +0300 Subject: [PATCH 062/117] Add with-iconv --- configure.ac | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 9c20cecd..c9a994ab 100644 --- a/configure.ac +++ b/configure.ac @@ -23,6 +23,13 @@ AC_ARG_WITH( [ if test "x$with_asterisk" = "xyes" -o "x$with_asterisk" = "xno" ; then AC_MSG_ERROR([Invalid --with-asterisk=path value]); fi ], [ with_asterisk="../include /usr/include /usr/local/include /opt/local/include" ] ) +dnl Set iconv headers patch +AC_ARG_WITH( + [iconv], + AS_HELP_STRING([--with-iconv=path], [set iconv headers location]), + [ if test "x$with_iconv" = "xyes" -o "x$with_iconv" = "xno" ; then AC_MSG_ERROR([Invalid --with-iconv=path value]); fi ], + [ with_iconv="../include /usr/include /usr/local/include /opt/local/include" ] +) dnl Required: asterisk version AC_ARG_WITH( @@ -102,7 +109,7 @@ AC_DEFUN([AC_HEADER_FIND], [ ) AC_HEADER_FIND([asterisk.h], $with_asterisk) -AC_HEADER_FIND([iconv.h], /usr/include /usr/local/include /opt/local/include) +AC_HEADER_FIND([iconv.h], $with_iconv) AC_DEFINE([ICONV_CONST],[], [Define to const if you has iconv() const declaration of input buffer]) AC_MSG_CHECKING([for iconv use const inbuf]) From 62259b2049a90581068e82150162fa1fbd7da93e Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Wed, 20 Nov 2019 09:09:58 +0100 Subject: [PATCH 063/117] Trim trailing garbage from provider name causing ast_json_vpack error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The AT+COPS command has been observed to return similar but different provider names. Sometimes with a trailing illegal character, or a trailing @. Dongle make/model E1550 (patched for voice support), identified by Linux as "Huawei Technologies Co., Ltd. E161/E169/E620/E800 HSDPA Modem". Example output: AT+COPS? +COPS: 0,2,"25020",2 AT+COPS=? +COPS: (2,"TELE2 RU","TELE2 RU","25020",2), (1,"MTS-RUS","MTS","25001",2), (1,"Beeline","Beeline","25099",2), (1,"MegaFon) AT+COPS=? +COPS: (2,"TELE2 RU","TELE2 RU","25020",2), (1,"MegaFon RUS","MegaFon","25002",2), (1,"Beeline","Beeline","25099",2), (1,) AT+COPS=? +COPS: (2,"Tele2@","Tele2�","25020",2), (1,"MTS-RUS","MTS","25001",2), (1,"Beeline","Beeline","25099",2), (1,"MegaFon RUS) (that replacement character (U+FFFD) is probably something else; likely a single octet in the >=128 range) A dongle change (same make/model) did not help. A simcard change did not help. Likely culprit for now seems to be the provider (Russian Tele2?). This changeset drops trailing "@" and trailing non-printables from the provider name. It would still switch from "TELE2 RU" to "Tele2", but not to something with unreadable characters. Closes #39. This also fixes this error: json.c:607 ast_json_vpack: Error building JSON from '{s: s, s: s}': Invalid UTF-8 string. Generated at: for (idx = 0; idx < ITEMS_OF(dev_vars); ++idx) { # dev_vars[1] == { "DONGLEPROVIDER", pvt->provider_name } pbx_builtin_setvar_helper(channel, dev_vars[idx].name, dev_vars[idx].value); } Because newer Asterisken use JSON to publish these messages, and that character (which isn't U+FFFD) is illegal UTF-8. Closes #69. Thanks go to sergio (@532910) for debugging and testing this issue. --- at_parse.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/at_parse.c b/at_parse.c index 888ff8a6..e7d872fe 100644 --- a/at_parse.c +++ b/at_parse.c @@ -103,6 +103,20 @@ EXPORT_DEF char* at_parse_cops (char* str) if(marks[3][-1] == '"') marks[3]--; marks[3][0] = 0; + /* Sometimes there is trailing garbage here; + * e.g. "Tele2@" or "Tele2" instead of "Tele2". + * Unsure why it happens (provider? dongle?), but it causes + * trouble later on too (at pbx_builtin_setvar_helper which + * is not encoding agnostic anymore, now that it uses json + * for messaging). See wdoekes/asterisk-chan-dongle + * GitHub issues #39 and #69. */ + while (marks[3] > marks[2] && ( + (unsigned char)marks[3][-1] < 32 || + (unsigned char)marks[3][-1] == '@' || + (unsigned char)marks[3][-1] >= 128)) { + marks[3]--; + marks[3][0] = 0; + } return marks[2]; } From e08f0d44b0429a0752942a15bdb091438109809f Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Wed, 20 Nov 2019 09:42:15 +0100 Subject: [PATCH 064/117] Fix missing trailing LF in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0e058549..c7897339 100644 --- a/README.md +++ b/README.md @@ -220,4 +220,4 @@ see: [HOWTO](https://gist.github.com/garronej/01f0dac45efe9161969a83890c019efa) For additional information about Huawei dongle usage look to chan\_dongle Wiki at http://wiki.e1550.mobi and chan\_dongle project home at -https://github.com/wdoekes/asterisk-chan-dongle/ \ No newline at end of file +https://github.com/wdoekes/asterisk-chan-dongle/ From 2067262e43eb68de9c9327463b7ad0903889bd6e Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Wed, 20 Nov 2019 09:45:31 +0100 Subject: [PATCH 065/117] Add chanvar setting ast_debug to aid in future debugging --- channel.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/channel.c b/channel.c index a26e0a1f..1022b6e1 100644 --- a/channel.c +++ b/channel.c @@ -1222,8 +1222,13 @@ static void set_channel_vars(struct pvt* pvt, struct ast_channel* channel) //ast_string_field_set (channel, language, CONF_SHARED(pvt, language); #endif /* ^11- */ - for(idx = 0; idx < ITEMS_OF(dev_vars); ++idx) - pbx_builtin_setvar_helper (channel, dev_vars[idx].name, dev_vars[idx].value); + for (idx = 0; idx < ITEMS_OF(dev_vars); ++idx) { + ast_debug(1, "[%s] Setting chanvar %s = %s\n", + PVT_ID(pvt), + (dev_vars[idx].name ? dev_vars[idx].name : "(null)"), + (dev_vars[idx].value ? dev_vars[idx].value : "(null)")); + pbx_builtin_setvar_helper(channel, dev_vars[idx].name, dev_vars[idx].value); + } } /* NOTE: called from device and current levels with locked pvt */ From 8b1827c536b0a702608bb4c2f207e54d6c48b8f0 Mon Sep 17 00:00:00 2001 From: Garrone Joseph Date: Mon, 21 Jan 2019 10:41:12 +0100 Subject: [PATCH 066/117] Better params for the JITTERBUFFER in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c7897339..07f29719 100644 --- a/README.md +++ b/README.md @@ -205,7 +205,7 @@ The Gosub will be executed for each destination channel." [from-sip] ; This will be executed by an inbound SIP channel ( call initiated on the SIP side ) - exten => _[+0-9].,1,Set(JITTERBUFFER(adaptive)=default) + exten => _[+0-9].,1,Set(JITTERBUFFER(adaptive)=2000,1600,120) same => n,Set(AGC(rx)=4000) same => n,Dial(Dongle/i:${IMEI_OF_MY_DONGLE}/${NUMBER_OF_BOB}) From c0bfc3233102b2835f1f6df488f6fbb35bbf3049 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Thu, 12 Dec 2019 11:44:42 +0100 Subject: [PATCH 067/117] travis: Alter asterisk include files remote path --- .travis.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index cce556ba..d12772d4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,16 +13,17 @@ env: # Extend build matrix matrix: include: - - env: ASTVER=15 - - env: ASTVER=14 + - env: ASTVER=16 + #- env: ASTVER=15 + #- env: ASTVER=14 - env: ASTVER=13 - - env: ASTVER=12 + #- env: ASTVER=12 - env: ASTVER=11 - - env: ASTVER=10 + #- env: ASTVER=10 - env: ASTVER=1.8 before_install: - - wget http://junk.devs.nu/asterisk-$ASTVER-include.tar.bz2 + - wget http://junk.devs.nu/a/asterisk/asterisk-$ASTVER-include.tar.bz2 - tar jxf asterisk-$ASTVER-include.tar.bz2 before_script: From 923db635e0b29ce65078bfcf1e118d6a99bddf76 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Wed, 12 Feb 2020 09:50:22 +0100 Subject: [PATCH 068/117] Disable Asterisk 1.8 travis test It's old, and conflicts with #75 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d12772d4..2c104f6d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,7 +20,7 @@ matrix: #- env: ASTVER=12 - env: ASTVER=11 #- env: ASTVER=10 - - env: ASTVER=1.8 + #- env: ASTVER=1.8 before_install: - wget http://junk.devs.nu/a/asterisk/asterisk-$ASTVER-include.tar.bz2 From fe3b185f35b8096763ca782b79479976926d9e78 Mon Sep 17 00:00:00 2001 From: Max von Buelow Date: Thu, 13 Feb 2020 12:11:24 +0100 Subject: [PATCH 069/117] Disable travis test with unstable asterisk versions --- .travis.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index d12772d4..f271ccde 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,13 +14,7 @@ env: matrix: include: - env: ASTVER=16 - #- env: ASTVER=15 - #- env: ASTVER=14 - env: ASTVER=13 - #- env: ASTVER=12 - - env: ASTVER=11 - #- env: ASTVER=10 - - env: ASTVER=1.8 before_install: - wget http://junk.devs.nu/a/asterisk/asterisk-$ASTVER-include.tar.bz2 From f46dd16e87e921aa96bf33663180409251096909 Mon Sep 17 00:00:00 2001 From: Max von Buelow Date: Thu, 13 Feb 2020 12:29:28 +0100 Subject: [PATCH 070/117] Added complete GSM-7 tables; Implemented GSM-7 escape --- char_conv.c | 31 +- gsm7_luts.h | 3153 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 3163 insertions(+), 21 deletions(-) create mode 100644 gsm7_luts.h diff --git a/char_conv.c b/char_conv.c index e77639f5..8c76e995 100644 --- a/char_conv.c +++ b/char_conv.c @@ -246,23 +246,6 @@ static ssize_t char_to_hexstr_7bit_pad_6(const char* in, size_t in_length, char* return char_to_hexstr_7bit_padded(in, in_length, out, out_size, 6); } -/* GSM 03.38 7bit alphabet */ -static const char *const alphabet_7bit[128] = { - "@", "£", "$", "¥", "è", "é", "ù", "ì", "ò", "Ç", "\n", "Ø", - "ø", "\r", "Å", "å", "∆", "_", "Φ", "Γ", "Λ", "Ω", "Π", "Ψ", - "Σ", "Θ", "Ξ", "\x1b" /* ESC */, "Æ", "æ", "ß", "É", " ", "!", - "\"", "#", "¤", "%", "&", "'", "(", ")", "*", "+", ",", "-", - ".", "/", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", - ":", ";", "<", "=", ">", "?", "¡", "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", "Ä", "Ö", "Ñ", - "Ü", "§", "¿", "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", "ä", "ö", "ñ", "ü", "à" - /* TODO: ESC could unlock the basic charset extension, - * interpeting the following char differently. */ -}; - static ssize_t hexstr_7bit_to_char_padded(const char* in, size_t in_length, char* out, size_t out_size, unsigned in_padding) { @@ -298,6 +281,8 @@ static ssize_t hexstr_7bit_to_char_padded(const char* in, size_t in_length, char memset(buf, 0, sizeof(buf)); /* parse the hexstring */ + int esc = 0; + int ss = 0, ls = 0; for (x = i = 0; i != in_length; i++) { if (x >= out_size) return -1; @@ -311,10 +296,14 @@ static ssize_t hexstr_7bit_to_char_padded(const char* in, size_t in_length, char in_padding -= 7; value >>= 7; { - const char *val = alphabet_7bit[value & 0x7F]; - do { - out[x++] = *val++; - } while (*val && x < out_size); + const char *val = (esc ? LUT_GSM7_SS : LUT_GSM7_LS)[esc ? ss : ls][value & 0x7F]; + if (val[0] == '\x1b' && !val[1]) { + esc = 1; + } else { + do { + out[x++] = *val++; + } while (*val && x < out_size); + } } } } diff --git a/gsm7_luts.h b/gsm7_luts.h new file mode 100644 index 00000000..6c304291 --- /dev/null +++ b/gsm7_luts.h @@ -0,0 +1,3153 @@ +#ifndef CHAN_DONGLE_GSM7_LUTS_H_INCLUDED +#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 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 int LUT_GSM7_REV1[] = { + + 0, 3, -1, 1, -1, -1, 10, -1, -1, 5, 6, 9, 7, 8, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 2, -1, 4, -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, -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 +}; +#define GSM7_INVALID 254 +static const uint8_t LUT_GSM7_REV2_INV[] = { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }; +static const uint8_t LUT_GSM7_REV2[11][256][14] = { + { + { 228, 228, 255, 254, 228, 228, 228, 228, 228, 228, 228, 228, 228, 228 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 10, 10, 254, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 138 }, + { 13, 13, 254, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 27, 27, 254, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 32, 32, 254, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32 }, + { 33, 33, 254, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33 }, + { 34, 34, 254, 34, 133, 133, 133, 133, 133, 133, 133, 133, 133, 133 }, + { 35, 35, 254, 35, 151, 151, 151, 151, 151, 151, 151, 151, 151, 151 }, + { 2, 2, 254, 2, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130 }, + { 37, 37, 254, 37, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135 }, + { 38, 38, 254, 38, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136 }, + { 39, 39, 254, 39, 137, 137, 137, 137, 137, 137, 137, 137, 137, 137 }, + { 40, 40, 254, 40, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41 }, + { 41, 41, 254, 41, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40 }, + { 42, 42, 254, 42, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152 }, + { 43, 43, 254, 43, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140 }, + { 44, 44, 254, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44 }, + { 45, 45, 254, 45, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142 }, + { 46, 46, 254, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46 }, + { 47, 47, 254, 47, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143 }, + { 48, 48, 254, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48 }, + { 49, 49, 254, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49 }, + { 50, 50, 254, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50 }, + { 51, 51, 254, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51 }, + { 52, 52, 254, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52 }, + { 53, 53, 254, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53 }, + { 54, 54, 254, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54 }, + { 55, 55, 254, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55 }, + { 56, 56, 254, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56 }, + { 57, 57, 254, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57 }, + { 58, 58, 254, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58 }, + { 59, 59, 254, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59 }, + { 60, 60, 254, 60, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144 }, + { 61, 61, 254, 61, 145, 145, 145, 145, 145, 145, 145, 145, 145, 145 }, + { 62, 62, 254, 62, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146 }, + { 63, 63, 254, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 }, + { 0, 0, 254, 0, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }, + { 65, 65, 254, 65, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193 }, + { 66, 66, 254, 66, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194 }, + { 67, 67, 254, 67, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195 }, + { 68, 68, 254, 68, 196, 196, 196, 196, 196, 196, 196, 196, 196, 196 }, + { 69, 69, 254, 69, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197 }, + { 70, 70, 254, 70, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198 }, + { 71, 71, 254, 71, 199, 199, 199, 199, 199, 199, 199, 199, 199, 199 }, + { 72, 72, 254, 72, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200 }, + { 73, 73, 254, 73, 201, 201, 201, 201, 201, 201, 201, 201, 201, 201 }, + { 74, 74, 254, 74, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202 }, + { 75, 75, 254, 75, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203 }, + { 76, 76, 254, 76, 204, 204, 204, 204, 204, 204, 204, 204, 204, 204 }, + { 77, 77, 254, 77, 205, 205, 205, 205, 205, 205, 205, 205, 205, 205 }, + { 78, 78, 254, 78, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206 }, + { 79, 79, 254, 79, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207 }, + { 80, 80, 254, 80, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208 }, + { 81, 81, 254, 81, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209 }, + { 82, 82, 254, 82, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210 }, + { 83, 83, 254, 83, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211 }, + { 84, 84, 254, 84, 212, 212, 212, 212, 212, 212, 212, 212, 212, 212 }, + { 85, 85, 254, 85, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213 }, + { 86, 86, 254, 86, 214, 214, 214, 214, 214, 214, 214, 214, 214, 214 }, + { 87, 87, 254, 87, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215 }, + { 88, 88, 254, 88, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216 }, + { 89, 89, 254, 89, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217 }, + { 90, 90, 254, 90, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218 }, + { 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188 }, + { 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175 }, + { 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190 }, + { 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148 }, + { 17, 17, 254, 17, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150 }, + { 254, 254, 254, 125, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 97, 97, 254, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97 }, + { 98, 98, 254, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98 }, + { 99, 99, 254, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99 }, + { 100, 100, 254, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100 }, + { 101, 101, 254, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101 }, + { 102, 102, 254, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102 }, + { 103, 103, 254, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103 }, + { 104, 104, 254, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104 }, + { 105, 105, 254, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105 }, + { 106, 106, 254, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106 }, + { 107, 107, 254, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107 }, + { 108, 108, 254, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108 }, + { 109, 109, 254, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109 }, + { 110, 110, 254, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110 }, + { 111, 111, 254, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111 }, + { 112, 112, 254, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112 }, + { 113, 113, 254, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113 }, + { 114, 114, 254, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114, 114 }, + { 115, 115, 254, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115 }, + { 116, 116, 254, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116 }, + { 117, 117, 254, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117 }, + { 118, 118, 254, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118 }, + { 119, 119, 254, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119 }, + { 120, 120, 254, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120 }, + { 121, 121, 254, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121 }, + { 122, 122, 254, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122 }, + { 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168 }, + { 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192 }, + { 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169 }, + { 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189, 189 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 64, 254, 254, 254, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 1, 1, 254, 1, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129 }, + { 36, 36, 254, 254, 134, 134, 134, 134, 134, 134, 134, 134, 134, 134 }, + { 3, 3, 254, 3, 131, 131, 131, 131, 131, 131, 131, 131, 131, 131 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 95, 95, 254, 95, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 18, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 36, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 96, 254, 254, 254, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132 }, + { 254, 254, 254, 193, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 193, 142, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 225, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 219, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 91, 91, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 14, 14, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 28, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 9, 9, 254, 19, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 31, 31, 254, 31, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 159, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 201, 201, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 93, 93, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 207, 207, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 139, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 220, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 92, 92, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 11, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 213, 213, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 94, 94, 254, 94, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 30, 30, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 127, 127, 254, 127, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 225, 143, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 255, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 251, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 123, 123, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 15, 15, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 29, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 227, 137, 137, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 4, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 5, 5, 254, 5, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 133, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 7, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 233, 233, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 125, 125, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 8, 8, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 239, 239, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 140, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 252, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 124, 124, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 12, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 6, 6, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 245, 245, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 126, 126, 254, 126, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 } + }, + { + { 254, 254, 254, 158, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 19, 19, 254, 147, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 16, 16, 254, 16, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 25, 25, 254, 153, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 20, 20, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 26, 26, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 22, 22, 254, 150, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 24, 24, 254, 152, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 18, 18, 254, 146, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 23, 23, 254, 151, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 21, 21, 254, 149, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 } + }, + { + { 255, 230, 232, 232, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 } + }, + { + { 254, 255, 136, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 199, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 231, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 201, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 233, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 211, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 243, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 } + }, + { + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 21, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 } + }, + { + { 254, 254, 254, 254, 187, 155, 187, 155, 155, 155, 155, 155, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 0, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 1, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 2, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 3, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 4, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 5, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 6, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 7, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 8, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 9, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 11, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 12, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 14, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 15, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 16, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 17, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 18, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 19, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 20, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 21, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 22, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 23, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 24, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 25, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 26, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 28, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 29, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 30, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 31, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 34, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 35, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 36, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 37, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 38, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 39, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 42, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 43, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 45, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 47, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 60, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 61, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 62, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 64, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 65, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 66, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 67, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 68, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 69, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 70, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 71, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 72, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 73, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 74, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 75, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 76, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 77, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 78, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 79, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 80, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 81, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 82, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 83, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 84, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 85, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 86, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 87, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 88, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 89, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 90, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 91, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 92, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 93, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 94, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 95, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 96, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 166, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 167, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 170, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 171, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 172, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 173, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 174, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 176, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 177, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 178, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 179, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 180, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 181, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 182, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 183, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 184, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 153, 153, 153, 153, 153, 153, 153, 254, 254 }, + { 254, 254, 254, 254, 254, 154, 154, 154, 154, 154, 154, 154, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 156, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 157, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 158, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 159, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 160, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 161, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 162, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 163, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 164, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 165, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 185, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 186, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 123, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 124, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 125, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 126, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 127, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 0, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 1, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 2, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 3, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 4, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 5, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 6, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 7, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 8, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 9, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 11, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 15, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 16, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 19, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 20, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 21, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 22, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 23, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 24, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 25, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 26, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 28, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 29, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 30, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 31, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 34, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 35, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 36, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 37, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 38, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 39, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 42, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 43, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 45, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 47, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 61, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 62, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 64, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 65, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 66, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 67, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 68, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 70, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 74, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 75, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 76, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 77, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 78, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 79, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 80, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 81, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 82, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 83, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 84, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 85, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 86, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 89, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 90, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 93, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 94, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 95, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 96, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 123, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 124, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 125, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 164, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 165, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 166, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 167, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 170, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 153, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 154, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 156, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 157, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 158, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 159, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 160, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 161, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 162, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 163, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 126, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 127, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 171, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 172, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 173, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 174, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 176, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 177, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 178, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 179, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 180, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 } + }, + { + { 254, 254, 254, 254, 254, 167, 254, 254, 254, 254, 174, 0, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 0, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 1, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 2, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 3, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 4, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 5, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 6, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 7, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 8, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 15, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 16, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 19, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 20, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 21, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 22, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 23, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 24, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 25, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 26, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 28, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 29, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 30, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 31, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 34, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 35, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 36, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 37, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 38, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 39, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 42, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 43, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 45, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 47, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 61, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 62, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 64, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 65, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 66, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 67, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 68, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 70, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 71, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 73, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 74, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 76, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 77, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 78, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 80, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 81, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 82, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 83, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 84, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 89, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 90, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 93, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 94, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 95, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 96, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 166, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 167, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 170, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 171, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 172, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 156, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 157, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 158, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 159, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 160, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 161, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 162, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 163, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 164, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 165, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 123, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 124, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 125, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 126, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 127, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 173, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 0, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 1, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 2, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 3, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 4, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 5, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 6, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 7, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 8, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 9, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 11, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 12, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 15, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 16, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 17, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 19, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 20, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 21, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 22, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 23, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 24, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 25, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 26, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 28, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 29, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 30, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 31, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 34, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 35, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 36, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 37, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 38, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 39, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 42, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 43, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 45, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 47, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 61, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 62, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 64, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 65, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 66, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 67, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 68, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 70, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 71, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 73, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 74, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 75, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 76, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 77, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 78, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 79, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 80, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 81, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 82, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 83, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 84, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 85, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 86, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 87, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 89, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 90, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 91, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 93, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 94, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 95, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 96, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 123, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 124, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 125, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 126, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 156, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 157, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 158, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 159, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 160, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 161, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 162, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 163, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 164, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 165, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 127, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 } + }, + { + { 254, 254, 254, 254, 254, 254, 254, 174, 0, 254, 254, 254, 187, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 0, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 1, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 2, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 3, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 4, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 5, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 6, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 7, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 8, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 9, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 11, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 14, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 15, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 16, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 18, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 19, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 20, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 21, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 22, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 23, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 24, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 25, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 26, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 28, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 29, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 30, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 31, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 34, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 35, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 36, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 37, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 38, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 39, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 42, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 43, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 45, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 47, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 61, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 62, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 64, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 65, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 66, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 67, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 68, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 69, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 70, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 71, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 73, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 74, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 75, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 76, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 77, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 79, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 80, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 81, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 82, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 83, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 84, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 85, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 86, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 88, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 89, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 90, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 92, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 93, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 94, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 95, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 96, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 123, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 166, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 167, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 124, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 125, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 126, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 127, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 156, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 157, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 158, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 159, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 160, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 161, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 162, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 163, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 164, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 165, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 170, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 171, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 172, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 173, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 174, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 176, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 177, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 178, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 1, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 2, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 3, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 4, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 5, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 6, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 7, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 8, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 9, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 11, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 14, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 15, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 16, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 18, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 19, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 20, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 21, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 22, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 23, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 24, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 25, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 26, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 28, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 29, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 30, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 31, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 34, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 35, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 36, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 37, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 38, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 39, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 42, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 43, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 45, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 47, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 61, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 62, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 64, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 65, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 66, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 67, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 68, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 69, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 70, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 71, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 73, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 74, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 75, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 76, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 77, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 78, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 79, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 80, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 81, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 82, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 83, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 84, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 85, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 86, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 88, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 89, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 90, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 92, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 93, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 94, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 95, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 96, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 123, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 166, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 124, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 125, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 126, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 127, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 156, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 157, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 158, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 159, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 160, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 161, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 162, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 163, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 164, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 165, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 167, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 170, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 } + }, + { + { 254, 254, 254, 254, 254, 254, 254, 254, 187, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 1, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 2, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 3, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 4, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 5, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 6, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 7, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 8, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 9, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 11, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 14, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 15, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 16, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 18, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 19, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 20, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 21, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 22, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 23, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 24, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 25, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 26, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 28, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 29, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 30, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 31, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 34, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 35, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 36, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 37, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 38, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 39, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 42, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 43, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 45, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 47, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 61, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 62, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 64, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 65, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 66, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 67, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 68, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 69, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 70, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 71, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 72, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 73, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 74, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 75, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 76, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 77, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 79, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 80, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 81, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 82, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 83, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 84, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 85, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 86, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 88, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 89, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 90, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 92, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 93, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 94, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 95, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 96, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 123, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 124, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 125, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 126, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 156, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 157, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 158, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 159, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 160, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 161, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 162, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 163, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 164, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 165, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 166, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 167, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 170, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 171, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 172, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 173, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 127, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 174, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 176, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 177, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 178, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 179, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 180, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 } + }, + { + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 174, 254, 91, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 0, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 1, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 2, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 3, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 4, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 5, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 6, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 7, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 8, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 9, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 11, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 15, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 16, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 19, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 20, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 21, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 22, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 23, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 24, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 25, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 26, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 28, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 29, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 30, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 31, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 34, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 35, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 36, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 37, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 38, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 39, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 42, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 43, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 45, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 47, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 61, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 62, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 64, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 65, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 66, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 67, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 68, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 70, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 71, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 73, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 74, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 75, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 76, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 77, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 78, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 79, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 80, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 81, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 82, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 83, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 84, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 85, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 86, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 89, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 90, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 93, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 94, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 95, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 96, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 123, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 166, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 167, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 170, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 124, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 125, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 126, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 127, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 156, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 157, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 158, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 159, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 160, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 161, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 162, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 163, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 164, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 165, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 171, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 172, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 1, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 2, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 3, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 4, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 5, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 6, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 7, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 8, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 14, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 15, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 16, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 18, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 19, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 20, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 21, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 25, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 26, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 29, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 31, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 34, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 38, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 39, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 47, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 60, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 61, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 66, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 67, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 68, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 69, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 70, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 71, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 72, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 73, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 74, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 75, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 76, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 77, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 80, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 81, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 82, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 83, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 84, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 88, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 89, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 90, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 92, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 93, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 94, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 95, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 96, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 123, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 156, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 157, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 158, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 159, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 160, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 161, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 162, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 163, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 164, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 165, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 124, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 125, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 126, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 166, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 167, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 170, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 171, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 172, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 173, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 127, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 174, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 } + }, + { + { 137, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 155 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 154 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 166 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 167 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 170 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 171 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 172 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 173 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 174 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 176 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 177 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 178 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 179 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 88 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 1 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 0 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 2 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 7 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 16 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 17 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 24 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 25 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 26 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 36 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 37 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 43 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 61 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 62 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 64 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 65 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 66 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 67 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 68 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 180 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 69 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 70 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 77 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 78 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 79 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 83 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 92 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 94 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 93 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 124 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 181 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 125 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 96 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 123 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 126 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 95 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 182 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 183 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 184 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 127 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 185 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 186 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 11 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 14 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 3 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 15 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 12 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 5 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 9 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 4 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 18 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 20 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 19 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 21 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 22 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 23 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 29 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 30 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 31 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 28 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 35 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 34 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 38 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 39 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 45 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 47 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 42 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 60 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 6 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 71 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 72 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 73 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 74 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 76 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 75 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 80 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 81 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 82 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 87 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 86 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 8 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 84 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 89 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 187 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 90 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 91 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 191 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 85 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 156 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 157 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 158 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 159 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 160 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 161 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 162 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 163 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 164 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 165 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 }, + { 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 } + } +}; + +#endif From 1161b7cec4a2964de40573daca2ce1ef07bf642a Mon Sep 17 00:00:00 2001 From: Max von Buelow Date: Thu, 13 Feb 2020 12:31:18 +0100 Subject: [PATCH 071/117] Implemented concatenated SMS support --- Makefile.in | 17 +- at_command.c | 126 ++++---------- at_parse.c | 17 +- at_parse.h | 3 +- at_response.c | 51 ++++-- chan_dongle.c | 5 +- chan_dongle.h | 1 - char_conv.c | 217 +++++++++++------------ char_conv.h | 30 ++-- cli.c | 5 +- dc_config.c | 24 ++- dc_config.h | 7 +- etc/dongle.conf | 3 +- manager.c | 1 - pdu.c | 408 +++++++++++++++++++++++++++---------------- pdu.h | 12 +- smsdb.c | 449 ++++++++++++++++++++++++++++++++++++++++++++++++ smsdb.h | 14 ++ test/gen.c | 94 ++++++++++ test/parse.c | 39 +++-- 20 files changed, 1084 insertions(+), 439 deletions(-) create mode 100644 smsdb.c create mode 100644 smsdb.h create mode 100644 test/gen.c diff --git a/Makefile.in b/Makefile.in index c5158740..11b630a3 100644 --- a/Makefile.in +++ b/Makefile.in @@ -3,25 +3,26 @@ 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 + memmem.o ringbuffer.o cpvt.o dc_config.o pdu.o mixbuffer.o pdiscovery.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 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 + manager.c memmem.c ringbuffer.c single.c pdu.c mixbuffer.c pdiscovery.c smsdb.c -test_SOURCES = test/test1.c test/parse.c +test_SOURCES = test/test1.c test/parse.c test/gen.c 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 + mutils.h smsdb.h tools_HEADERS = tools/tty.h @@ -38,7 +39,7 @@ INSTALL = @INSTALL@ CHMOD = chmod # -DAST_MODULE=\"$(PROJM)\" -D_THREAD_SAFE -CFLAGS = @CFLAGS@ -I$(srcdir) -DAST_MODULE_SELF_SYM=__internal_chan_dongle_self \ +CFLAGS = @CFLAGS@ -I$(srcdir) -std=gnu99 -DAST_MODULE_SELF_SYM=__internal_chan_dongle_self \ @CPPFLAGS@ @DEFS@ @AC_CFLAGS@ LDFLAGS = @LDFLAGS@ SOLINK = @SOLINK@ @@ -76,12 +77,16 @@ $(PROJS): $(chan_dongles_so_OBJS) Makefile check: tests ./test/test1 ./test/parse + ./test/gen -tests: test/test1 test/parse +tests: test/test1 test/parse test/gen test/test1: $(test1_OBJS) $(LD) $(LDFLAGS) -o $@ $(test1_OBJS) $(LIBS) +test/gen: $(gen_OBJS) + $(LD) $(LDFLAGS) -o $@ $(gen_OBJS) $(LIBS) + test/parse: $(parse_OBJS) $(LD) $(LDFLAGS) -o $@ $(parse_OBJS) $(LIBS) diff --git a/at_command.c b/at_command.c index 1f767314..1eb9ebf2 100644 --- a/at_command.c +++ b/at_command.c @@ -13,6 +13,7 @@ Copyright (C) 2009 - 2010 Artem Makhutov Artem Makhutov http://www.makhutov.org + Copyright (C) 2020 Max von Buelow */ #include "ast_config.h" @@ -25,6 +26,7 @@ #include "char_conv.h" /* char_to_hexstr_7bit() */ #include "chan_dongle.h" /* struct pvt */ #include "pdu.h" /* build_pdu() */ +#include "smsdb.h" static const char cmd_at[] = "AT\r"; static const char cmd_chld1x[] = "AT+CHLD=1%d\r"; @@ -32,6 +34,12 @@ 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 @@ -202,7 +210,7 @@ EXPORT_DEF int at_enque_initialization(struct cpvt* cpvt, at_cmd_t from_command) } else if(cmds[out].cmd == CMD_AT_CMGF) { - err = at_fill_generic_cmd(&cmds[out], "AT+CMGF=%d\r", CONF_SHARED(pvt, smsaspdu) ? 0 : 1); + err = at_fill_generic_cmd(&cmds[out], "AT+CMGF=0\r"); if(err) goto failure; ptmp2 = cmds[out].data; @@ -285,113 +293,43 @@ EXPORT_DEF int at_enque_pdu(struct cpvt * cpvt, const char * pdu, attribute_unus } /*! - * \brief Enque an SMS message + * \brief Enque a partial SMS message * \param cpvt -- cpvt structure * \param number -- the destination of the message * \param msg -- utf-8 encoded message */ +static int at_enque_sms_part(const char *buf, unsigned length, void *s) +{ + (void)(length); + csms_part_data_t *dta = s; + return at_enque_pdu(dta->cpvt, buf, NULL, 0, 0, dta->id); +} EXPORT_DEF int at_enque_sms (struct cpvt* cpvt, const char* destination, const char* msg, unsigned validity_minutes, int report_req, void ** id) { ssize_t res; - char buf[1024] = "AT+CMGS=\""; - char pdu_buf[2048]; pvt_t* pvt = cpvt->pvt; - at_queue_cmd_t at_cmd[] = { - { CMD_AT_CMGS, RES_SMS_PROMPT, ATQ_CMD_FLAG_DEFAULT, { ATQ_CMD_TIMEOUT_MEDIUM, 0}, NULL, 0 }, - { CMD_AT_SMSTEXT, RES_OK, ATQ_CMD_FLAG_DEFAULT, { ATQ_CMD_TIMEOUT_LONG, 0}, NULL, 0 } - }; - - if(pvt->use_pdu) - { - /* set default validity period */ - if(validity_minutes <= 0) - validity_minutes = 3 * 24 * 60; + /* set default validity period */ + 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); */ - res = pdu_build(pdu_buf, sizeof(pdu_buf), "", destination, msg, validity_minutes, report_req); - 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)); - } - /* TODO: complain on other errors */ - return res; - } - - if(res > (int)(sizeof(pdu_buf) - 2)) - return -1; - - return at_enque_pdu(cpvt, pdu_buf, NULL, 0, 0, id); - } - else + csms_part_data_t dta = { cpvt, id }; + uint16_t csmsref = smsdb_outgoing_get(PVT_ID(pvt)); + res = pdu_build_mult(at_enque_sms_part, "", destination, msg, validity_minutes, report_req, csmsref, &dta); + if(res <= 0) { - at_cmd[0].length = 9; - - res = str_recode (RECODE_ENCODE, STR_ENCODING_UCS2_HEX, destination, strlen (destination), buf + at_cmd[0].length, sizeof(buf) - at_cmd[0].length - 3); - if(res <= 0) + if(res == -E2BIG) { - ast_log (LOG_ERROR, "[%s] Error converting SMS number to UCS-2\n", PVT_ID(pvt)); - return -4; + 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)); } - at_cmd[0].length += res; - buf[at_cmd[0].length++] = '"'; - buf[at_cmd[0].length++] = '\r'; - buf[at_cmd[0].length] = '\0'; + /* TODO: complain on other errors */ + return res; } - at_cmd[0].data = ast_strdup (buf); - if(!at_cmd[0].data) - return -ENOMEM; - - res = strlen (msg); - -// if(!pvt->use_pdu) -// { - if (pvt->use_ucs2_encoding) - { - /* NOTE: bg: i test limit of no response is 133, but for +CMS ERROR: ? */ - /* message limit in 178 octet of TPDU (w/o SCA) Headers: Type(1)+MR(1)+DA(3..12)+PID(1)+DCS(1)+VP(0,1,7)+UDL(1) = 8..24 (usually 14) */ - if(res > 70) - { - ast_log (LOG_ERROR, "[%s] SMS message too long, 70 symbols max\n", PVT_ID(pvt)); - return -4; - } - - res = str_recode (RECODE_ENCODE, STR_ENCODING_UCS2_HEX, msg, res, pdu_buf, sizeof(pdu_buf) - 2); - if (res < 0) - { - ast_free (at_cmd[0].data); - ast_log (LOG_ERROR, "[%s] Error converting SMS to UCS-2: '%s'\n", PVT_ID(pvt), msg); - return -4; - } - pdu_buf[res++] = 0x1a; - pdu_buf[res] = 0; - at_cmd[1].length = res; - } - else - { - if(res > 140) - { - ast_log (LOG_ERROR, "[%s] SMS message too long, 140 symbols max\n", PVT_ID(pvt)); - return -4; - } - - at_cmd[1].length = snprintf (pdu_buf, sizeof(pdu_buf), "%.160s\x1a", msg); - } -// } - - at_cmd[1].data = ast_strdup(pdu_buf); - if(!at_cmd[1].data) - { - ast_free(at_cmd[0].data); - return -ENOMEM; - } - - return at_queue_insert_task(cpvt, at_cmd, ITEMS_OF(at_cmd), 0, (struct at_queue_task **)id); + return res; } /*! @@ -415,12 +353,12 @@ EXPORT_DEF int at_enque_ussd (struct cpvt * cpvt, const char * code, attribute_u length = STRLEN(cmd); if (pvt->cusd_use_7bit_encoding) - cusd_encoding = STR_ENCODING_7BIT_HEX_PAD_0; + 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_7BIT; - res = str_recode(RECODE_ENCODE, cusd_encoding, code, strlen (code), buf + STRLEN(cmd), sizeof (buf) - STRLEN(cmd) - STRLEN(cmd_end) - 1); + 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); diff --git a/at_parse.c b/at_parse.c index e7d872fe..42dac006 100644 --- a/at_parse.c +++ b/at_parse.c @@ -284,8 +284,9 @@ EXPORT_DEF int at_parse_cmti (const char* str) } -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) +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) { + (void)(udh); /* * parse cmgr info in the following TEXT format: * +CMGR: "","+123456789",,timestamp @@ -313,13 +314,13 @@ static const char * parse_cmgr_text(char ** str, size_t len, char * oa, size_t o length = marks[1] - marks[0] + 1; if(oa_len < length) return "Not enought space for store number"; - *oa_enc = get_encoding(RECODE_DECODE, marks[0], length - 1); + *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(RECODE_DECODE, *msg, length); + *msg_enc = get_encoding(*msg, length); return NULL; } else if(count > 0) @@ -328,7 +329,7 @@ static const char * parse_cmgr_text(char ** str, size_t len, char * oa, size_t o return "Can't parse +CMGR response text"; } -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) +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 @@ -353,7 +354,7 @@ static const char* parse_cmgr_pdu(char** str, attribute_unused size_t len, char* 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); + return pdu_parse(str, tpdu_length, oa, oa_len, oa_enc, msg, msg_enc, udh); } return "Can't parse +CMGR response"; @@ -370,7 +371,7 @@ 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) +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) { const char* rv = "Can't parse +CMGR response line"; @@ -388,10 +389,10 @@ EXPORT_DEF const char * at_parse_cmgr(char ** str, size_t len, char * oa, size_t 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); + 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; - rv = (*fptr)(str, len, oa, oa_len, oa_enc, msg, msg_enc); + rv = (*fptr)(str, len, oa, oa_len, oa_enc, msg, msg_enc, udh); } return rv; diff --git a/at_parse.h b/at_parse.h index b2e2ed51..0134a830 100644 --- a/at_parse.h +++ b/at_parse.h @@ -8,13 +8,14 @@ #include "export.h" /* EXPORT_DECL EXPORT_DECL */ #include "char_conv.h" /* str_encoding_t */ +#include "pdu.h" struct pvt; 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); +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_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_response.c b/at_response.c index 6a47736c..4cfa386f 100644 --- a/at_response.c +++ b/at_response.c @@ -7,6 +7,8 @@ Dmitry Vagin bg + + Copyright (C) 2020 Max von Buelow */ #include "ast_config.h" @@ -23,6 +25,7 @@ #include "char_conv.h" #include "manager.h" #include "channel.h" /* channel_queue_hangup() channel_queue_control() */ +#include "smsdb.h" #define DEF_STR(str) str,STRLEN(str) @@ -175,8 +178,7 @@ static int at_response_ok (struct pvt* pvt, at_res_t res) break; case CMD_AT_CMGF: - pvt->use_pdu = CONF_SHARED(pvt, smsaspdu); - ast_debug (1, "[%s] SMS operation mode set to %s\n", PVT_ID(pvt), pvt->use_pdu ? "PDU" : "TEXT"); + ast_debug (1, "[%s] SMS operation mode set to PDU\n", PVT_ID(pvt)); break; case CMD_AT_CSCS: @@ -1203,8 +1205,10 @@ static int at_response_cmgr (struct pvt* pvt, const char * str, size_t len) char sms_utf8_str[4096]; char* number; char from_number_utf8_str[1024]; - char text_base64[16384]; + char text_base64[40800]; size_t msg_len; + pdu_udh_t udh; + pdu_udh_init(&udh); const struct at_queue_cmd * ecmd = at_queue_head_cmd (pvt); @@ -1218,7 +1222,7 @@ static int at_response_cmgr (struct pvt* pvt, const char * str, size_t len) 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); // YYY + 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]; @@ -1237,13 +1241,13 @@ static int at_response_cmgr (struct pvt* pvt, const char * str, size_t len) /* last chance to define encodings */ if (oa_enc == STR_ENCODING_UNKNOWN) - oa_enc = pvt->use_ucs2_encoding ? STR_ENCODING_UCS2_HEX : STR_ENCODING_7BIT; + 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_7BIT; + msg_enc = pvt->use_ucs2_encoding ? STR_ENCODING_UCS2_HEX : STR_ENCODING_ASCII; /* decode number and message */ - res = str_recode (RECODE_DECODE, oa_enc, oa, strlen(oa), from_number_utf8_str, sizeof (from_number_utf8_str)); + 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); @@ -1254,7 +1258,7 @@ static int at_response_cmgr (struct pvt* pvt, const char * str, size_t len) number = from_number_utf8_str; msg_len = strlen(msg); - res = str_recode (RECODE_DECODE, msg_enc, msg, msg_len, sms_utf8_str, sizeof (sms_utf8_str)); + 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); @@ -1266,15 +1270,34 @@ static int at_response_cmgr (struct pvt* pvt, const char * str, size_t len) msg_len = res; } - ast_verb (1, "[%s] Got SMS from %s: '%s'\n", PVT_ID(pvt), number, msg); - ast_base64encode (text_base64, (unsigned char*)msg, msg_len, sizeof(text_base64)); + 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; + } + if (cnt < udh.parts) { + ast_verb (1, "[%s] Waiting for following parts\n", PVT_ID(pvt)); + return 0; + } + 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)); - manager_event_new_sms(PVT_ID(pvt), number, msg); + manager_event_new_sms(PVT_ID(pvt), number, fullmsg); manager_event_new_sms_base64(PVT_ID(pvt), number, text_base64); { channel_var_t vars[] = { - { "SMS", msg } , + { "SMS", fullmsg } , { "SMS_BASE64", text_base64 }, { "CMGR", (char *)str }, { NULL, NULL }, @@ -1373,10 +1396,10 @@ static int at_response_cusd (struct pvt * pvt, char * str, size_t len) // FIXME: strictly check USSD encoding and detect encoding if ((dcs == 0 || dcs == 15) && !pvt->cusd_use_ucs2_decoding) - ussd_encoding = STR_ENCODING_7BIT_HEX_PAD_0; + ussd_encoding = STR_ENCODING_GSM7_HEX_PAD_0; else ussd_encoding = STR_ENCODING_UCS2_HEX; - res = str_recode (RECODE_DECODE, ussd_encoding, cusd, strlen (cusd), cusd_utf8_str, sizeof (cusd_utf8_str)); + res = str_decode (ussd_encoding, cusd, strlen (cusd), cusd_utf8_str, sizeof (cusd_utf8_str), 0, 0); if(res >= 0) { cusd = cusd_utf8_str; diff --git a/chan_dongle.c b/chan_dongle.c index 5e6d725a..5204eb7f 100644 --- a/chan_dongle.c +++ b/chan_dongle.c @@ -69,6 +69,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Rev: " PACKAGE_REVISION " $") #include "channel.h" /* channel_queue_hangup() */ #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" EXPORT_DEF const char * const dev_state_strs[4] = { "stop", "restart", "remove", "start" }; EXPORT_DEF public_state_t * gpublic; @@ -321,12 +322,10 @@ static void disconnect_dongle (struct pvt* pvt) pvt->has_sms = 0; pvt->has_voice = 0; pvt->has_call_waiting = 0; - pvt->use_pdu = 0; } pvt->connected = 0; pvt->initialized = 0; - pvt->use_pdu = 0; pvt->has_call_waiting = 0; /* FIXME: LOST real device state */ @@ -1493,7 +1492,6 @@ static int pvt_reconfigure(struct pvt * pvt, const pvt_config_t * settings, rest || strcmp(UCONFIG(settings, imsi), CONF_UNIQ(pvt, imsi)) || SCONFIG(settings, u2diag) != CONF_SHARED(pvt, u2diag) || SCONFIG(settings, resetdongle) != CONF_SHARED(pvt, resetdongle) - || SCONFIG(settings, smsaspdu) != CONF_SHARED(pvt, smsaspdu) || SCONFIG(settings, callwaiting) != CONF_SHARED(pvt, callwaiting)) { /* TODO: schedule restart */ @@ -1696,6 +1694,7 @@ static int public_state_init(struct public_state * state) /* register our channel type */ if(ast_channel_register(&channel_tech) == 0) { + smsdb_init(); cli_register(); app_register(); diff --git a/chan_dongle.h b/chan_dongle.h index 2c71663d..7d9f1c97 100644 --- a/chan_dongle.h +++ b/chan_dongle.h @@ -175,7 +175,6 @@ typedef struct pvt #define VOLUME_SYNC_BEGIN 0 #define VOLUME_SYNC_DONE 3 - unsigned int use_pdu:1; /*!< PDU SMS mode in force */ unsigned int has_sms:1; /*!< device has SMS support */ unsigned int has_voice:1; /*!< device has voice call support */ unsigned int has_call_waiting:1; /*!< call waiting enabled on device */ diff --git a/char_conv.c b/char_conv.c index 8c76e995..33342030 100644 --- a/char_conv.c +++ b/char_conv.c @@ -7,6 +7,8 @@ Copyright (C) 2010 - 2011 bg + + Copyright (C) 2020 Max von Buelow */ #include "ast_config.h" @@ -19,8 +21,9 @@ #include "char_conv.h" #include "mutils.h" /* ITEMS_OF() */ +#include "gsm7_luts.h" -static ssize_t convert_string (const char* in, size_t in_length, char* out, size_t out_size, char* from, char* to) +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; @@ -114,6 +117,10 @@ static ssize_t chars8bit_to_hexstr (const char* in, size_t in_length, char* out, return out_size; } +static ssize_t chars16bit_to_hexstr (const uint16_t* in, size_t in_length, char* out, size_t out_size) +{ + return chars8bit_to_hexstr((const char*)in, in_length * 2, out, out_size); +} static ssize_t hexstr_ucs2_to_utf8 (const char* in, size_t in_length, char* out, size_t out_size) { @@ -203,52 +210,57 @@ static ssize_t char_to_hexstr_7bit_padded(const char* in, size_t in_length, char /* return total string length, excluding terminating zero */ return x; } - -static ssize_t char_to_hexstr_7bit_pad_0(const char* in, size_t in_length, char* out, - size_t out_size) -{ - return char_to_hexstr_7bit_padded(in, in_length, out, out_size, 0); -} - -static ssize_t char_to_hexstr_7bit_pad_1(const char* in, size_t in_length, char* out, - size_t out_size) -{ - return char_to_hexstr_7bit_padded(in, in_length, out, out_size, 1); -} - -static ssize_t char_to_hexstr_7bit_pad_2(const char* in, size_t in_length, char* out, - size_t out_size) +static ssize_t chars16bit_to_hexstr_7bit(const uint16_t* in, size_t in_length, char* out, + size_t out_size, unsigned out_padding) { - return char_to_hexstr_7bit_padded(in, in_length, out, out_size, 2); -} + size_t i; + size_t x; + char buf[4]; + unsigned value = 0; -static ssize_t char_to_hexstr_7bit_pad_3(const char* in, size_t in_length, char* out, - size_t out_size) -{ - return char_to_hexstr_7bit_padded(in, in_length, out, out_size, 3); -} + /* 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 */; -static ssize_t char_to_hexstr_7bit_pad_4(const char* in, size_t in_length, char* out, - size_t out_size) -{ - return char_to_hexstr_7bit_padded(in, in_length, out, out_size, 4); -} + /* check that the buffer is not too small */ + if (x > out_size) + return -1; -static ssize_t char_to_hexstr_7bit_pad_5(const char* in, size_t in_length, char* out, - size_t out_size) -{ - return char_to_hexstr_7bit_padded(in, in_length, out, out_size, 5); -} + 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; + value >>= 8; + out_padding -= 8; + } + } + if (out_padding != 0) { + snprintf (buf, sizeof(buf), "%02X", value & 0xFF); + memcpy (out + x, buf, 2); + x = x + 2; + } + /* zero terminate final string */ + out[x] = '\0'; -static ssize_t char_to_hexstr_7bit_pad_6(const char* in, size_t in_length, char* out, - size_t out_size) -{ - return char_to_hexstr_7bit_padded(in, in_length, out, out_size, 6); + /* return total string length, excluding terminating zero */ + return x; } static ssize_t hexstr_7bit_to_char_padded(const char* in, size_t in_length, char* out, - size_t out_size, unsigned in_padding) + 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]; @@ -282,7 +294,6 @@ static ssize_t hexstr_7bit_to_char_padded(const char* in, size_t in_length, char /* parse the hexstring */ int esc = 0; - int ss = 0, ls = 0; for (x = i = 0; i != in_length; i++) { if (x >= out_size) return -1; @@ -300,6 +311,7 @@ static ssize_t hexstr_7bit_to_char_padded(const char* in, size_t in_length, char if (val[0] == '\x1b' && !val[1]) { esc = 1; } else { + esc = 0; do { out[x++] = *val++; } while (*val && x < out_size); @@ -315,48 +327,6 @@ static ssize_t hexstr_7bit_to_char_padded(const char* in, size_t in_length, char return x; } -static ssize_t hexstr_7bit_to_char_pad_0(const char* in, size_t in_length, char* out, - size_t out_size) -{ - return hexstr_7bit_to_char_padded(in, in_length, out, out_size, 0); -} - -static ssize_t hexstr_7bit_to_char_pad_1(const char* in, size_t in_length, char* out, - size_t out_size) -{ - return hexstr_7bit_to_char_padded(in, in_length, out, out_size, 1); -} - -static ssize_t hexstr_7bit_to_char_pad_2(const char* in, size_t in_length, char* out, - size_t out_size) -{ - return hexstr_7bit_to_char_padded(in, in_length, out, out_size, 2); -} - -static ssize_t hexstr_7bit_to_char_pad_3(const char* in, size_t in_length, char* out, - size_t out_size) -{ - return hexstr_7bit_to_char_padded(in, in_length, out, out_size, 3); -} - -static ssize_t hexstr_7bit_to_char_pad_4(const char* in, size_t in_length, char* out, - size_t out_size) -{ - return hexstr_7bit_to_char_padded(in, in_length, out, out_size, 4); -} - -static ssize_t hexstr_7bit_to_char_pad_5(const char* in, size_t in_length, char* out, - size_t out_size) -{ - return hexstr_7bit_to_char_padded(in, in_length, out, out_size, 5); -} - -static ssize_t hexstr_7bit_to_char_pad_6(const char* in, size_t in_length, char* out, - size_t out_size) -{ - return hexstr_7bit_to_char_padded(in, in_length, out, out_size, 6); -} - #/* */ ssize_t just_copy (const char* in, size_t in_length, char* out, size_t out_size) { @@ -370,53 +340,68 @@ ssize_t just_copy (const char* in, size_t in_length, char* out, size_t out_size) return -ENOMEM; } -typedef ssize_t (*coder) (const char* in, size_t in_length, char* out, size_t out_size); - -/* array in order of values RECODE_* */ -static const coder recoders[STR_ENCODING_UNKNOWN][2] = +#/* */ +EXPORT_DEF ssize_t str_encode(str_encoding_t encoding, const char* in, size_t in_length, char* out, size_t out_size) { - [STR_ENCODING_7BIT_HEX_PAD_0] = { hexstr_7bit_to_char_pad_0, char_to_hexstr_7bit_pad_0 }, - [STR_ENCODING_8BIT_HEX] = { hexstr_to_8bitchars, chars8bit_to_hexstr }, - [STR_ENCODING_UCS2_HEX] = { hexstr_ucs2_to_utf8, utf8_to_hexstr_ucs2 }, - [STR_ENCODING_7BIT] = { just_copy, just_copy }, - [STR_ENCODING_7BIT_HEX_PAD_1] = { hexstr_7bit_to_char_pad_1, char_to_hexstr_7bit_pad_1 }, - [STR_ENCODING_7BIT_HEX_PAD_2] = { hexstr_7bit_to_char_pad_2, char_to_hexstr_7bit_pad_2 }, - [STR_ENCODING_7BIT_HEX_PAD_3] = { hexstr_7bit_to_char_pad_3, char_to_hexstr_7bit_pad_3 }, - [STR_ENCODING_7BIT_HEX_PAD_4] = { hexstr_7bit_to_char_pad_4, char_to_hexstr_7bit_pad_4 }, - [STR_ENCODING_7BIT_HEX_PAD_5] = { hexstr_7bit_to_char_pad_5, char_to_hexstr_7bit_pad_5 }, - [STR_ENCODING_7BIT_HEX_PAD_6] = { hexstr_7bit_to_char_pad_6, char_to_hexstr_7bit_pad_6 }, -}; + 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_recode(recode_direction_t dir, str_encoding_t encoding, const char* in, size_t in_length, char* out, size_t out_size) +EXPORT_DEF ssize_t str_encode16(str_encoding_t encoding, const uint16_t* in, size_t in_length, char* out, size_t out_size) { - unsigned idx = encoding; - if((dir == RECODE_DECODE || dir == RECODE_ENCODE) && idx < ITEMS_OF(recoders)) - return (recoders[idx][dir])(in, in_length, out, 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 str_encoding_t get_encoding(recode_direction_t hint, const char* in, size_t length) +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(hint == RECODE_ENCODE) - { - for(; length; --length, ++in) - if(*in & 0x80) - return STR_ENCODING_UCS2_HEX; - return STR_ENCODING_7BIT_HEX_PAD_0; + 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); } - else + 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) { - size_t x; - for(x = 0; x < length; ++x) - { - if(parse_hexdigit(in[x]) < 0) { - return STR_ENCODING_7BIT; - } + if(parse_hexdigit(in[x]) < 0) { + return STR_ENCODING_ASCII; } - // TODO: STR_ENCODING_7BIT_HEX_PAD_X or STR_ENCODING_8BIT_HEX or STR_ENCODING_UCS2_HEX } + // 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 592f507a..e128e647 100644 --- a/char_conv.h +++ b/char_conv.h @@ -6,36 +6,38 @@ #include /* ssize_t size_t */ #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_7BIT_HEX_PAD_0 = 0, /* 7bit encoding, 0 bits of padding */ + 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_7BIT, /* 7bit ASCII no need recode to utf-8 */ - STR_ENCODING_7BIT_HEX_PAD_1, /* 7bit encoding, 1 bit of padding */ - STR_ENCODING_7BIT_HEX_PAD_2, /* 7bit encoding, 2 bits of padding */ - STR_ENCODING_7BIT_HEX_PAD_3, /* 7bit encoding, 3 bits padding */ - STR_ENCODING_7BIT_HEX_PAD_4, /* 7bit encoding, 4 bits padding */ - STR_ENCODING_7BIT_HEX_PAD_5, /* 7bit encoding, 5 bits padding */ - STR_ENCODING_7BIT_HEX_PAD_6, /* 7bit encoding, 6 bits padding */ + 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; -typedef enum { - RECODE_DECODE = 0, /* from encoded to UTF-8 */ - RECODE_ENCODE /* from UTF-8 to encoded */ -} recode_direction_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_recode(recode_direction_t dir, str_encoding_t encoding, const char* in, size_t in_length, char* out, size_t out_size); +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(recode_direction_t hint, const char * in, size_t in_length); +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); #endif /* CHAN_DONGLE_CHAR_CONV_H_INCLUDED */ diff --git a/cli.c b/cli.c index 4f95c0bd..02f4618a 100644 --- a/cli.c +++ b/cli.c @@ -144,7 +144,6 @@ static char* cli_show_device_settings (struct ast_cli_entry* e, int cmd, struct ast_cli (a->fd, " Auto delete SMS : %s\n", CONF_SHARED(pvt, autodeletesms) ? "Yes" : "No"); ast_cli (a->fd, " Disable SMS : %s\n", CONF_SHARED(pvt, disablesms) ? "Yes" : "No"); ast_cli (a->fd, " Reset Dongle : %s\n", CONF_SHARED(pvt, resetdongle) ? "Yes" : "No"); - ast_cli (a->fd, " SMS PDU : %s\n", CONF_SHARED(pvt, smsaspdu) ? "Yes" : "No"); ast_cli (a->fd, " Call Waiting : %s\n", dc_cw_setting2str(CONF_SHARED(pvt, callwaiting))); ast_cli (a->fd, " DTMF : %s\n", dc_dtmf_setting2str(CONF_SHARED(pvt, dtmf))); ast_cli (a->fd, " Minimal DTMF Gap : %d\n", CONF_SHARED(pvt, mindtmfgap)); @@ -489,7 +488,7 @@ static char* cli_sms (struct ast_cli_entry* e, int cmd, struct ast_cli_args* a) return CLI_SHOWUSAGE; } - buf = ast_str_create (256); + buf = ast_str_create (160 * 255); for (i = 4; i < a->argc; i++) { if (i < (a->argc - 1)) @@ -502,7 +501,7 @@ 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, 0, &status, &msgid); + msg = send_sms(a->argv[2], a->argv[3], ast_str_buffer(buf), 0, "1", &status, &msgid); ast_free (buf); if(status) diff --git a/dc_config.c b/dc_config.c index f25331f1..b1482f72 100644 --- a/dc_config.c +++ b/dc_config.c @@ -167,10 +167,6 @@ EXPORT_DEF void dc_sconfig_fill(struct ast_config * cfg, const char * cat, struc { config->disablesms = ast_true (v->value); /* disablesms is set to 0 if invalid */ } - else if (!strcasecmp (v->name, "smsaspdu")) - { - config->smsaspdu = ast_true (v->value); /* send_sms_as_pdu us set to 0 if invalid */ - } else if (!strcasecmp (v->name, "disable")) { config->initstate = ast_true (v->value) ? DEV_STATE_REMOVED : DEV_STATE_STARTED; @@ -234,11 +230,13 @@ EXPORT_DEF void dc_gconfig_fill(struct ast_config * cfg, const char * cat, struc { struct ast_variable * v; int tmp; - const char * stmp; + const char * stmp, *smsdb, *csmsttl; /* set default values */ memcpy(&config->jbconf, &jbconf_default, sizeof(config->jbconf)); config->discovery_interval = DEFAULT_DISCOVERY_INT; + ast_copy_string (config->sms_db, DEFAULT_SMS_DB, sizeof(DEFAULT_SMS_DB)); + config->csms_ttl = DEFAULT_CSMS_TTL; stmp = ast_variable_retrieve (cfg, cat, "interval"); if(stmp) @@ -251,6 +249,22 @@ EXPORT_DEF void dc_gconfig_fill(struct ast_config * cfg, const char * cat, struc config->discovery_interval = tmp; } + smsdb = ast_variable_retrieve (cfg, cat, "smsdb"); + if(smsdb) + { + ast_copy_string (config->sms_db, smsdb, sizeof (config->sms_db)); + } + + csmsttl = ast_variable_retrieve (cfg, cat, "csmsttl"); + if(csmsttl) + { + errno = 0; + tmp = (int) strtol (csmsttl, (char**) NULL, 10); + if (tmp == 0 && errno == EINVAL) + ast_log (LOG_NOTICE, "Error parsing 'csmsttl' in general section, using default value %d\n", config->csms_ttl); + else + config->csms_ttl = tmp; + } for (v = ast_variable_browse (cfg, cat); v; v = v->next) /* handle jb conf */ diff --git a/dc_config.h b/dc_config.h index 02858586..278d492b 100644 --- a/dc_config.h +++ b/dc_config.h @@ -15,6 +15,7 @@ #define DEVNAMELEN 31 #define IMEI_SIZE 15 #define IMSI_SIZE 15 +#define PATHLEN 256 #define DEVPATHLEN 256 typedef enum { @@ -76,7 +77,6 @@ typedef struct dc_sconfig unsigned int autodeletesms:1; /*! 0 */ unsigned int resetdongle:1; /*! 1 */ unsigned int disablesms:1; /*! 0 */ - unsigned int smsaspdu:1; /*! 0 */ dev_state_t initstate; /*! DEV_STATE_STARTED */ // unsigned int disable:1; /*! 0 */ @@ -99,6 +99,11 @@ typedef struct dc_gconfig struct ast_jb_conf jbconf; /*!< jitter buffer settings, disabled by default */ int discovery_interval; /*!< The device discovery interval */ #define DEFAULT_DISCOVERY_INT 60 + char sms_db[PATHLEN]; +#define DEFAULT_SMS_DB "/var/lib/asterisk/smsdb" + int csms_ttl; +#define DEFAULT_CSMS_TTL 600 + } dc_gconfig_t; /* Local required (unique) settings */ diff --git a/etc/dongle.conf b/etc/dongle.conf index aa486cfe..f834153d 100644 --- a/etc/dongle.conf +++ b/etc/dongle.conf @@ -1,6 +1,8 @@ [general] interval=15 ; Number of seconds between trying to connect to devices +smsdb=/var/lib/asterisk/smsdb +csmsttl=600 ;------------------------------ JITTER BUFFER CONFIGURATION -------------------------- ;jbenable = yes ; Enables the use of a jitterbuffer on the receiving side of a @@ -54,7 +56,6 @@ disablesms=no ; disable of SMS reading from device when received ; default = no language=en ; set channel default language -smsaspdu=yes ; if 'yes' send SMS in PDU mode, feature implementation incomplete and we strongly recommend say 'yes' mindtmfgap=45 ; minimal interval from end of previews DTMF from begining of next in ms mindtmfduration=80 ; minimal DTMF tone duration in ms mindtmfinterval=200 ; minimal interval between ends of DTMF of same digits in ms diff --git a/manager.c b/manager.c index df747d9a..de1de269 100644 --- a/manager.c +++ b/manager.c @@ -63,7 +63,6 @@ static int manager_show_devices (struct mansession* s, const struct message* m) astman_append (s, "AutoDeleteSMS: %s\r\n", CONF_SHARED(pvt, autodeletesms) ? "Yes" : "No"); astman_append (s, "DisableSMS: %s\r\n", CONF_SHARED(pvt, disablesms) ? "Yes" : "No"); astman_append (s, "ResetDongle: %s\r\n", CONF_SHARED(pvt, resetdongle) ? "Yes" : "No"); - astman_append (s, "SMSPDU: %s\r\n", CONF_SHARED(pvt, smsaspdu) ? "Yes" : "No"); astman_append (s, "CallWaitingSetting: %s\r\n", dc_cw_setting2str(CONF_SHARED(pvt, callwaiting))); astman_append (s, "DTMF: %s\r\n", dc_dtmf_setting2str(CONF_SHARED(pvt, dtmf))); astman_append (s, "MinimalDTMFGap: %d\r\n", CONF_SHARED(pvt, mindtmfgap)); diff --git a/pdu.c b/pdu.c index 1be99493..bfd98bfd 100644 --- a/pdu.c +++ b/pdu.c @@ -1,5 +1,6 @@ /* Copyright (C) 2010 bg + Copyright (C) 2020 Max von Buelow */ #include "ast_config.h" @@ -8,6 +9,7 @@ #include "pdu.h" #include "helpers.h" /* dial_digit_code() */ #include "char_conv.h" /* utf8_to_hexstr_ucs2() */ +#include "gsm7_luts.h" /* SMS-SUBMIT format SCA 1..12 octet(s) Service Center Address information element @@ -267,44 +269,57 @@ #define PDU_DCS_ALPHABET(dcs) ((dcs) & PDU_DCS_ALPHABET_MASK) #define ROUND_UP2(x) (((x) + 1) & (0xFFFFFFFF << 1)) -#define LENGTH2OCTETS(x) (((x) + 1)/2) +#define DIV2UP(x) (((x) + 1)/2) + +#define CSMS_GSM7_MAX_LEN 153 +#define SMS_GSM7_MAX_LEN 160 +#define CSMS_UCS2_MAX_LEN 66 +#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->ss = 0; + udh->ls = 0; +} #/* get digit code, 0 if invalid */ EXPORT_DEF char pdu_digit2code(char digit) { - switch(digit) - { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - break; - case '*': - digit = 'A'; - break; - case '#': - digit = 'B'; - break; - case 'a': - case 'A': - digit = 'C'; - break; - case 'b': - case 'B': - digit = 'D'; - break; - case 'c': - case 'C': - digit = 'E'; - break; - default: - return 0; + switch(digit) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + break; + case '*': + digit = 'A'; + break; + case '#': + digit = 'B'; + break; + case 'a': + case 'A': + digit = 'C'; + break; + case 'b': + case 'B': + digit = 'D'; + break; + case 'c': + case 'C': + digit = 'E'; + break; + default: + return 0; } return digit; } @@ -312,45 +327,44 @@ EXPORT_DEF char pdu_digit2code(char digit) #/* */ static char pdu_code2digit(char 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; - default: - return -1; + 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; + default: + return -1; } return code; } @@ -525,55 +539,129 @@ static int pdu_parse_timestamp(char ** pdu, size_t * length) return -EINVAL; } -#/* TODO: remove / TODO: append 8 bit */ -static int check_encoding(const char* msg, unsigned length) +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) { - str_encoding_t possible_enc = get_encoding(RECODE_ENCODE, msg, length); - if(possible_enc == STR_ENCODING_7BIT_HEX_PAD_0) - return PDU_DCS_ALPHABET_7BIT; - return PDU_DCS_ALPHABET_UCS2; + 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'; + + unsigned split = 0; + unsigned cnt = 0, cur = 1, off = 0; + int res; + if (is_gsm7) { + if (gsm7_len > SMS_GSM7_MAX_LEN) { + split = CSMS_GSM7_MAX_LEN; + } else { + split = SMS_GSM7_MAX_LEN; + } + while (off < utf16_len) { + unsigned septets = 0, n; + for (n = 0; off + n < utf16_len; ++n) { + unsigned req = msg_gsm7[off + n] > 255 ? 2 : 1; + if (septets + req >= split) break; + septets += req; + } + ++cnt; + off += n; + } + if (cnt > 255) { + cnt = -E2BIG; + goto CLEAN; + } + off = 0; + while (off < utf16_len) { + unsigned septets = 0, n; + for (n = 0; off + n < utf16_len; ++n) { + unsigned req = msg_gsm7[off + n] > 255 ? 2 : 1; + if (septets + req >= split) break; + septets += req; + } + pdu_udh_t udh; + udh.ref = csmsref; + udh.order = cur++; + 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; + } + off += n; + } + } else { + if (utf16_len > SMS_UCS2_MAX_LEN) { + split = CSMS_UCS2_MAX_LEN; + } else { + split = SMS_UCS2_MAX_LEN; + } + cnt = (utf16_len + split - 1) / split; + if (cnt > 255) { + cnt = -E2BIG; + goto CLEAN; + } + while (off < utf16_len) { + unsigned r = utf16_len - off; + unsigned n = r < split ? r : split; + pdu_udh_t udh; + udh.ref = csmsref; + udh.order = cur++; + 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; + } + off += n; + } + } + +CLEAN: + return 0; } -/*! - * \brief Build PDU text for SMS - * \param buffer -- pointer to place where PDU will be stored - * \param length -- length of buffer - * \param sca -- number of SMS center may be with leading '+' in International format - * \param dst -- destination number for SMS may be with leading '+' in International format - * \param msg -- SMS message in utf-8 - * \param valid_minutes -- Validity period - * \param srr -- Status Report Request - * \param sca_len -- pointer where length of SCA header (in bytes) will be stored - * \return number of bytes written to buffer w/o trailing 0x1A or 0, -ENOMEM if buffer too short, -EINVAL on iconv recode errors, -E2BIG if message too long - */ -#/* */ -EXPORT_DEF int pdu_build(char* buffer, size_t length, const char* sca, const char* dst, const char* msg, unsigned valid_minutes, int srr) + +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) { - char tmp; int len = 0; int data_len; int sca_toa = NUMBER_TYPE_INTERNATIONAL; int dst_toa = NUMBER_TYPE_INTERNATIONAL; 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 dcs; + int use_udh = udh->parts > 1; + if (use_udh) pdutype |= PDUTYPE_UDHI_HAS_HEADER; unsigned dst_len; unsigned sca_len; - unsigned msg_len; - - /* detect msg encoding and use 7Bit or UCS-2, not use 8Bit */ - msg_len = strlen(msg); - dcs = check_encoding(msg, msg_len); - /* cannot exceed 140 octets for not compressed or cannot exceed 160 septets for compressed */ -#if 0 - if (((PDU_DCS_ALPHABET(dcs) == PDU_DCS_ALPHABET_UCS2) && msg_len > 70) || - (PDU_DCS_ALPHABET(dcs) == PDU_DCS_ALPHABET_8BIT && msg_len > 140) || - msg_len > 160) { - return -E2BIG; - } -#endif if(sca[0] == '+') sca++; @@ -584,16 +672,12 @@ EXPORT_DEF int pdu_build(char* buffer, size_t length, const char* sca, const cha sca_len = strlen(sca); dst_len = strlen(dst); - /* check buffer has enougth space */ - if(length < ((sca_len == 0 ? 2 : 4 + ROUND_UP2(sca_len)) + 8 + ROUND_UP2(dst_len) + 8 + msg_len * 4 + 4)) - return -ENOMEM; - /* SCA Length */ /* Type-of-address of the SMSC */ /* Address of SMSC */ if(sca_len) { - len += snprintf(buffer + len, length - len, "%02X%02X", 1 + LENGTH2OCTETS(sca_len), sca_toa); + len += snprintf(buffer + len, length - len, "%02X%02X", 1 + DIV2UP(sca_len), sca_toa); len += pdu_store_number(buffer + len, sca, sca_len); } else @@ -615,11 +699,21 @@ EXPORT_DEF int pdu_build(char* buffer, size_t length, const char* sca, const cha /* Destination address */ len += pdu_store_number(buffer + len, dst, dst_len); - /* forward TP-User-Data */ - data_len = str_recode( - RECODE_ENCODE, - (dcs == PDU_DCS_ALPHABET_UCS2 ? STR_ENCODING_UCS2_HEX : STR_ENCODING_7BIT_HEX_PAD_0), - msg, msg_len, buffer + len + 8, length - len - 11); + /* 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 + + /* encode UDH */ + 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 + } + + /* 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; @@ -629,22 +723,11 @@ EXPORT_DEF int pdu_build(char* buffer, size_t length, const char* sca, const cha return -E2BIG; } - /* calc UDL */ - if(dcs == PDU_DCS_ALPHABET_UCS2) - msg_len = data_len / 2; - - /* TP-PID. Protocol identifier */ - /* TP-DCS. Data coding scheme */ - /* TP-Validity-Period */ - /* TP-User-Data-Length */ - tmp = buffer[len + 8]; - len += snprintf(buffer + len, length - len, "%02X%02X%02X%02X", PDU_PID_SMS, dcs, pdu_relative_validity(valid_minutes), msg_len); - buffer[len] = tmp; - len += data_len; + buffer[len] = '\0'; - /* also check message limit in 178 octets of TPDU (w/o SCA) */ - if(len - sca_len > 178 * 2) + /* 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; } @@ -662,7 +745,7 @@ static str_encoding_t pdu_dcs_alphabet2encoding(int alphabet) switch(alphabet) { case (PDU_DCS_ALPHABET_7BIT >> PDU_DCS_ALPHABET_SHIFT): - rv = STR_ENCODING_7BIT_HEX_PAD_0; + rv = STR_ENCODING_GSM7_HEX_PAD_0; break; case (PDU_DCS_ALPHABET_8BIT >> PDU_DCS_ALPHABET_SHIFT): rv = STR_ENCODING_8BIT_HEX; @@ -682,13 +765,14 @@ static str_encoding_t pdu_dcs_alphabet2encoding(int alphabet) * \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) +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) { 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); @@ -752,9 +836,9 @@ EXPORT_DEF const char * pdu_parse(char ** pdu, size_t tpdu_length, char * oa, si } pid = pdu_parse_byte(pdu, &pdu_length); - *oa_enc = STR_ENCODING_7BIT; + *oa_enc = STR_ENCODING_ASCII; if ((oa_toa & TP_A_TON) == TP_A_TON_ALPHANUMERIC) { - *oa_enc = STR_ENCODING_7BIT_HEX_PAD_0; + *oa_enc = STR_ENCODING_GSM7_HEX_PAD_0; } if (pid < 0) { @@ -877,25 +961,25 @@ EXPORT_DEF const char * pdu_parse(char ** pdu, size_t tpdu_length, char * oa, si } /* adjust 7-bit padding */ - if (*msg_enc == STR_ENCODING_7BIT_HEX_PAD_0) { + if (*msg_enc == STR_ENCODING_GSM7_HEX_PAD_0) { switch (6 - (udhl % 7)) { case 1: - *msg_enc = STR_ENCODING_7BIT_HEX_PAD_1; + *msg_enc = STR_ENCODING_GSM7_HEX_PAD_1; break; case 2: - *msg_enc = STR_ENCODING_7BIT_HEX_PAD_2; + *msg_enc = STR_ENCODING_GSM7_HEX_PAD_2; break; case 3: - *msg_enc = STR_ENCODING_7BIT_HEX_PAD_3; + *msg_enc = STR_ENCODING_GSM7_HEX_PAD_3; break; case 4: - *msg_enc = STR_ENCODING_7BIT_HEX_PAD_4; + *msg_enc = STR_ENCODING_GSM7_HEX_PAD_4; break; case 5: - *msg_enc = STR_ENCODING_7BIT_HEX_PAD_5; + *msg_enc = STR_ENCODING_GSM7_HEX_PAD_5; break; case 6: - *msg_enc = STR_ENCODING_7BIT_HEX_PAD_6; + *msg_enc = STR_ENCODING_GSM7_HEX_PAD_6; break; default: /* no change */ @@ -909,11 +993,10 @@ EXPORT_DEF const char * pdu_parse(char ** pdu, size_t tpdu_length, char * oa, si } while (udhl >= 2) { - int iei_len; + int iei_type, iei_len; /* get type byte */ - (void)pdu_parse_byte(pdu, &pdu_length); /* iei_type */ - + iei_type = pdu_parse_byte(pdu, &pdu_length); /* get length byte */ iei_len = pdu_parse_byte(pdu, &pdu_length); @@ -923,10 +1006,35 @@ EXPORT_DEF const char * pdu_parse(char ** pdu, size_t tpdu_length, char * oa, si /* skip data, if any */ if (iei_len >= 0 && iei_len <= udhl) { - /* skip rest of IEI */ - *pdu += iei_len * 2; - pdu_length -= iei_len * 2; - udhl -= iei_len; + 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; + } } else { diff --git a/pdu.h b/pdu.h index 01d6d4e8..81cdbe39 100644 --- a/pdu.h +++ b/pdu.h @@ -8,9 +8,17 @@ #include "export.h" /* EXPORT_DECL EXPORT_DEF */ #include "char_conv.h" /* str_encoding_t */ +typedef struct pdu_udh { + uint16_t ref; + uint8_t parts, order; + uint8_t ls, ss; +} pdu_udh_t; + +EXPORT_DECL void pdu_udh_init(pdu_udh_t *udh); EXPORT_DECL char pdu_digit2code(char digit); -EXPORT_DECL int pdu_build(char * buffer, size_t length, const char * csca, const char * dst, const char * msg, unsigned valid_minutes, int srr); -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); +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); #endif /* CHAN_DONGLE_PDU_H_INCLUDED */ diff --git a/smsdb.c b/smsdb.c new file mode 100644 index 00000000..8b4afebb --- /dev/null +++ b/smsdb.c @@ -0,0 +1,449 @@ +/* + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * + * \brief SMSdb + * + * \author Max von Buelow + * \author Mark Spencer + * + * The original code is from the astdb prat of the Asterisk project. + */ + +#include "asterisk.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "asterisk/app.h" +#include "asterisk/utils.h" + +#include "smsdb.h" +#include "chan_dongle.h" + +#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(clear_messages_stmt, "DELETE FROM incoming WHERE key = ?") +DEFINE_SQL_STATEMENT(purge_messages_stmt, "DELETE FROM incoming WHERE arrival < 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_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 = ?"); + +static int init_stmt(sqlite3_stmt **stmt, const char *sql, size_t len) +{ + ast_mutex_lock(&dblock); + if (sqlite3_prepare(smsdb, sql, len, stmt, NULL) != SQLITE_OK) { + ast_log(LOG_WARNING, "Couldn't prepare statement '%s': %s\n", sql, sqlite3_errmsg(smsdb)); + ast_mutex_unlock(&dblock); + return -1; + } + ast_mutex_unlock(&dblock); + + return 0; +} + +/*! \internal + * \brief Clean up the prepared SQLite3 statement + * \note dblock should already be locked prior to calling this method + */ +static int clean_stmt(sqlite3_stmt **stmt, const char *sql) +{ + if (sqlite3_finalize(*stmt) != SQLITE_OK) { + ast_log(LOG_WARNING, "Couldn't finalize statement '%s': %s\n", sql, sqlite3_errmsg(smsdb)); + *stmt = NULL; + return -1; + } + *stmt = NULL; + return 0; +} + +/*! \internal + * \brief Clean up all prepared SQLite3 statements + * \note dblock should already be locked prior to calling this method + */ +static void clean_statements(void) +{ + clean_stmt(&get_full_message_stmt, get_full_message_stmt_sql); + clean_stmt(&put_message_stmt, put_message_stmt_sql); + clean_stmt(&clear_messages_stmt, clear_messages_stmt_sql); + clean_stmt(&purge_messages_stmt, purge_messages_stmt_sql); + 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); +} + +static int init_statements(void) +{ + /* Don't initialize create_smsdb_statement here as the smsdb table needs to exist + * brefore these statements can be initialized */ + return init_stmt(&get_full_message_stmt, get_full_message_stmt_sql, sizeof(get_full_message_stmt_sql)) + || init_stmt(&put_message_stmt, put_message_stmt_sql, sizeof(put_message_stmt_sql)) + || 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)); +} + +static int db_create_smsdb(void) +{ + int res = 0; + + if (!create_incoming_stmt) { + init_stmt(&create_incoming_stmt, create_incoming_stmt_sql, sizeof(create_incoming_stmt_sql)); + } + ast_mutex_lock(&dblock); + if (sqlite3_step(create_incoming_stmt) != SQLITE_DONE) { + ast_log(LOG_WARNING, "Couldn't create smsdb table: %s\n", sqlite3_errmsg(smsdb)); + res = -1; + } + sqlite3_reset(create_incoming_stmt); + ast_mutex_unlock(&dblock); + + if (!create_index_stmt) { + init_stmt(&create_index_stmt, create_index_stmt_sql, sizeof(create_index_stmt_sql)); + } + ast_mutex_lock(&dblock); + if (sqlite3_step(create_index_stmt) != SQLITE_DONE) { + ast_log(LOG_WARNING, "Couldn't create smsdb index: %s\n", sqlite3_errmsg(smsdb)); + res = -1; + } + 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)); + } + ast_mutex_lock(&dblock); + if (sqlite3_step(create_outgoing_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); + + db_sync(); + ast_mutex_unlock(&dblock); + + return res; +} + +static int db_open(void) +{ + char *dbname; + if (!(dbname = ast_alloca(strlen(CONF_GLOBAL(sms_db)) + sizeof(".sqlite3")))) { + return -1; + } + strcpy(dbname, CONF_GLOBAL(sms_db)); + strcat(dbname, ".sqlite3"); + + ast_mutex_lock(&dblock); + if (sqlite3_open(dbname, &smsdb) != SQLITE_OK) { + ast_log(LOG_WARNING, "Unable to open Asterisk database '%s': %s\n", dbname, sqlite3_errmsg(smsdb)); + sqlite3_close(smsdb); + ast_mutex_unlock(&dblock); + return -1; + } + + ast_mutex_unlock(&dblock); + + return 0; +} + +static int db_init() +{ + if (smsdb) { + return 0; + } + + if (db_open() || db_create_smsdb() || init_statements()) { + return -1; + } + + return 0; +} + +/* We purposely don't lock around the sqlite3 call because the transaction + * calls will be called with the database lock held. For any other use, make + * sure to take the dblock yourself. */ +static int db_execute_sql(const char *sql, int (*callback)(void *, int, char **, char **), void *arg) +{ + char *errmsg = NULL; + int res =0; + + if (sqlite3_exec(smsdb, sql, callback, arg, &errmsg) != SQLITE_OK) { + ast_log(LOG_WARNING, "Error executing SQL (%s): %s\n", sql, errmsg); + sqlite3_free(errmsg); + res = -1; + } + + return res; +} + +static int smsdb_begin_transaction(void) +{ + return db_execute_sql("BEGIN TRANSACTION", NULL, NULL); +} + +static int smsdb_commit_transaction(void) +{ + return db_execute_sql("COMMIT", NULL, NULL); +} + +static int smsdb_rollback_transaction(void) +{ + return db_execute_sql("ROLLBACK", NULL, NULL); +} + + +/*! + * \brief Adds a message part into the DB and returns the whole message into 'out' when the message is complete. + * \param id -- Some ID for the device or so, e.g. the IMSI + * \param addr -- The sender address + * \param ref -- The reference ID + * \param parts -- The total number of messages + * \param order -- The current message number + * \param msg -- The current message part + * \param out -- Output: Only written if parts == cnt + * \retval <=0 Error + * \retval >0 Current number of messages in the DB + */ +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; + int res = 0; + + 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)); + return -1; + } + + fullkey_len = snprintf(fullkey, sizeof(fullkey), "%s/%s/%d/%d", id, addr, ref, parts); + + ast_mutex_lock(&dblock); + 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) { + 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) { + ast_log(LOG_WARNING, "Couldn't execute statement: %s\n", sqlite3_errmsg(smsdb)); + res = -1; + } + + sqlite3_reset(put_message_stmt); + + if (sqlite3_bind_text(get_cnt_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_cnt_stmt) != SQLITE_ROW) { + ast_debug(1, "Unable to find key '%s'\n", fullkey); + res = -1; + } + res = sqlite3_column_int(get_cnt_stmt, 0); + + sqlite3_reset(get_cnt_stmt); + + if (res != -1 && res == parts) { + if (sqlite3_bind_text(get_full_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 while (sqlite3_step(get_full_message_stmt) == SQLITE_ROW) { + part = (const char*)sqlite3_column_text(get_full_message_stmt, 0); + if (!part) { + ast_log(LOG_WARNING, "Couldn't get value\n"); + res = -1; + break; + } + out = stpcpy(out, part); + } + sqlite3_reset(get_full_message_stmt); + + if (res != -1) { + 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; + } else if (sqlite3_step(clear_messages_stmt) != SQLITE_DONE) { + ast_debug(1, "Unable to find key '%s'; Ignoring\n", fullkey); + } + sqlite3_reset(clear_messages_stmt); + } + } + + db_sync(); + + ast_mutex_unlock(&dblock); + + return res; +} + +static int smsdb_purge(int ttl) +{ + int res = 0; + + if (sqlite3_bind_int(purge_messages_stmt, 1, ttl) != 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) { + res = -1; + } + sqlite3_reset(purge_messages_stmt); + + return res; +} + +EXPORT_DEF int smsdb_outgoing_get(const char *id) +{ + int res = 0; + + ast_mutex_lock(&dblock); + + if (sqlite3_bind_text(inc_outgoing_stmt, 1, id, strlen(id), 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(inc_outgoing_stmt) != SQLITE_DONE) { + res = -1; + } + sqlite3_reset(inc_outgoing_stmt); + db_sync(); + + if (res != -1) { + if (sqlite3_bind_text(get_outgoing_stmt, 1, id, strlen(id), 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); + res = -1; + } + res = sqlite3_column_int(get_outgoing_stmt, 0); + sqlite3_reset(get_outgoing_stmt); + } + + ast_mutex_unlock(&dblock); + + 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) +{ + dosync = 1; + ast_cond_signal(&dbcond); +} + +/*! + * \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); + } + dosync = 0; + smsdb_purge(CONF_GLOBAL(csms_ttl)); + if (smsdb_commit_transaction()) { + smsdb_rollback_transaction(); + } + if (doexit) { + ast_mutex_unlock(&dblock); + break; + } + smsdb_begin_transaction(); + ast_mutex_unlock(&dblock); + sleep(1); + ast_mutex_lock(&dblock); + } + + return NULL; +} + +/*! + * \internal + * \brief Clean up resources on Asterisk shutdown + */ +static void smsdb_atexit(void) +{ + /* 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) { + smsdb = NULL; + } + ast_mutex_unlock(&dblock); +} + +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 new file mode 100644 index 00000000..52daa35c --- /dev/null +++ b/smsdb.h @@ -0,0 +1,14 @@ +/* + Copyright (C) 2020 Max von Buelow +*/ + +#ifndef CHAN_DONGLE_SMSDB_H_INCLUDED +#define CHAN_DONGLE_SMSDB_H_INCLUDED + +#include "export.h" /* EXPORT_DECL EXPORT_DEF */ + +EXPORT_DECL int smsdb_init(); +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); + +#endif diff --git a/test/gen.c b/test/gen.c new file mode 100644 index 00000000..52ca6fab --- /dev/null +++ b/test/gen.c @@ -0,0 +1,94 @@ +#include +#include "pdu.h" +#include "gsm7_luts.h" + +int ok = 0; +int faults = 0; + +/* We call ast_log from pdu.c, so we'll fake an implementation here. */ +void ast_log(int level, const char* fmt, ...) +{ + /* Silence compiler warnings */ + (void)level; + (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) +{ + if (strcmp(buf, res4[idx])) { + fprintf(stderr, "Check 4 unsuccessful; Expected %s, got %s\n", res4[idx], buf); + ++faults; + } else { + ++ok; + } + ++idx; + return 0; +} +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); +} + + +#/* */ +int main() +{ + test_pdu_build(); + + fprintf(stderr, "done %d tests: %d OK %d FAILS\n", ok + faults, ok, faults); + + if (faults) { + return 1; + } + return 0; +} diff --git a/test/parse.c b/test/parse.c index 14afa125..c72badb0 100644 --- a/test/parse.c +++ b/test/parse.c @@ -190,6 +190,7 @@ void test_parse_cmgr() char * msg; str_encoding_t msg_enc; char * msg_utf8; + pdu_udh_t udh; }; static const struct test_case { const char * input; @@ -200,7 +201,7 @@ void test_parse_cmgr() NULL, "\"REC READ\",\"+79139131234", "+79139131234", - STR_ENCODING_7BIT, + STR_ENCODING_ASCII, "041F04400438043204350442", STR_ENCODING_UNKNOWN, NULL @@ -222,9 +223,9 @@ void test_parse_cmgr() NULL, "B1582C168BC562B1984C2693C96432994C369BCD66B3D96C369BD168341A8D46A3D168B55AAD56ABD56AB59ACD66B3D96C369BCD76BBDD6EB7DBED76BBE170381C0E87C3E170B95C2E97CBE572B91C0C0683C16030180C", "+11111111112", - STR_ENCODING_7BIT, + STR_ENCODING_ASCII, "B1582C168BC562B1984C2693C96432994C369BCD66B3D96C369BD168341A8D46A3D168B55AAD56ABD56AB59ACD66B3D96C369BCD76BBDD6EB7DBED76BBE170381C0E87C3E170B95C2E97CBE572B91C0C0683C16030180C", - STR_ENCODING_7BIT_HEX_PAD_0, + STR_ENCODING_GSM7_HEX_PAD_0, "111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999000000000" } }, @@ -233,7 +234,7 @@ void test_parse_cmgr() NULL, "0031003100310031003100310031003100310031003200320032003200320032003200320032003200330033003300330033003300330033003300330034003400340034003400340034003400340034003500350035003500350035003500350035003500360036003600360036003600360036003600360037003700370037003700370037", "+22222222022", - STR_ENCODING_7BIT, + STR_ENCODING_ASCII, "0031003100310031003100310031003100310031003200320032003200320032003200320032003200330033003300330033003300330033003300330034003400340034003400340034003400340034003500350035003500350035003500350035003500360036003600360036003600360036003600360037003700370037003700370037", STR_ENCODING_UCS2_HEX, "1111111111222222222233333333334444444444555555555566666666667777777" @@ -244,9 +245,9 @@ void test_parse_cmgr() NULL, "82E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3", "+33600000000", - STR_ENCODING_7BIT, + STR_ENCODING_ASCII, "82E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3", - STR_ENCODING_7BIT_HEX_PAD_1, + 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" } }, @@ -255,9 +256,9 @@ void test_parse_cmgr() NULL, "C26150301C0E8741C170381C0605C3E17018", "+33600000000", - STR_ENCODING_7BIT, + STR_ENCODING_ASCII, "C26150301C0E8741C170381C0605C3E17018", - STR_ENCODING_7BIT_HEX_PAD_1, + STR_ENCODING_GSM7_HEX_PAD_1, "aa Aaaaa Aaaaa Aaaaa" } }, @@ -266,9 +267,9 @@ void test_parse_cmgr() NULL, "A9E5391D14060941439015240409414290102404094142901024040941429010240409414290106405594142901564055941429012A40429AD4AABD22A7481AC56101264455A915624C80AB282AC20A1D06A0559415610D20A4282AC2024C80AB282AC202BC80AB282AC2E9012B4042D414A90D2055282942E90D20502819420254809528294", "+2222222222", - STR_ENCODING_7BIT, + STR_ENCODING_ASCII, "A9E5391D14060941439015240409414290102404094142901024040941429010240409414290106405594142901564055941429012A40429AD4AABD22A7481AC56101264455A915624C80AB282AC20A1D06A0559415610D20A4282AC2024C80AB282AC202BC80AB282AC2E9012B4042D414A90D2055282942E90D20502819420254809528294", - STR_ENCODING_7BIT_HEX_PAD_1, + 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" } }, @@ -277,9 +278,9 @@ void test_parse_cmgr() NULL, "A09976D7E9E5390B640FB3D364103DCD668364B3562CD692C1623417", "Ufone", /* 55F3DB5D062 */ - STR_ENCODING_7BIT_HEX_PAD_0, + STR_ENCODING_GSM7_HEX_PAD_0, "A09976D7E9E5390B640FB3D364103DCD668364B3562CD692C1623417", - STR_ENCODING_7BIT_HEX_PAD_5, + STR_ENCODING_GSM7_HEX_PAD_5, "Minutes, valid till 23-11-2014." } }, @@ -288,9 +289,9 @@ void test_parse_cmgr() NULL, "49A7F1099A36A720D9EC059BB140319C2E06D38186EF39FD0D1AA3D3E176981E06155D20184B467381926CD0585E26A7E96F1001547481683816ACE60241CB7250DA6D7E83E67550D95E76D3EB61761AF486EBD36F771A14A6D3D3F632A80C12BFDDF539485E9EA7C9F534688C4E87DB61100D968BD95C", "40033", /* 09D034186C3603 */ - STR_ENCODING_7BIT_HEX_PAD_0, + STR_ENCODING_GSM7_HEX_PAD_0, "49A7F1099A36A720D9EC059BB140319C2E06D38186EF39FD0D1AA3D3E176981E06155D20184B467381926CD0585E26A7E96F1001547481683816ACE60241CB7250DA6D7E83E67550D95E76D3EB61761AF486EBD36F771A14A6D3D3F632A80C12BFDDF539485E9EA7C9F534688C4E87DB61100D968BD95C", - STR_ENCODING_7BIT_HEX_PAD_0, + 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." } }, @@ -312,22 +313,22 @@ 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.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_recode(RECODE_DECODE, result.oa_enc, + if (str_decode(result.oa_enc, result.oa, strlen(result.oa), - tmp_oa, sizeof(tmp_oa)) >= 0) { + 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_recode(RECODE_DECODE, result.msg_enc, + if (str_decode(result.msg_enc, result.str, strlen(result.str), - buf, sizeof(buf)) >= 0) { + buf, sizeof(buf), 0, 0) >= 0) { result.msg_utf8 = buf; } } From 1815dddcfbbf9f5b03a021ce9e5d2aa11db24cb3 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Mon, 24 Feb 2020 10:41:50 +0100 Subject: [PATCH 072/117] Rename enque to enqueue, and remove fishy function pointer casts The casting of functions taking fewer parameters than were given probably did work on most platforms with common calling conventions. But the calling convention is not defined by us. And the C-style cast removes all other sanity checks as well. --- at_command.c | 95 ++++++++++++++++++++++++++------------------------- at_command.h | 42 +++++++++++------------ at_response.c | 26 +++++++------- chan_dongle.c | 4 +-- channel.c | 10 +++--- helpers.c | 12 +++---- 6 files changed, 96 insertions(+), 93 deletions(-) diff --git a/at_command.c b/at_command.c index 1eb9ebf2..39876eb7 100644 --- a/at_command.c +++ b/at_command.c @@ -84,7 +84,7 @@ static int __attribute__ ((format(printf, 2, 3))) at_fill_generic_cmd (at_queue_ } /*! - * \brief Enque generic command + * \brief Enqueue generic command * \param pvt -- pvt structure * \param cmd -- at command * \param prio -- queue priority of this command @@ -92,7 +92,7 @@ static int __attribute__ ((format(printf, 2, 3))) at_fill_generic_cmd (at_queue_ * \return 0 on success */ -static int __attribute__ ((format(printf, 4, 5))) at_enque_generic (struct cpvt* cpvt, at_cmd_t cmd, int prio, const char * format, ...) +static int __attribute__ ((format(printf, 4, 5))) at_enqueue_generic(struct cpvt *cpvt, at_cmd_t cmd, int prio, const char *format, ...) { va_list ap; int rv; @@ -108,12 +108,12 @@ static int __attribute__ ((format(printf, 4, 5))) at_enque_generic (struct cpvt* } /*! - * \brief Enque initialization commands + * \brief Enqueue initialization commands * \param cpvt -- cpvt structure * \param from_command -- begin initialization from this command in list * \return 0 on success */ -EXPORT_DEF int at_enque_initialization(struct cpvt* cpvt, at_cmd_t from_command) +EXPORT_DEF int at_enqueue_initialization(struct cpvt *cpvt, at_cmd_t from_command) { static const char cmd2[] = "ATZ\r"; static const char cmd3[] = "ATE0\r"; @@ -232,12 +232,12 @@ EXPORT_DEF int at_enque_initialization(struct cpvt* cpvt, at_cmd_t from_command) } /*! - * \brief Enque the AT+COPS? command + * \brief Enqueue the AT+COPS? command * \param cpvt -- cpvt structure * \return 0 on success */ -EXPORT_DEF int at_enque_cops (struct cpvt* cpvt) +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); @@ -247,7 +247,7 @@ EXPORT_DEF int at_enque_cops (struct cpvt* cpvt) /* SMS sending */ -EXPORT_DEF int at_enque_pdu(struct cpvt * cpvt, const char * pdu, attribute_unused const char * u1, attribute_unused unsigned u2, attribute_unused int u3, void ** id) +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) { char * ptr = (char *) pdu; char buf[8+25+1]; @@ -293,19 +293,19 @@ EXPORT_DEF int at_enque_pdu(struct cpvt * cpvt, const char * pdu, attribute_unus } /*! - * \brief Enque a partial SMS message + * \brief Enqueue a partial SMS message * \param cpvt -- cpvt structure * \param number -- the destination of the message * \param msg -- utf-8 encoded message */ -static int at_enque_sms_part(const char *buf, unsigned length, void *s) +static int at_enqueue_sms_part(const char *buf, unsigned length, void *s) { (void)(length); csms_part_data_t *dta = s; - return at_enque_pdu(dta->cpvt, buf, NULL, 0, 0, dta->id); + return at_enqueue_pdu(dta->cpvt, buf, NULL, 0, 0, dta->id); } -EXPORT_DEF int at_enque_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, void **id) { ssize_t res; pvt_t* pvt = cpvt->pvt; @@ -317,7 +317,7 @@ EXPORT_DEF int at_enque_sms (struct cpvt* cpvt, const char* destination, const c */ csms_part_data_t dta = { cpvt, id }; uint16_t csmsref = smsdb_outgoing_get(PVT_ID(pvt)); - res = pdu_build_mult(at_enque_sms_part, "", destination, msg, validity_minutes, report_req, csmsref, &dta); + res = pdu_build_mult(at_enqueue_sms_part, "", destination, msg, validity_minutes, report_req, csmsref, &dta); if(res <= 0) { if(res == -E2BIG) @@ -333,12 +333,12 @@ EXPORT_DEF int at_enque_sms (struct cpvt* cpvt, const char* destination, const c } /*! - * \brief Enque AT+CUSD. + * \brief Enqueue AT+CUSD. * \param cpvt -- cpvt structure * \param code the CUSD code to send */ -EXPORT_DEF int at_enque_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, attribute_unused const char *u1, attribute_unused unsigned u2, attribute_unused int u3, void **id) { static const char cmd[] = "AT+CUSD=1,\""; static const char cmd_end[] = "\",15\r"; @@ -379,13 +379,13 @@ EXPORT_DEF int at_enque_ussd (struct cpvt * cpvt, const char * code, attribute_u /*! - * \brief Enque a DTMF command + * \brief Enqueue a DTMF command * \param cpvt -- cpvt structure * \param digit -- the dtmf digit to send * \return -2 if digis is invalid, 0 on success */ -EXPORT_DEF int at_enque_dtmf (struct cpvt* cpvt, char digit) +EXPORT_DEF int at_enqueue_dtmf(struct cpvt *cpvt, char digit) { switch (digit) { @@ -413,18 +413,19 @@ EXPORT_DEF int at_enque_dtmf (struct cpvt* cpvt, char digit) case '*': case '#': - return at_enque_generic(cpvt, CMD_AT_DTMF, 1, "AT^DTMF=%d,%c\r", cpvt->call_idx, digit); + return at_enqueue_generic(cpvt, CMD_AT_DTMF, 1, "AT^DTMF=%d,%c\r", cpvt->call_idx, digit); } return -1; } /*! - * \brief Enque the AT+CCWA command (disable call waiting) + * \brief Enqueue the AT+CCWA command (disable call waiting) * \param cpvt -- cpvt structure * \return 0 on success */ -EXPORT_DEF int at_enque_set_ccwa (struct cpvt* cpvt, attribute_unused const char * unused1, attribute_unused const char * unused2, unsigned call_waiting) +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) { static const char cmd_ccwa_get[] = "AT+CCWA=1,2,1\r"; static const char cmd_ccwa_set[] = "AT+CCWA=%d,%d,%d\r"; @@ -459,12 +460,13 @@ EXPORT_DEF int at_enque_set_ccwa (struct cpvt* cpvt, attribute_unused const char } /*! - * \brief Enque the device reset command (AT+CFUN Operation Mode Setting) + * \brief Enqueue the device reset command (AT+CFUN Operation Mode Setting) * \param cpvt -- cpvt structure * \return 0 on success */ -EXPORT_DEF int at_enque_reset (struct cpvt* cpvt) +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) { 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); @@ -474,13 +476,13 @@ EXPORT_DEF int at_enque_reset (struct cpvt* cpvt) /*! - * \brief Enque a dial commands + * \brief Enqueue a dial commands * \param cpvt -- cpvt structure * \param number -- the called number * \param clir -- value of clir * \return 0 on success */ -EXPORT_DEF int at_enque_dial(struct cpvt* cpvt, const char * number, int clir) +EXPORT_DEF int at_enqueue_dial(struct cpvt *cpvt, const char *number, int clir) { struct pvt *pvt = cpvt->pvt; int err; @@ -534,11 +536,11 @@ EXPORT_DEF int at_enque_dial(struct cpvt* cpvt, const char * number, int clir) } /*! - * \brief Enque a answer commands + * \brief Enqueue a answer commands * \param cpvt -- cpvt structure * \return 0 on success */ -EXPORT_DEF int at_enque_answer(struct cpvt* cpvt) +EXPORT_DEF int at_enqueue_answer(struct cpvt *cpvt) { at_queue_cmd_t cmds[] = { ATQ_CMD_DECLARE_DYN(CMD_AT_A), @@ -573,11 +575,11 @@ EXPORT_DEF int at_enque_answer(struct cpvt* cpvt) } /*! - * \brief Enque an activate commands 'Put active calls on hold and activate call x.' + * \brief Enqueue an activate commands 'Put active calls on hold and activate call x.' * \param cpvt -- cpvt structure * \return 0 on success */ -EXPORT_DEF int at_enque_activate (struct cpvt* cpvt) +EXPORT_DEF int at_enqueue_activate(struct cpvt *cpvt) { at_queue_cmd_t cmds[] = { ATQ_CMD_DECLARE_DYN(CMD_AT_CHLD_2x), @@ -603,11 +605,11 @@ EXPORT_DEF int at_enque_activate (struct cpvt* cpvt) } /*! - * \brief Enque an commands for 'Put active calls on hold and activate the waiting or held call.' + * \brief Enqueue an commands for 'Put active calls on hold and activate the waiting or held call.' * \param pvt -- pvt structure * \return 0 on success */ -EXPORT_DEF int at_enque_flip_hold (struct cpvt* cpvt) +EXPORT_DEF int at_enqueue_flip_hold(struct cpvt *cpvt) { static const at_queue_cmd_t cmds[] = { ATQ_CMD_DECLARE_ST(CMD_AT_CHLD_2, cmd_chld2), @@ -618,11 +620,11 @@ EXPORT_DEF int at_enque_flip_hold (struct cpvt* cpvt) } /*! - * \brief Enque ping command + * \brief Enqueue ping command * \param pvt -- pvt structure * \return 0 on success */ -EXPORT_DEF int at_enque_ping (struct cpvt * cpvt) +EXPORT_DEF int at_enqueue_ping(struct cpvt *cpvt) { static const at_queue_cmd_t cmds[] = { ATQ_CMD_DECLARE_STIT(CMD_AT, cmd_at, ATQ_CMD_TIMEOUT_SHORT, 0), @@ -632,24 +634,25 @@ EXPORT_DEF int at_enque_ping (struct cpvt * cpvt) } /*! - * \brief Enque user-specified command + * \brief Enqueue user-specified command * \param cpvt -- cpvt structure * \param input -- user's command * \return 0 on success */ -EXPORT_DEF int at_enque_user_cmd(struct cpvt* cpvt, const char * input) +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) { - return at_enque_generic(cpvt, CMD_USER, 1, "%s\r", input); + return at_enqueue_generic(cpvt, CMD_USER, 1, "%s\r", input); } /*! - * \brief Enque commands for reading SMS + * \brief Enqueue commands for reading SMS * \param cpvt -- cpvt structure * \param index -- index of message in store - * \param delete -- if non-zero also enque commands for delete message in store after reading + * \param delete -- if non-zero also enqueue commands for delete message in store after reading * \return 0 on success */ -EXPORT_DEF int at_enque_retrive_sms (struct cpvt* cpvt, int index, int delete) +EXPORT_DEF int at_enqueue_retrieve_sms(struct cpvt *cpvt, int index, int delete) { int err; at_queue_cmd_t cmds[] = { @@ -680,13 +683,13 @@ EXPORT_DEF int at_enque_retrive_sms (struct cpvt* cpvt, int index, int delete) } /*! - * \brief Enque AT+CHLD1x or AT+CHUP hangup command + * \brief Enqueue AT+CHLD1x or AT+CHUP hangup command * \param cpvt -- channel_pvt structure * \param call_idx -- call id * \return 0 on success */ -EXPORT_DEF int at_enque_hangup (struct cpvt* cpvt, int call_idx) +EXPORT_DEF int at_enqueue_hangup(struct cpvt *cpvt, int call_idx) { /* @@ -762,7 +765,7 @@ EXPORT_DEF int at_enque_hangup (struct cpvt* cpvt, int call_idx) idx = 1; } - return at_enque_generic(cpvt, commands[idx].cmd, 1, commands[idx].data, call_idx); + return at_enqueue_generic(cpvt, commands[idx].cmd, 1, commands[idx].data, call_idx); */ static const char cmd_chup[] = "AT+CHUP\r"; @@ -793,12 +796,12 @@ EXPORT_DEF int at_enque_hangup (struct cpvt* cpvt, int call_idx) } /*! - * \brief Enque AT+CLVL commands for volume synchronization + * \brief Enqueue AT+CLVL commands for volume synchronization * \param cpvt -- cpvt structure * \return 0 on success */ -EXPORT_DEF int at_enque_volsync (struct cpvt* cpvt) +EXPORT_DEF int at_enqueue_volsync(struct cpvt *cpvt) { static const char cmd1[] = "AT+CLVL=1\r"; static const char cmd2[] = "AT+CLVL=5\r"; @@ -810,11 +813,11 @@ EXPORT_DEF int at_enque_volsync (struct cpvt* cpvt) } /*! - * \brief Enque AT+CLCC command + * \brief Enqueue AT+CLCC command * \param cpvt -- cpvt structure * \return 0 on success */ -EXPORT_DEF int at_enque_clcc (struct cpvt* cpvt) +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); @@ -822,11 +825,11 @@ EXPORT_DEF int at_enque_clcc (struct cpvt* cpvt) } /*! - * \brief Enque AT+CHLD=3 command + * \brief Enqueue AT+CHLD=3 command * \param cpvt -- cpvt structure * \return 0 on success */ -EXPORT_DEF int at_enque_conference (struct cpvt* cpvt) +EXPORT_DEF int at_enqueue_conference(struct cpvt *cpvt) { static const char cmd_chld3[] = "AT+CHLD=3\r"; static const at_queue_cmd_t cmds[] = { diff --git a/at_command.h b/at_command.h index e6cb69b7..8ad4feb0 100644 --- a/at_command.h +++ b/at_command.h @@ -148,26 +148,26 @@ INLINE_DECL const char* at_cmd2str (at_cmd_t cmd) struct cpvt; -EXPORT_DECL const char* at_cmd2str (at_cmd_t cmd); -EXPORT_DECL int at_enque_initialization(struct cpvt * cpvt, at_cmd_t from_command); -EXPORT_DECL int at_enque_ping (struct cpvt * cpvt); -EXPORT_DECL int at_enque_cops (struct cpvt * cpvt); -EXPORT_DECL int at_enque_sms (struct cpvt * cpvt, const char * number, const char * msg, unsigned validity_min, int report_req, void ** id); -EXPORT_DECL int at_enque_pdu (struct cpvt * cpvt, const char * pdu, attribute_unused const char *, attribute_unused unsigned, attribute_unused int, void ** id); -EXPORT_DECL int at_enque_ussd (struct cpvt * cpvt, const char * code, attribute_unused const char *, attribute_unused unsigned, attribute_unused int, void ** id); -EXPORT_DECL int at_enque_dtmf (struct cpvt * cpvt, char digit); -EXPORT_DECL int at_enque_set_ccwa (struct cpvt * cpvt, attribute_unused const char * unused1, attribute_unused const char * unused2, unsigned call_waiting); -EXPORT_DECL int at_enque_reset (struct cpvt * cpvt); -EXPORT_DECL int at_enque_dial(struct cpvt * cpvt, const char * number, int clir); -EXPORT_DECL int at_enque_answer(struct cpvt * cpvt); -EXPORT_DECL int at_enque_user_cmd(struct cpvt * cpvt, const char * input); -EXPORT_DECL int at_enque_retrive_sms(struct cpvt * cpvt, int index, int delete); -EXPORT_DECL int at_enque_hangup (struct cpvt * cpvt, int call_idx); -EXPORT_DECL int at_enque_volsync (struct cpvt * cpvt); -EXPORT_DECL int at_enque_clcc (struct cpvt * cpvt); -EXPORT_DECL int at_enque_activate (struct cpvt * cpvt); -EXPORT_DECL int at_enque_flip_hold (struct cpvt * cpvt); -EXPORT_DECL int at_enque_conference (struct cpvt * cpvt); -EXPORT_DECL void at_hangup_immediality(struct cpvt * cpvt); +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_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_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_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); +EXPORT_DECL int at_enqueue_clcc(struct cpvt *cpvt); +EXPORT_DECL int at_enqueue_activate(struct cpvt *cpvt); +EXPORT_DECL int at_enqueue_flip_hold(struct cpvt *cpvt); +EXPORT_DECL int at_enqueue_conference(struct cpvt *cpvt); +EXPORT_DECL void at_hangup_immediality(struct cpvt *cpvt); #endif /* CHAN_DONGLE_AT_SEND_H_INCLUDED */ diff --git a/at_response.c b/at_response.c index 4cfa386f..6b4f274c 100644 --- a/at_response.c +++ b/at_response.c @@ -395,7 +395,7 @@ static int at_response_error (struct pvt* pvt, at_res_t res) if (!pvt->initialized) { /* continue initialization in other job at cmd CMD_AT_CMGF */ - if (at_enque_initialization(task->cpvt, CMD_AT_CMGF)) + if (at_enqueue_initialization(task->cpvt, CMD_AT_CMGF)) { ast_log (LOG_ERROR, "[%s] Error schedule initialization commands\n", PVT_ID(pvt)); goto e_return; @@ -424,7 +424,7 @@ static int at_response_error (struct pvt* pvt, at_res_t res) if (pvt->has_voice) { /* continue initialization in other job at cmd CMD_AT_CSQ */ - if (at_enque_initialization(task->cpvt, CMD_AT_CSQ)) + if (at_enqueue_initialization(task->cpvt, CMD_AT_CSQ)) { ast_log (LOG_ERROR, "[%s] Error querying signal strength\n", PVT_ID(pvt)); goto e_return; @@ -596,9 +596,9 @@ static int at_response_mode (struct pvt* pvt, char* str, size_t len) static void request_clcc(struct pvt* pvt) { - if (at_enque_clcc(&pvt->sys_chan)) + if (at_enqueue_clcc(&pvt->sys_chan)) { - ast_log (LOG_ERROR, "[%s] Error enque List Current Calls request\n", PVT_ID(pvt)); + ast_log(LOG_ERROR, "[%s] Error enqueue List Current Calls request\n", PVT_ID(pvt)); } } @@ -652,7 +652,7 @@ static int at_response_orig (struct pvt* pvt, const char* str) if(pvt->volume_sync_step == VOLUME_SYNC_BEGIN) { pvt->volume_sync_step = VOLUME_SYNC_BEGIN; - if (at_enque_volsync (cpvt)) + if (at_enqueue_volsync(cpvt)) { ast_log (LOG_ERROR, "[%s] Error synchronize audio level\n", PVT_ID(pvt)); } @@ -822,11 +822,11 @@ static int at_response_conn (struct pvt* pvt, const char* str) PVT_STAT(pvt, calls_answered[cpvt->dir]) ++; change_channel_state(cpvt, CALL_STATE_ACTIVE, 0); if(CPVT_TEST_FLAG(cpvt, CALL_FLAG_CONFERENCE)) - at_enque_conference(cpvt); + at_enqueue_conference(cpvt); } else { - at_enque_hangup(&pvt->sys_chan, call_index); + at_enqueue_hangup(&pvt->sys_chan, call_index); ast_log (LOG_ERROR, "[%s] answered incoming call with not exists call idx %d, hanging up!\n", PVT_ID(pvt), call_index); } } @@ -857,7 +857,7 @@ static int start_pbx(struct pvt* pvt, const char * number, int call_idx, call_st { ast_log (LOG_ERROR, "[%s] Unable to allocate channel for incoming call\n", PVT_ID(pvt)); - if (at_enque_hangup (&pvt->sys_chan, call_idx)) + if (at_enqueue_hangup(&pvt->sys_chan, call_idx)) { ast_log (LOG_ERROR, "[%s] Error sending AT+CHUP command\n", PVT_ID(pvt)); } @@ -1027,7 +1027,7 @@ static int at_response_clcc (struct pvt* pvt, char* str) call will be activated but no voice */ ast_debug (1, "[%s] all %u call held, try activate some\n", PVT_ID(pvt), all); - if(at_enque_flip_hold(&pvt->sys_chan)) + if (at_enqueue_flip_hold(&pvt->sys_chan)) { ast_log (LOG_ERROR, "[%s] can't flip active and hold/waiting calls \n", PVT_ID(pvt)); } @@ -1126,7 +1126,7 @@ static int at_response_ring (struct pvt* pvt) /* We only want to syncronize volume on the first ring and if no channels yes */ if (pvt->volume_sync_step == VOLUME_SYNC_BEGIN && PVT_NO_CHANS(pvt)) { - if (at_enque_volsync (&pvt->sys_chan)) + if (at_enqueue_volsync(&pvt->sys_chan)) { ast_log (LOG_ERROR, "[%s] Error synchronize audio level\n", PVT_ID(pvt)); } @@ -1164,7 +1164,7 @@ static int at_response_cmti (struct pvt* pvt, const char* str) if (pvt_enabled(pvt)) { - if (at_enque_retrive_sms (&pvt->sys_chan, index, CONF_SHARED(pvt, autodeletesms))) + 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; @@ -1558,7 +1558,7 @@ static int at_response_creg (struct pvt* pvt, char* str, size_t len) char* lac; char* ci; - if (at_enque_cops (&pvt->sys_chan)) + if (at_enqueue_cops(&pvt->sys_chan)) { ast_log (LOG_ERROR, "[%s] Error sending query for provider name\n", PVT_ID(pvt)); } @@ -1574,7 +1574,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_enque_set_ccwa(&pvt->sys_chan, 0, 0, CONF_SHARED(pvt, callwaiting)); + at_enqueue_set_ccwa(&pvt->sys_chan, 0, 0, CONF_SHARED(pvt, callwaiting), 0, NULL); //#endif pvt->gsm_registered = 1; manager_event_device_status(PVT_ID(pvt), "Register"); diff --git a/chan_dongle.c b/chan_dongle.c index 5204eb7f..e2f27489 100644 --- a/chan_dongle.c +++ b/chan_dongle.c @@ -404,7 +404,7 @@ static void* do_monitor_phone (void* data) clean_read_data(dev, fd); /* schedule dongle initilization */ - if (at_enque_initialization (&pvt->sys_chan, CMD_AT)) + if (at_enqueue_initialization(&pvt->sys_chan, CMD_AT)) { ast_log (LOG_ERROR, "[%s] Error adding initialization commands to queue\n", dev); goto e_cleanup; @@ -444,7 +444,7 @@ static void* do_monitor_phone (void* data) ast_log (LOG_ERROR, "[%s] timedout while waiting '%s' in response to '%s'\n", dev, at_res2str (ecmd->res), at_cmd2str (ecmd->cmd)); goto e_cleanup; } - at_enque_ping(&pvt->sys_chan); + at_enqueue_ping(&pvt->sys_chan); ast_mutex_unlock (&pvt->lock); continue; } diff --git a/channel.c b/channel.c index 1022b6e1..f793f97c 100644 --- a/channel.c +++ b/channel.c @@ -287,7 +287,7 @@ static int channel_call(struct ast_channel* channel, char* dest, attribute_unuse } PVT_STAT(pvt, out_calls) ++; - if (at_enque_dial (cpvt, dest_num, clir)) + if (at_enqueue_dial(cpvt, dest_num, clir)) { ast_mutex_unlock (&pvt->lock); ast_log (LOG_ERROR, "[%s] Error sending ATD command\n", PVT_ID(pvt)); @@ -395,7 +395,7 @@ static int channel_hangup (struct ast_channel* channel) if (CPVT_TEST_FLAG(cpvt, CALL_FLAG_NEED_HANGUP)) { - if (at_enque_hangup (cpvt, cpvt->call_idx)) + if (at_enqueue_hangup(cpvt, cpvt->call_idx)) ast_log (LOG_ERROR, "[%s] Error adding AT+CHUP command to queue, call not terminated!\n", PVT_ID(pvt)); else CPVT_RESET_FLAGS(cpvt, CALL_FLAG_NEED_HANGUP); @@ -435,7 +435,7 @@ static int channel_answer (struct ast_channel* channel) if (cpvt->dir == CALL_DIR_INCOMING) { - if (at_enque_answer (cpvt)) + if (at_enqueue_answer(cpvt)) { ast_log (LOG_ERROR, "[%s] Error sending answer commands\n", PVT_ID(pvt)); } @@ -463,7 +463,7 @@ static int channel_digit_begin (struct ast_channel* channel, char digit) ast_mutex_lock (&pvt->lock); - rv = at_enque_dtmf (cpvt, digit); + rv = at_enqueue_dtmf(cpvt, digit); if (rv) { ast_mutex_unlock (&pvt->lock); @@ -1524,7 +1524,7 @@ static int channel_func_write(struct ast_channel* channel, const char* function, ; else if (oldstate == CALL_STATE_ONHOLD) { - if(at_enque_activate(cpvt)) + if (at_enqueue_activate(cpvt)) { /* TODO: handle error */ ast_log(LOG_ERROR, diff --git a/helpers.c b/helpers.c index bbf7e6ca..758aa358 100644 --- a/helpers.c +++ b/helpers.c @@ -115,7 +115,7 @@ static const char* send2(const char* dev_name, int * status, int online, const c EXPORT_DEF const char* send_ussd(const char* dev_name, const char* ussd, int * status, void ** id) { if(is_valid_ussd_string(ussd)) - return send2(dev_name, status, 1, "Error adding USSD command to queue", "USSD queued for send", (at_cmd_f)at_enque_ussd, ussd, 0, 0, 0, id); + 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"; @@ -139,7 +139,7 @@ EXPORT_DEF const char * send_sms(const char * dev_name, const char * number, con if(report) srr = ast_true (report); - return send2(dev_name, status, 1, "Error adding SMS commands to queue", "SMS queued for send", at_enque_sms, number, message, val, srr, id); + return send2(dev_name, status, 1, "Error adding SMS commands to queue", "SMS queued for send", at_enqueue_sms, number, message, val, srr, id); } if(status) *status = 0; @@ -149,25 +149,25 @@ EXPORT_DEF const char * send_sms(const char * dev_name, const char * number, con #/* */ 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_enque_pdu, pdu, NULL, 0, 0, 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); } #/* */ EXPORT_DEF const char* send_reset(const char* dev_name, int * status) { - return send2(dev_name, status, 0, "Error adding reset command to queue", "Reset command queued for execute", (at_cmd_f)at_enque_reset, 0, 0, 0, 0, NULL); + 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); } #/* */ EXPORT_DEF const char* send_ccwa_set(const char* dev_name, call_waiting_t enable, int * status) { - return send2(dev_name, status, 1, "Error adding CCWA commands to queue", "Call-Waiting commands queued for execute", (at_cmd_f)at_enque_set_ccwa, 0, 0, enable, 0, NULL); + 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); } #/* */ EXPORT_DEF const char* send_at_command(const char* dev_name, const char* command) { - return send2(dev_name, NULL, 0, "Error adding command", "Command queued for execute", (at_cmd_f)at_enque_user_cmd, command, NULL, 0, 0, NULL); + return send2(dev_name, NULL, 0, "Error adding command", "Command queued for execute", at_enqueue_user_cmd, command, NULL, 0, 0, NULL); } EXPORT_DEF const char* schedule_restart_event(dev_state_t event, restate_time_t when, const char* dev_name, int * status) From 9968d4a4cb32716b0c5b4fa63d69ab8f65030d2a Mon Sep 17 00:00:00 2001 From: miopa Date: Sun, 22 Jul 2018 16:40:08 +0200 Subject: [PATCH 073/117] send sms to local and internal network numbers --- pdu.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pdu.c b/pdu.c index bfd98bfd..4f46d01b 100644 --- a/pdu.c +++ b/pdu.c @@ -211,6 +211,9 @@ #define NUMBER_TYPE_INTERNATIONAL (TP_A_EXT_NOEXT | TP_A_TON_INTERNATIONAL | TP_A_NPI_TEL_E164_E163) /* 0x91 */ #define NUMBER_TYPE_NATIONAL (TP_A_EXT_NOEXT | TP_A_TON_SUBSCRIBERNUM | TP_A_NPI_NATIONALNUM) /* 0xC8 */ #define NUMBER_TYPE_ALPHANUMERIC (TP_A_EXT_NOEXT | TP_A_TON_ALPHANUMERIC | TP_A_NPI_UNKNOWN) /* 0xD0 */ +/* maybe NUMBER_TYPE_NETWORKSHORT should be 0xB1 ??? */ +#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 @@ -666,7 +669,16 @@ EXPORT_DEF int pdu_build16(char* buffer, size_t length, const char* sca, const c sca++; if(dst[0] == '+') + { dst++; + } + else + { + if(strlen(dst) < 6) + dst_toa=NUMBER_TYPE_NETWORKSHORT; //0xB9 + else + dst_toa=NUMBER_TYPE_UNKNOWN; //0X81 + } /* count length of strings */ sca_len = strlen(sca); From cb610d4e5e7d7c7e75e9b8fdc549376a38b47544 Mon Sep 17 00:00:00 2001 From: Max von Buelow Date: Tue, 3 Mar 2020 02:26:15 +0100 Subject: [PATCH 074/117] E3531 support --- at_command.c | 2 +- at_response.c | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/at_command.c b/at_command.c index 39876eb7..fac52354 100644 --- a/at_command.c +++ b/at_command.c @@ -160,7 +160,7 @@ EXPORT_DEF int at_enqueue_initialization(struct cpvt *cpvt, at_cmd_t from_comman ATQ_CMD_DECLARE_STI(CMD_AT_CREG_INIT,cmd14), /* GSM registration status setting */ ATQ_CMD_DECLARE_ST(CMD_AT_CREG, cmd15), /* GSM registration status */ - ATQ_CMD_DECLARE_ST(CMD_AT_CNUM, cmd16), /* Get Subscriber number */ + ATQ_CMD_DECLARE_STI(CMD_AT_CNUM, cmd16), /* Get Subscriber number */ ATQ_CMD_DECLARE_ST(CMD_AT_CVOICE, cmd17), /* read the current voice mode, and return sampling rate、data bit、frame period */ ATQ_CMD_DECLARE_ST(CMD_AT_CSCA, cmd6), /* Get SMS Service center address */ diff --git a/at_response.c b/at_response.c index 6b4f274c..078a7d89 100644 --- a/at_response.c +++ b/at_response.c @@ -337,6 +337,7 @@ static int at_response_error (struct pvt* pvt, at_res_t res) case CMD_AT_U2DIAG: case CMD_AT_CCWA_SET: case CMD_AT_CCWA_STATUS: + case CMD_AT_CNUM: ast_log (LOG_ERROR, "[%s] Command '%s' failed\n", PVT_ID(pvt), at_cmd2str (ecmd->cmd)); /* mean ignore error */ break; @@ -381,11 +382,6 @@ static int at_response_error (struct pvt* pvt, at_res_t res) ast_debug (1, "[%s] Error getting registration info\n", PVT_ID(pvt)); break; - case CMD_AT_CNUM: - ast_log (LOG_WARNING, "[%s] Error checking subscriber phone number\n", PVT_ID(pvt)); - ast_verb (3, "[%s] Dongle needs to be reinitialized. The SIM card is not ready yet\n", PVT_ID(pvt)); - goto e_return; - case CMD_AT_CVOICE: ast_debug (1, "[%s] Dongle has NO voice support\n", PVT_ID(pvt)); ast_log (LOG_WARNING, "[%s] Dongle has NO voice support\n", PVT_ID(pvt)); From a4a7a345e582ee3b15ca3f6142409e4ec2d5c7ba Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Sun, 3 May 2020 09:46:58 +0200 Subject: [PATCH 075/117] Add configure.ac checks for sqlite3 Closes #74, reported by @ruijorgesilva1. --- configure.ac | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index c9a994ab..8725699c 100644 --- a/configure.ac +++ b/configure.ac @@ -82,9 +82,9 @@ if test -z "$RM" ; then fi dnl Checks for libraries. -dnl AC_CHECK_LIB([pthread], [pthread_create]) -dnl AC_CHECK_LIB([iconv], [iconv]) -AC_SEARCH_LIBS([iconv], [c iconv]) +dnl AC_CHECK_LIB([pthread], [pthread_create]) # should use ast_pthread_join everywhere? +AC_SEARCH_LIBS([iconv], [c iconv],,AC_MSG_ERROR([iconv library missing])) +AC_CHECK_LIB([sqlite3], [sqlite3_open],,AC_MSG_ERROR([sqlite3 library missing])) dnl Checks for header files. AC_CHECK_HEADERS([fcntl.h stdlib.h string.h sys/time.h termios.h]) @@ -110,6 +110,7 @@ AC_DEFUN([AC_HEADER_FIND], [ AC_HEADER_FIND([asterisk.h], $with_asterisk) AC_HEADER_FIND([iconv.h], $with_iconv) +AC_CHECK_HEADER([sqlite3.h],,AC_MSG_ERROR([sqlite3.h header file missing])) AC_DEFINE([ICONV_CONST],[], [Define to const if you has iconv() const declaration of input buffer]) AC_MSG_CHECKING([for iconv use const inbuf]) From ba5e7215271283d555fd60083ce0d5d71dafe73e Mon Sep 17 00:00:00 2001 From: Hans Petter Selasky Date: Fri, 29 Sep 2017 10:45:10 +0200 Subject: [PATCH 076/117] Fix for stuck SMS. It has been observed that CMTI notifications can stop when the internal memory of the E1750 dongle is full. Instead an SMMEMFULL indication is received. This patch includes multiple fixes: - Always poll SMS messages during startup. - Only poll one SMS message at a time, by keeping a bitmap of polled messages. - Only delete message after successful reception. Assume that no more than 256 SMS's will be stored by the external SIM card. Signed-off-by: Hans Petter Selasky --- at_command.c | 104 +++++++++++++++++++++++++++++++++++++++++--------- at_command.h | 5 ++- at_response.c | 77 +++++++++++++++++++++++++++---------- at_response.h | 1 + chan_dongle.c | 8 ++++ chan_dongle.h | 4 ++ 6 files changed, 159 insertions(+), 40 deletions(-) diff --git a/at_command.c b/at_command.c index 39876eb7..f60f2598 100644 --- a/at_command.c +++ b/at_command.c @@ -645,39 +645,105 @@ EXPORT_DEF int at_enqueue_user_cmd(struct cpvt *cpvt, const char *input, attribu return at_enqueue_generic(cpvt, CMD_USER, 1, "%s\r", input); } +/*! + * \brief Start reading next SMS, if any + * \param cpvt -- cpvt structure + */ +EXPORT_DEF void at_retrieve_next_sms(struct cpvt *cpvt) +{ + pvt_t *pvt = cpvt->pvt; + unsigned int i; + + if (pvt->incoming_sms_index != -1U) + { + /* clear SMS index */ + i = pvt->incoming_sms_index; + pvt->incoming_sms_index = -1U; + + /* clear this message index from inbox */ + pvt->incoming_sms_inbox[i / 32] &= ~(1U << (i % 32)); + } + + /* get next message to fetch from inbox */ + for (i = 0; i != SMS_INDEX_MAX; i++) + { + if (pvt->incoming_sms_inbox[i / 32] & (1U << (i % 32))) + break; + } + + if (i == SMS_INDEX_MAX || + at_enqueue_retrieve_sms(cpvt, i) != 0) + { + pvt->incoming_sms = 0; + pvt_try_restate(pvt); + } +} + /*! * \brief Enqueue commands for reading SMS * \param cpvt -- cpvt structure * \param index -- index of message in store - * \param delete -- if non-zero also enqueue commands for delete message in store after reading * \return 0 on success */ -EXPORT_DEF int at_enqueue_retrieve_sms(struct cpvt *cpvt, int index, int delete) +EXPORT_DEF int at_enqueue_retrieve_sms(struct cpvt *cpvt, int index) { + pvt_t *pvt = cpvt->pvt; int err; at_queue_cmd_t cmds[] = { ATQ_CMD_DECLARE_DYN2(CMD_AT_CMGR, RES_CMGR), - ATQ_CMD_DECLARE_DYN(CMD_AT_CMGD) - }; - unsigned cmdsno = ITEMS_OF (cmds); + }; + unsigned cmdsno = ITEMS_OF(cmds); + + if (index < 0 || index >= SMS_INDEX_MAX) { + ast_log (LOG_WARNING, "[%s] SMS index [%d] too big\n", PVT_ID(pvt), index); + return -1; + } + + /* set that we want to receive this message */ + pvt->incoming_sms_inbox[index / 32] |= 1U << (index % 32); + + /* check if message is already being received */ + if (pvt->incoming_sms_index != -1U) { + ast_debug (4, "[%s] SMS retrieve of [%d] already in progress\n", + PVT_ID(pvt), pvt->incoming_sms_index); + return 0; + } + + pvt->incoming_sms_index = index; + pvt->incoming_sms = 1; err = at_fill_generic_cmd (&cmds[0], "AT+CMGR=%d\r", index); if (err) - return err; + goto error; - if (delete) - { - err = at_fill_generic_cmd (&cmds[1], "AT+CMGD=%d\r\r", index); - if(err) - { - ast_free (cmds[0].data); - return err; - } - } - else - { - cmdsno--; - } + err = at_queue_insert (cpvt, cmds, cmdsno, 0); + if (err) + goto error; + return 0; +error: + ast_log (LOG_WARNING, "[%s] SMS command error %d\n", PVT_ID(pvt), err); + pvt->incoming_sms_index = -1U; + pvt->incoming_sms = 0; + return err; +} + +/*! + * \brief Enqueue commands for deleting SMS + * \param cpvt -- cpvt structure + * \param index -- index of message in store + * \return 0 on success + */ +EXPORT_DEF int at_enqueue_delete_sms(struct cpvt *cpvt, int index) +{ + int err; + at_queue_cmd_t cmds[] = { + ATQ_CMD_DECLARE_DYN(CMD_AT_CMGD) + }; + unsigned cmdsno = ITEMS_OF(cmds); + + err = at_fill_generic_cmd (&cmds[0], "AT+CMGD=%d\r", index); + if (err) + return err; return at_queue_insert (cpvt, cmds, cmdsno, 0); } diff --git a/at_command.h b/at_command.h index 8ad4feb0..762976fc 100644 --- a/at_command.h +++ b/at_command.h @@ -11,6 +11,7 @@ #include "mutils.h" /* enum2str_def() ITEMS_OF() */ #define CCWA_CLASS_VOICE 1 +#define SMS_INDEX_MAX 256 /* exclusive */ /* magic order !!! keep order of this values like in at_cmd2str() */ @@ -161,7 +162,9 @@ EXPORT_DECL int at_enqueue_reset(struct cpvt *cpvt, const char *u1, const char * 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_retrieve_sms(struct cpvt *cpvt, int index, int delete); +EXPORT_DECL void at_retrieve_next_sms(struct cpvt *cpvt); +EXPORT_DECL int at_enqueue_retrieve_sms(struct cpvt *cpvt, int index); +EXPORT_DECL int at_enqueue_delete_sms(struct cpvt *cpvt, int index); EXPORT_DECL int at_enqueue_hangup(struct cpvt *cpvt, int call_idx); EXPORT_DECL int at_enqueue_volsync(struct cpvt *cpvt); EXPORT_DECL int at_enqueue_clcc(struct cpvt *cpvt); diff --git a/at_response.c b/at_response.c index 6b4f274c..fca74ef1 100644 --- a/at_response.c +++ b/at_response.c @@ -274,6 +274,7 @@ static int at_response_ok (struct pvt* pvt, at_res_t res) case CMD_AT_CMGR: ast_debug (1, "[%s] SMS message see later\n", PVT_ID(pvt)); + at_retrieve_next_sms(&pvt->sys_chan); break; case CMD_AT_CMGD: @@ -481,14 +482,11 @@ static int at_response_error (struct pvt* pvt, at_res_t res) break; case CMD_AT_CMGR: - pvt->incoming_sms = 0; - pvt_try_restate(pvt); ast_log (LOG_ERROR, "[%s] Error reading SMS message\n", PVT_ID(pvt)); + at_retrieve_next_sms(&pvt->sys_chan); break; case CMD_AT_CMGD: - pvt->incoming_sms = 0; - pvt_try_restate(pvt); ast_log (LOG_ERROR, "[%s] Error deleting SMS message\n", PVT_ID(pvt)); break; @@ -529,7 +527,14 @@ static int at_response_error (struct pvt* pvt, at_res_t res) } else if (ecmd) { - ast_log (LOG_ERROR, "[%s] Received 'ERROR' when expecting '%s', ignoring\n", PVT_ID(pvt), at_res2str (ecmd->res)); + switch (ecmd->cmd) { + case CMD_AT_CMGR: + at_retrieve_next_sms(&pvt->sys_chan); + break; + default: + ast_log (LOG_ERROR, "[%s] Received 'ERROR' when expecting '%s', ignoring\n", PVT_ID(pvt), at_res2str (ecmd->res)); + break; + } } else { @@ -1138,6 +1143,36 @@ static int at_response_ring (struct pvt* pvt) return 0; } +/*! + * \brief Poll for SMS messages + * \param pvt -- pvt structure + * \retval 0 success + * \retval -1 failure + */ +int +at_poll_sms (struct pvt *pvt) +{ + /* poll all SMSs stored in device */ + if (CONF_SHARED(pvt, disablesms) == 0) + { + int i; + + for (i = 0; i != SMS_INDEX_MAX; i++) + { + if (at_enqueue_retrieve_sms(&pvt->sys_chan, i)) + { + ast_log (LOG_ERROR, "[%s] Error sending CMGR to retrieve SMS message #%d\n", PVT_ID(pvt), i); + return -1; + } + } + return 0; + } + else + { + return -1; + } +} + /*! * \brief Handle +CMTI response * \param pvt -- pvt structure @@ -1162,14 +1197,10 @@ static int at_response_cmti (struct pvt* pvt, const char* str) { ast_debug (1, "[%s] Incoming SMS message\n", PVT_ID(pvt)); - if (pvt_enabled(pvt)) + if (at_enqueue_retrieve_sms(&pvt->sys_chan, index)) { - 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; + ast_log (LOG_ERROR, "[%s] Error sending CMGR to retrieve SMS message\n", PVT_ID(pvt)); + return -1; } } else @@ -1218,8 +1249,6 @@ static int at_response_cmgr (struct pvt* pvt, const char * str, size_t len) 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 @@ -1229,12 +1258,12 @@ static int at_response_cmgr (struct pvt* pvt, const char * str, size_t len) 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); - return 0; + goto receive_next; } 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; + goto receive_next_no_delete; } ast_debug (1, "[%s] Successfully read SMS message\n", PVT_ID(pvt)); @@ -1252,7 +1281,7 @@ static int at_response_cmgr (struct pvt* pvt, const char * str, size_t len) { ast_log (LOG_ERROR, "[%s] Error decode SMS originator address: '%s', message is '%s'\n", PVT_ID(pvt), oa, str); number = oa; - return 0; + goto receive_next_no_delete; } else number = from_number_utf8_str; @@ -1262,7 +1291,7 @@ static int at_response_cmgr (struct pvt* pvt, const char * str, size_t len) 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; + goto receive_next_no_delete; } else { @@ -1277,14 +1306,15 @@ static int at_response_cmgr (struct pvt* pvt, const char * str, size_t len) 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; + goto receive_as_is; } if (cnt < udh.parts) { ast_verb (1, "[%s] Waiting for following parts\n", PVT_ID(pvt)); - return 0; + goto receive_next; } fullmsg_len = strlen(fullmsg); } else { + receive_as_is: strncpy(fullmsg, msg, msg_len); fullmsg_len = msg_len; } @@ -1310,6 +1340,13 @@ static int at_response_cmgr (struct pvt* pvt, const char * str, size_t len) ast_log (LOG_ERROR, "[%s] Received '+CMGR' when expecting '%s' response to '%s', ignoring\n", PVT_ID(pvt), at_res2str (ecmd->res), at_cmd2str (ecmd->cmd)); } +receive_next: + if (CONF_SHARED(pvt, autodeletesms) && pvt->incoming_sms_index != -1U) + { + at_enqueue_delete_sms(&pvt->sys_chan, pvt->incoming_sms_index); + } +receive_next_no_delete: + at_retrieve_next_sms(&pvt->sys_chan); } else { diff --git a/at_response.h b/at_response.h index d00444bd..b0ee2dcc 100644 --- a/at_response.h +++ b/at_response.h @@ -81,5 +81,6 @@ typedef struct at_responses_t EXPORT_DECL const at_responses_t at_responses; EXPORT_DECL const char* at_res2str (at_res_t res); EXPORT_DECL int at_response (struct pvt* pvt, const struct iovec * iov, int iovcnt, at_res_t at_res); +EXPORT_DECL int at_poll_sms (struct pvt* pvt); #endif /* CHAN_DONGLE_AT_RESPONSE_H_INCLUDED */ diff --git a/chan_dongle.c b/chan_dongle.c index e2f27489..4fb0655c 100644 --- a/chan_dongle.c +++ b/chan_dongle.c @@ -334,6 +334,7 @@ static void disconnect_dongle (struct pvt* pvt) pvt->cwaiting = 0; pvt->outgoing_sms = 0; pvt->incoming_sms = 0; + pvt->incoming_sms_index = -1U; pvt->volume_sync_step = VOLUME_SYNC_BEGIN; pvt->current_state = DEV_STATE_STOPPED; @@ -410,6 +411,12 @@ static void* do_monitor_phone (void* data) goto e_cleanup; } + /* Poll first SMS, if any */ + if (at_poll_sms(pvt) == 0) + { + ast_debug (1, "[%s] Polling first SMS message\n", PVT_ID(pvt)); + } + ast_mutex_unlock (&pvt->lock); @@ -1424,6 +1431,7 @@ static struct pvt * pvt_create(const pvt_config_t * settings) pvt->timeout = DATA_READ_TIMEOUT; pvt->cusd_use_ucs2_decoding = 1; pvt->gsm_reg_status = -1; + pvt->incoming_sms_index = -1U; ast_copy_string (pvt->provider_name, "NONE", sizeof (pvt->provider_name)); ast_copy_string (pvt->subscriber_number, "Unknown", sizeof (pvt->subscriber_number)); diff --git a/chan_dongle.h b/chan_dongle.h index 7d9f1c97..69b2e7a3 100644 --- a/chan_dongle.h +++ b/chan_dongle.h @@ -24,6 +24,7 @@ #include "cpvt.h" /* struct cpvt */ #include "export.h" /* EXPORT_DECL EXPORT_DEF */ #include "dc_config.h" /* pvt_config_t */ +#include "at_command.h" #define MODULE_DESCRIPTION "Huawei 3G Dongle Channel Driver" #define MAXDONGLEDEVICES 128 @@ -163,6 +164,9 @@ typedef struct pvt char cell_id[8]; char sms_scenter[20]; + unsigned int incoming_sms_index; + unsigned int incoming_sms_inbox[(SMS_INDEX_MAX + 31) / 32]; + volatile unsigned int connected:1; /*!< do we have an connection to a device */ unsigned int initialized:1; /*!< whether a service level connection exists or not */ unsigned int gsm_registered:1; /*!< do we have an registration to a GSM */ From 413cad90774f6ef547abf239e6101129bbe4d20d Mon Sep 17 00:00:00 2001 From: Hans Petter Selasky Date: Mon, 2 Mar 2020 21:54:41 +0100 Subject: [PATCH 077/117] Fix bug in SMS parsing logic. Try to use length field at beginning of TP-DA, instead of assuming fixed length. [dongle0] Error parsing incoming message '+CMGR: 0,,24 06917429000102068E0A917419778290023020020200400230200202004000' at position 70: Good report, but delivery failed Signed-off-by: Hans Petter Selasky --- pdu.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/pdu.c b/pdu.c index bfd98bfd..718d2b0b 100644 --- a/pdu.c +++ b/pdu.c @@ -797,10 +797,12 @@ EXPORT_DEF const char * pdu_parse(char ** pdu, size_t tpdu_length, char * oa, si { 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; + int tp_da_len = pdu_parse_byte(pdu, &pdu_length); + + /* Skip over N-bytes of TP-DA */ + if (reference >= 0 && tp_da_len >= 0 && pdu_length >= (size_t)tp_da_len) { + (*pdu) += tp_da_len; + pdu_length -= tp_da_len; /* Skip over 7 bytes timestamp TP-SCTS */ if (pdu_parse_timestamp(pdu, &pdu_length) >= 0 && /* Skip over 7 bytes timestamp TP-DT */ From 868bbd41fbf2911cdfba483e50c8e99a79d84258 Mon Sep 17 00:00:00 2001 From: Max von Buelow Date: Thu, 27 Feb 2020 23:10:26 +0100 Subject: [PATCH 078/117] Seperated PDU parsing (and building) into hex parsing, actual PDU decoding and UCS-2 decoding; Implemented delivery reports; Fixed USSD generating --- Makefile.in | 13 +- README.md | 2 +- app.c | 31 +- at_command.c | 307 +++++++++------- at_command.h | 11 +- at_parse.c | 186 +++++----- at_parse.h | 6 +- at_queue.c | 17 +- at_queue.h | 3 +- at_response.c | 352 ++++++++++-------- at_response.h | 1 + chan_dongle.c | 46 ++- chan_dongle.h | 4 +- channel.c | 2 +- char_conv.c | 409 ++++++--------------- char_conv.h | 40 +-- cli.c | 86 +---- error.c | 3 + error.h | 51 +++ etc/extensions.conf | 2 +- gsm7_luts.h | 564 ++++++++++++++--------------- helpers.c | 185 +++++----- helpers.h | 13 +- manager.c | 141 ++------ manager.h | 4 +- pdu.c | 859 +++++++++++++++++++------------------------- pdu.h | 38 +- smsdb.c | 545 ++++++++++++++++++++++------ smsdb.h | 11 +- test/gen.c | 127 +++---- test/parse.c | 125 +++---- 31 files changed, 2131 insertions(+), 2053 deletions(-) create mode 100644 error.c create mode 100644 error.h 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); From 71e3206122a36f45f097a712a3c8cb611db365c4 Mon Sep 17 00:00:00 2001 From: Max von Buelow Date: Fri, 15 May 2020 12:47:23 +0200 Subject: [PATCH 079/117] Fixed issue #85 --- at_response.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/at_response.c b/at_response.c index 3f26d2d2..cf3b1445 100644 --- a/at_response.c +++ b/at_response.c @@ -1229,6 +1229,47 @@ static int at_response_cmti (struct pvt* pvt, const char* str) return 0; } +/*! + * \brief Handle +CDSI response + * \param pvt -- pvt structure + * \param str -- string containing response (null terminated) + * \param len -- string lenght + * \retval 0 success + * \retval -1 error + */ + +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 (at_enqueue_retrieve_sms(&pvt->sys_chan, index)) + { + ast_log (LOG_ERROR, "[%s] Error sending CMGR to retrieve SMS message\n", PVT_ID(pvt)); + return -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 * \param pvt -- pvt structure From 6f377e75cec9daad559fca08fdea010dd3edb997 Mon Sep 17 00:00:00 2001 From: Hans Petter Selasky Date: Thu, 28 May 2020 12:07:28 +0200 Subject: [PATCH 080/117] Use incoming_sms_index field as indication of incoming SMS instead of incoming_sms. Signed-off-by: Hans Petter Selasky --- at_command.c | 3 --- chan_dongle.c | 7 +++---- chan_dongle.h | 1 - 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/at_command.c b/at_command.c index 42d15559..d29e7e14 100644 --- a/at_command.c +++ b/at_command.c @@ -711,7 +711,6 @@ EXPORT_DEF void at_retrieve_next_sms(struct cpvt *cpvt) if (i == SMS_INDEX_MAX || at_enqueue_retrieve_sms(cpvt, i) != 0) { - pvt->incoming_sms = 0; pvt_try_restate(pvt); } } @@ -748,7 +747,6 @@ EXPORT_DEF int at_enqueue_retrieve_sms(struct cpvt *cpvt, int index) } pvt->incoming_sms_index = index; - pvt->incoming_sms = 1; err = at_fill_generic_cmd (&cmds[0], "AT+CMGR=%d\r", index); if (err) @@ -761,7 +759,6 @@ EXPORT_DEF int at_enqueue_retrieve_sms(struct cpvt *cpvt, int index) error: ast_log (LOG_WARNING, "[%s] SMS command error %d\n", PVT_ID(pvt), err); pvt->incoming_sms_index = -1U; - pvt->incoming_sms = 0; chan_dongle_err = E_UNKNOWN; return -1; } diff --git a/chan_dongle.c b/chan_dongle.c index 1352c1d3..c215b5a3 100644 --- a/chan_dongle.c +++ b/chan_dongle.c @@ -333,7 +333,6 @@ static void disconnect_dongle (struct pvt* pvt) pvt->ring = 0; pvt->cwaiting = 0; pvt->outgoing_sms = 0; - pvt->incoming_sms = 0; pvt->incoming_sms_index = -1U; pvt->volume_sync_step = VOLUME_SYNC_BEGIN; @@ -1253,7 +1252,7 @@ EXPORT_DEF const char* pvt_str_state(const struct pvt* pvt) state = pvt_call_dir(pvt); else if(PVT_STATE(pvt, chan_count[CALL_STATE_ONHOLD]) > 0) state = "Held"; - else if(pvt->outgoing_sms || pvt->incoming_sms) + else if(pvt->outgoing_sms || pvt->incoming_sms_index != -1U) state = "SMS"; else state = "Free"; @@ -1291,7 +1290,7 @@ EXPORT_DEF struct ast_str* pvt_str_state_ex(const struct pvt* pvt) if(PVT_STATE(pvt, chan_count[CALL_STATE_ONHOLD]) > 0) ast_str_append (&buf, 0, "Held %u ", PVT_STATE(pvt, chan_count[CALL_STATE_ONHOLD])); - if(pvt->incoming_sms) + if(pvt->incoming_sms_index != -1U) ast_str_append (&buf, 0, "Incoming SMS "); if(pvt->outgoing_sms) @@ -1476,7 +1475,7 @@ static int pvt_time4restate(const struct pvt * pvt) { if(pvt->desired_state != pvt->current_state) { - if(pvt->restart_time == RESTATE_TIME_NOW || (PVT_NO_CHANS(pvt) && !pvt->outgoing_sms && !pvt->incoming_sms)) + if(pvt->restart_time == RESTATE_TIME_NOW || (PVT_NO_CHANS(pvt) && !pvt->outgoing_sms && pvt->incoming_sms_index == -1U)) return 1; } return 0; diff --git a/chan_dongle.h b/chan_dongle.h index 1ace9516..f0a017bb 100644 --- a/chan_dongle.h +++ b/chan_dongle.h @@ -172,7 +172,6 @@ typedef struct pvt unsigned int ring:1; /*!< HW state; true if has incoming call from first RING until CEND or CONN */ unsigned int cwaiting:1; /*!< HW state; true if has incoming call waiting from first CCWA until CEND or CONN for */ unsigned int outgoing_sms:1; /*!< outgoing sms */ - unsigned int incoming_sms:1; /*!< incoming sms */ unsigned int volume_sync_step:2; /*!< volume synchronized stage */ #define VOLUME_SYNC_BEGIN 0 #define VOLUME_SYNC_DONE 3 From dd658fb5a6173c488b22a97ede18e47d3bc55502 Mon Sep 17 00:00:00 2001 From: Hans Petter Selasky Date: Thu, 28 May 2020 16:27:17 +0200 Subject: [PATCH 081/117] Fix compile warning. Ensure enum2str() is declared. Signed-off-by: Hans Petter Selasky --- error.h | 1 + 1 file changed, 1 insertion(+) diff --git a/error.h b/error.h index 28637ebc..800da610 100644 --- a/error.h +++ b/error.h @@ -5,6 +5,7 @@ #ifndef ERROR_H_INCLUDED #define ERROR_H_INCLUDED #include "export.h" /* EXPORT_DECL EXPORT_DEF */ +#include "mutils.h" enum error { E_UNKNOWN = 0, From 0b68e298bfb1782d45aa0c17294db2c58946e2f8 Mon Sep 17 00:00:00 2001 From: Hans Petter Selasky Date: Thu, 28 May 2020 16:30:07 +0200 Subject: [PATCH 082/117] Fix uninitialized use of res variable. Signed-off-by: Hans Petter Selasky --- at_response.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/at_response.c b/at_response.c index cf3b1445..a0ee9644 100644 --- a/at_response.c +++ b/at_response.c @@ -1504,6 +1504,8 @@ static int at_response_cusd (struct pvt * pvt, char * str, size_t len) } 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); + } else { + res = -1; } if (res < 0) { return -1; From ea421a5f2ab839e754b47e843cabe9ab1d0b59ed Mon Sep 17 00:00:00 2001 From: Hans Petter Selasky Date: Thu, 28 May 2020 16:37:20 +0200 Subject: [PATCH 083/117] Fix compile warning for clang: Unsequenced modification of i. Signed-off-by: Hans Petter Selasky --- pdu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pdu.c b/pdu.c index 481634b6..8edf4cad 100644 --- a/pdu.c +++ b/pdu.c @@ -882,7 +882,8 @@ EXPORT_DEF int tpdu_parse_deliver(uint8_t *pdu, size_t pdu_length, int tpdu_type chan_dongle_err = E_UNKNOWN; return -1; } - udh->ref = (pdu[i++] << 8) | pdu[i++]; + udh->ref = (pdu[i++] << 8); + udh->ref |= pdu[i++]; udh->parts = pdu[i++]; udh->order = pdu[i++]; udhl -= 4; From d456f2273da8f04aea22431262c0f2d2c068debb Mon Sep 17 00:00:00 2001 From: Hans Petter Selasky Date: Thu, 28 May 2020 16:34:03 +0200 Subject: [PATCH 084/117] Remove unused variables. Signed-off-by: Hans Petter Selasky --- at_command.c | 3 --- chan_dongle.c | 1 - pdu.c | 5 ++--- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/at_command.c b/at_command.c index d29e7e14..b6b2f92e 100644 --- a/at_command.c +++ b/at_command.c @@ -239,7 +239,6 @@ EXPORT_DEF int at_enqueue_cops(struct cpvt *cpvt) /* SMS sending */ 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]; at_queue_cmd_t at_cmd[] = { { CMD_AT_CMGS, RES_SMS_PROMPT, ATQ_CMD_FLAG_DEFAULT, { ATQ_CMD_TIMEOUT_MEDIUM, 0}, NULL, 0 }, @@ -338,7 +337,6 @@ EXPORT_DEF int at_enqueue_ussd(struct cpvt *cpvt, const char *code) ssize_t res; int length; char buf[4096]; - pvt_t* pvt = cpvt->pvt; memcpy (buf, cmd, STRLEN(cmd)); length = STRLEN(cmd); @@ -607,7 +605,6 @@ EXPORT_DEF int at_enqueue_activate(struct cpvt *cpvt) ATQ_CMD_DECLARE_DYN(CMD_AT_CHLD_2x), ATQ_CMD_DECLARE_ST(CMD_AT_CLCC, cmd_clcc), }; - int err; if (cpvt->state == CALL_STATE_ACTIVE) return 0; diff --git a/chan_dongle.c b/chan_dongle.c index c215b5a3..fb92137e 100644 --- a/chan_dongle.c +++ b/chan_dongle.c @@ -966,7 +966,6 @@ 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) { - char * res = ""; struct pvt * pvt = find_device(name); if (pvt) { diff --git a/pdu.c b/pdu.c index 8edf4cad..83c6ee75 100644 --- a/pdu.c +++ b/pdu.c @@ -374,7 +374,7 @@ static int pdu_relative_validity(unsigned minutes) */ static int pdu_store_number(uint8_t* buffer, int toa, const char *number, unsigned length) { - int i = 0, res; + int i = 0; buffer[i++] = toa; int j; @@ -684,7 +684,6 @@ EXPORT_DEF int tpdu_parse_type(uint8_t *pdu, size_t pdu_length, int *type) 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; @@ -709,7 +708,7 @@ EXPORT_DEF int tpdu_parse_status_report(uint8_t *pdu, size_t pdu_length, int *mr } 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; + int i = 0, field_len, oa_digits, pid, dcs, alphabet, udl, udhl, msg_padding = 0; if (i + 1 > pdu_length) { chan_dongle_err = E_UNKNOWN; From 403ecee47729beb1d8b71bb63e14404ef51f74f8 Mon Sep 17 00:00:00 2001 From: Hans Petter Selasky Date: Thu, 28 May 2020 16:32:54 +0200 Subject: [PATCH 085/117] Fix compile warnings. Signed-off-by: Hans Petter Selasky --- at_response.c | 6 +++--- chan_dongle.c | 2 +- char_conv.c | 4 ++-- helpers.c | 2 +- manager.c | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/at_response.c b/at_response.c index a0ee9644..4dda2d20 100644 --- a/at_response.c +++ b/at_response.c @@ -496,7 +496,7 @@ static int at_response_error (struct pvt* pvt, at_res_t res) 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); + ast_verb (3, "[%s] Error payload: %.*s\n", PVT_ID(pvt), (int) payload_len, payload); channel_var_t vars[] = { { "SMS_REPORT_PAYLOAD", payload }, @@ -1326,7 +1326,7 @@ static int at_response_cmgr (struct pvt* pvt, const char * str, size_t len) 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); + ast_verb(1, "[%s] Success: %d; Payload: %.*s; Report string: %s\n", PVT_ID(pvt), success, (int) payload_len, payload, status_report_str); payload[payload_len] = '\0'; channel_var_t vars[] = { @@ -1855,7 +1855,7 @@ int at_response (struct pvt* pvt, const struct iovec iov[2], int iovcnt, at_res_ 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); + ast_verb (3, "[%s] Error payload: %.*s\n", PVT_ID(pvt), (int) payload_len, payload); channel_var_t vars[] = { { "SMS_REPORT_PAYLOAD", payload }, diff --git a/chan_dongle.c b/chan_dongle.c index fb92137e..2d6bfc2b 100644 --- a/chan_dongle.c +++ b/chan_dongle.c @@ -377,7 +377,7 @@ static void handle_expired_reports(struct pvt *pvt) 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); + ast_verb (3, "[%s] TTL payload: %.*s\n", PVT_ID(pvt), (int) payload_len, payload); channel_var_t vars[] = { { "SMS_REPORT_PAYLOAD", payload }, diff --git a/char_conv.c b/char_conv.c index ce1d64ef..64ae0cdc 100644 --- a/char_conv.c +++ b/char_conv.c @@ -80,11 +80,11 @@ EXPORT_DEF ssize_t ucs2_to_utf8(const uint16_t *in, size_t in_length, char *out, } -static char hexchar2val(char h) +static char hexchar2val(unsigned char h) { return lut_hex2val[h]; } -static char val2hexchar(char h) +static char val2hexchar(unsigned char h) { return lut_val2hex[h]; } diff --git a/helpers.c b/helpers.c index d8a5afc3..eb3b3896 100644 --- a/helpers.c +++ b/helpers.c @@ -23,7 +23,7 @@ static int is_valid_ussd_string(const char* number) { for (; *number; number++) { - if (*number >= '0' && *number <= '9' || *number == '*' || *number == '#') { + if ((*number >= '0' && *number <= '9') || *number == '*' || *number == '#') { continue; } return 0; diff --git a/manager.c b/manager.c index 40158270..1b2224da 100644 --- a/manager.c +++ b/manager.c @@ -200,7 +200,7 @@ EXPORT_DEF void manager_event_report(const char * devname, const char *payload, "Type: %d\r\n" "Report: %s\r\n", devname, - payload_len, payload, + (int) payload_len, payload, scts, dt, success, type, From db20b69e85802a2c9e91ddf6cb9c38bc185802f7 Mon Sep 17 00:00:00 2001 From: Hans Petter Selasky Date: Thu, 28 May 2020 16:45:19 +0200 Subject: [PATCH 086/117] Fix variable type. Signed-off-by: Hans Petter Selasky --- at_response.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/at_response.c b/at_response.c index 4dda2d20..8211cd4d 100644 --- a/at_response.c +++ b/at_response.c @@ -1284,7 +1284,7 @@ static int at_response_cmgr (struct pvt* pvt, const char * str, size_t len) char oa[512] = "", sca[512] = ""; char scts[64], dt[64]; int mr, st; - char* msg[4096]; + char msg[4096]; int res; char text_base64[40800]; size_t msg_len; @@ -1358,7 +1358,7 @@ static int at_response_cmgr (struct pvt* pvt, const char * str, size_t len) fullmsg_len = strlen(fullmsg); } else { receive_as_is: - ast_verb (1, "[%s] Got signle SM from %s: '%s'\n", PVT_ID(pvt), oa, msg); + ast_verb (1, "[%s] Got single SM from %s: '%s'\n", PVT_ID(pvt), oa, msg); strncpy(fullmsg, msg, msg_len); fullmsg[msg_len] = '\0'; fullmsg_len = msg_len; From 4426101cdfded982e206f676f3fe4089d9b98e38 Mon Sep 17 00:00:00 2001 From: Hans Petter Selasky Date: Thu, 28 May 2020 16:53:04 +0200 Subject: [PATCH 087/117] Always receive next SMS regardless of error. Signed-off-by: Hans Petter Selasky --- at_response.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/at_response.c b/at_response.c index 8211cd4d..14935771 100644 --- a/at_response.c +++ b/at_response.c @@ -1309,7 +1309,7 @@ static int at_response_cmgr (struct pvt* pvt, const char * str, size_t len) 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; + goto receive_next_no_delete; } switch (PDUTYPE_MTI(tpdu_type)) { case PDUTYPE_MTI_SMS_STATUS_REPORT: From 81064ef4022920eceb24d1de731ece2f65a3cb7b Mon Sep 17 00:00:00 2001 From: Hans Petter Selasky Date: Thu, 28 May 2020 18:09:55 +0200 Subject: [PATCH 088/117] Fix integer to pointer conversion. Signed-off-by: Hans Petter Selasky --- cli.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli.c b/cli.c index 6705647e..34c2750d 100644 --- a/cli.c +++ b/cli.c @@ -413,8 +413,8 @@ static char* cli_cmd (struct ast_cli_entry* e, int cmd, struct ast_cli_args* a) return CLI_SHOWUSAGE; } - msg = send_at_command(a->argv[2], a->argv[3]); - ast_cli (a->fd, "[%s] '%s' %s\n", a->argv[2], a->argv[3], msg); + int res = send_at_command(a->argv[2], a->argv[3]); + ast_cli (a->fd, "[%s] '%s' %s\n", a->argv[2], a->argv[3], res < 0 ? error2str(chan_dongle_err) : "AT command queued"); return CLI_SUCCESS; } From 51106953d0051561a4a0c1dea19137c8332832a7 Mon Sep 17 00:00:00 2001 From: Hans Petter Selasky Date: Thu, 28 May 2020 18:45:10 +0200 Subject: [PATCH 089/117] Fix regression issue after PDU parsing was separated. Signed-off-by: Hans Petter Selasky --- at_queue.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/at_queue.h b/at_queue.h index 5eaea76a..d6f5b873 100644 --- a/at_queue.h +++ b/at_queue.h @@ -79,8 +79,9 @@ typedef struct at_queue_task unsigned cindex; struct cpvt* cpvt; - at_queue_cmd_t cmds[0]; int uid; + + at_queue_cmd_t cmds[0]; /* this field must be last */ } at_queue_task_t; From b8eb6734c5f48a5a125f76a54c1a23002a94f0b8 Mon Sep 17 00:00:00 2001 From: p-velasco <> Date: Wed, 3 Jun 2020 07:18:40 +0200 Subject: [PATCH 090/117] Removed some magic. Sorry, I'm not a wizard --- at_command.h | 183 ++++++++++++++++++-------------------------------- at_response.c | 58 ++-------------- at_response.h | 101 +++++++++++++++++----------- 3 files changed, 132 insertions(+), 210 deletions(-) diff --git a/at_command.h b/at_command.h index f41bb86d..78980e12 100644 --- a/at_command.h +++ b/at_command.h @@ -13,66 +13,71 @@ #define CCWA_CLASS_VOICE 1 #define SMS_INDEX_MAX 256 /* exclusive */ -/* magic order !!! keep order of this values like in at_cmd2str() -*/ -typedef enum { - CMD_USER = 0, - - CMD_AT, - CMD_AT_A, - CMD_AT_CCWA_STATUS, - CMD_AT_CCWA_SET, - CMD_AT_CFUN, - - CMD_AT_CGMI, - CMD_AT_CGMM, - CMD_AT_CGMR, - CMD_AT_CGSN, - - CMD_AT_CHUP, - CMD_AT_CIMI, -// CMD_AT_CLIP, - CMD_AT_CLIR, - - CMD_AT_CLVL, - CMD_AT_CMGD, - CMD_AT_CMGF, - CMD_AT_CMGR, - - CMD_AT_CMGS, - CMD_AT_SMSTEXT, - CMD_AT_CNMI, - CMD_AT_CNUM, +/* AT_COMMANDS_TABLE */ +#define AT_CMD_AS_ENUM(cmd, str) CMD_ ## cmd, +#define AT_CMD_AS_STRING(cmd, str) str, + +#define AT_COMMANDS_TABLE(_) \ + _( USER, "USER'S") \ + _( AT, "AT") \ + _( AT_A, "ATA") \ + _( AT_CCWA_STATUS, "AT+CCWA?") \ + _( AT_CCWA_SET, "AT+CCWA=") \ + _( AT_CFUN, "AT+CFUN") \ +\ + _( AT_CGMI, "AT+CGMI") \ + _( AT_CGMM, "AT+CGMM") \ + _( AT_CGMR, "AT+CGMR") \ + _( AT_CGSN, "AT+CGSN") \ +\ + _( AT_CHUP, "AT+CHUP") \ + _( AT_CIMI, "AT+CIMI") \ +/* _( AT_CLIP, "AT+CLIP") */ \ + _( AT_CLIR, "AT+CLIR") \ +\ + _( AT_CLVL, "AT+CLVL") \ + _( AT_CMGD, "AT+CMGD") \ + _( AT_CMGF, "AT+CMGF") \ + _( AT_CMGR, "AT+CMGR") \ +\ + _( AT_CMGS, "AT+CMGS") \ + _( AT_SMSTEXT, "SMSTEXT") \ + _( AT_CNMI, "AT+CNMI") \ + _( AT_CNUM, "AT+CNUM") \ +\ + _( AT_COPS, "AT+COPS?") \ + _( AT_COPS_INIT, "AT+COPS=") \ + _( AT_CPIN, "AT+CPIN?") \ + _( AT_CPMS, "AT+CPMS") \ +\ + _( AT_CREG, "AT+CREG?") \ + _( AT_CREG_INIT, "AT+CREG=") \ + _( AT_CSCS, "AT+CSCS") \ + _( AT_CSQ, "AT+CSQ") \ +\ + _( AT_CSSN, "AT+CSSN") \ + _( AT_CUSD, "AT+CUSD") \ + _( AT_CVOICE, "AT^CVOICE") \ + _( AT_D, "ATD") \ +\ + _( AT_DDSETEX, "AT^DDSETEX") \ + _( AT_DTMF, "AT^DTMF") \ + _( AT_E, "ATE") \ +\ + _( AT_U2DIAG, "AT^U2DIAG") \ + _( AT_Z, "ATZ") \ + _( AT_CMEE, "AT+CMEE") \ + _( AT_CSCA, "AT+CSCA") \ +\ + _( AT_CHLD_1x, "AT+CHLD=1x") \ + _( AT_CHLD_2x, "AT+CHLD=2x") \ + _( AT_CHLD_2, "AT+CHLD=2") \ + _( AT_CHLD_3, "AT+CHLD=3") \ + _( AT_CLCC, "AT+CLCC") \ +/* AT_COMMANDS_TABLE */ - CMD_AT_COPS, - CMD_AT_COPS_INIT, - CMD_AT_CPIN, - CMD_AT_CPMS, - - CMD_AT_CREG, - CMD_AT_CREG_INIT, - CMD_AT_CSCS, - CMD_AT_CSQ, - - CMD_AT_CSSN, - CMD_AT_CUSD, - CMD_AT_CVOICE, - CMD_AT_D, - - CMD_AT_DDSETEX, - CMD_AT_DTMF, - CMD_AT_E, - - CMD_AT_U2DIAG, - CMD_AT_Z, - CMD_AT_CMEE, - CMD_AT_CSCA, - - CMD_AT_CHLD_1x, - CMD_AT_CHLD_2x, - CMD_AT_CHLD_2, - CMD_AT_CHLD_3, - CMD_AT_CLCC +typedef enum { + AT_COMMANDS_TABLE(AT_CMD_AS_ENUM) } at_cmd_t; /*! @@ -83,70 +88,12 @@ typedef enum { INLINE_DECL const char* at_cmd2str (at_cmd_t cmd) { - /* magic!!! must be in same order as elements of enums in at_cmd_t */ static const char * const cmds[] = { - "USER'S", - - "AT", - "ATA", - "AT+CCWA?", - "AT+CCWA=", - "AT+CFUN", - - "AT+CGMI", - "AT+CGMM", - "AT+CGMR", - "AT+CGSN", - - "AT+CHUP", - "AT+CIMI", -// "AT+CLIP", - "AT+CLIR", - - "AT+CLVL", - "AT+CMGD", - "AT+CMGF", - "AT+CMGR", - - "AT+CMGS", - "SMSTEXT", - "AT+CNMI", - "AT+CNUM", - - "AT+COPS?", - "AT+COPS=", - "AT+CPIN?", - "AT+CPMS", - - "AT+CREG?", - "AT+CREG=", - "AT+CSCS", - "AT+CSQ", - - "AT+CSSN", - "AT+CUSD", - "AT^CVOICE", - "ATD", - - "AT^DDSETEX", - "AT^DTMF", - "ATE", - - "AT^U2DIAG", - "ATZ", - "AT+CMEE", - "AT+CSCA", - - "AT+CHLD=1x", - "AT+CHLD=2x", - "AT+CHLD=2", - "AT+CHLD=3", - "AT+CLCC" + AT_COMMANDS_TABLE(AT_CMD_AS_STRING) }; return enum2str_def(cmd, cmds, ITEMS_OF(cmds), "UNDEFINED"); } - struct cpvt; EXPORT_DECL const char *at_cmd2str(at_cmd_t cmd); diff --git a/at_response.c b/at_response.c index 14935771..24576388 100644 --- a/at_response.c +++ b/at_response.c @@ -28,8 +28,6 @@ #include "smsdb.h" #include "error.h" -#define DEF_STR(str) str,STRLEN(str) - #define CCWA_STATUS_NOT_ACTIVE 0 #define CCWA_STATUS_ACTIVE 1 @@ -37,59 +35,17 @@ #define CLCC_CALL_TYPE_DATA 1 #define CLCC_CALL_TYPE_FAX 2 -/* magic!!! must be in same order as elements of enums in at_res_t */ static const at_response_t at_responses_list[] = { - { RES_PARSE_ERROR,"PARSE ERROR", 0, 0 }, - { RES_UNKNOWN,"UNKNOWN", 0, 0 }, - - { RES_BOOT,"^BOOT",DEF_STR("^BOOT:") }, - { RES_BUSY,"BUSY",DEF_STR("BUSY\r") }, - { RES_CEND,"^CEND",DEF_STR("^CEND:") }, - - { 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:") }, - { RES_CONN,"^CONN",DEF_STR("^CONN:") }, - { RES_COPS,"+COPS",DEF_STR("+COPS:") }, - { RES_CPIN,"+CPIN",DEF_STR("+CPIN:") }, - - { RES_CREG,"+CREG",DEF_STR("+CREG:") }, - { RES_CSQ,"+CSQ",DEF_STR("+CSQ:") }, - { RES_CSSI,"+CSSI",DEF_STR("+CSSI:") }, - { RES_CSSU,"+CSSU",DEF_STR("+CSSU:") }, - - { RES_CUSD,"+CUSD",DEF_STR("+CUSD:") }, - { RES_ERROR,"ERROR",DEF_STR("ERROR\r") }, /* and "COMMAND NOT SUPPORT\r" */ - { RES_MODE,"^MODE",DEF_STR("^MODE:") }, - { RES_NO_CARRIER,"NO CARRIER",DEF_STR("NO CARRIER\r") }, - - { RES_NO_DIALTONE,"NO DIALTONE",DEF_STR("NO DIALTONE\r") }, - { RES_OK,"OK",DEF_STR("OK\r") }, - { RES_ORIG,"^ORIG",DEF_STR("^ORIG:") }, - { RES_RING,"RING",DEF_STR("RING\r") }, - - { RES_RSSI,"^RSSI",DEF_STR("^RSSI:") }, - { RES_SMMEMFULL,"^SMMEMFULL",DEF_STR("^SMMEMFULL:") }, - { RES_SMS_PROMPT,"> ",DEF_STR("> ") }, - { RES_SRVST,"^SRVST",DEF_STR("^SRVST:") }, - - { RES_CVOICE,"^CVOICE",DEF_STR("^CVOICE:") }, - { RES_CMGS,"+CMGS",DEF_STR("+CMGS:") }, - { RES_CPMS,"+CPMS",DEF_STR("+CPMS:") }, - { RES_CSCA,"+CSCA",DEF_STR("+CSCA:") }, - - { RES_CLCC,"+CLCC", DEF_STR("+CLCC:") }, - { RES_CCWA,"+CCWA", DEF_STR("+CCWA:") }, - - /* duplicated response undef other id */ + + AT_RESPONSES_TABLE(AT_RES_AS_STRUCTLIST) + + /* The hackish way to define the duplicated responses in the meantime */ +#define DEF_STR(str) str,STRLEN(str) { RES_CNUM, "+CNUM",DEF_STR("ERROR+CNUM:") }, { RES_ERROR,"ERROR",DEF_STR("COMMAND NOT SUPPORT\r") }, - }; #undef DEF_STR + }; + EXPORT_DEF const at_responses_t at_responses = { at_responses_list, 2, ITEMS_OF(at_responses_list), RES_MIN, RES_MAX}; diff --git a/at_response.h b/at_response.h index 93c019b0..b234d73f 100644 --- a/at_response.h +++ b/at_response.h @@ -9,53 +9,72 @@ struct pvt; struct iovec; -/* magic order!!! keep this enum order same as in at_responses_list */ -typedef enum { - RES_PARSE_ERROR = -1, - RES_MIN = RES_PARSE_ERROR, - RES_UNKNOWN = 0, - - RES_BOOT, - RES_BUSY, - RES_CEND, +/* AT_RESPONSES_TABLE */ +#define AT_RES_AS_ENUM(res, desc, str) RES_ ## res, +#define AT_RES_AS_STRUCTLIST(res, desc, str) {RES_ ## res, desc, str, (sizeof(str)-1)}, - RES_CMGR, - RES_CMS_ERROR, - RES_CMTI, - RES_CDSI, - RES_CNUM, +#define AT_RESPONSES_TABLE(_) \ + _( PARSE_ERROR, "PARSE ERROR", "") \ + _( UNKNOWN, "UNKNOWN", "") \ +\ + _( BOOT, "^BOOT", "^BOOT:") \ + _( BUSY, "BUSY", "BUSY\r") \ + _( CEND, "^CEND", "^CEND:") \ +\ + _( CMGR, "+CMGR", "+CMGR:") \ + _( CMS_ERROR, "+CMS ERROR", "+CMS ERROR:") \ + _( CMTI, "+CMTI", "+CMTI:") \ + _( CDSI, "+CDSI", "+CDSI:") \ +\ + _( CNUM, "+CNUM", "+CNUM:") \ + /* and "ERROR+CNUM:", hacked later on */ \ +\ + _( CONF, "^CONF", "^CONF:") \ + _( CONN, "^CONN", "^CONN:") \ + _( COPS, "+COPS", "+COPS:") \ + _( CPIN, "+CPIN", "+CPIN:") \ +\ + _( CREG, "+CREG", "+CREG:") \ + _( CSQ, "+CSQ", "+CSQ:") \ + _( CSSI, "+CSSI", "+CSSI:") \ + _( CSSU, "+CSSU", "+CSSU:") \ +\ + _( CUSD, "+CUSD", "+CUSD:") \ + _( ERROR, "ERROR", "ERROR\r") \ + /* and "COMMAND NOT SUPPORT\r", hacked later on */ \ +\ + _( MODE, "^MODE", "^MODE:") \ + _( NO_CARRIER, "NO CARRIER", "NO CARRIER\r") \ +\ + _( NO_DIALTONE, "NO DIALTONE", "NO DIALTONE\r") \ + _( OK, "OK", "OK\r") \ + _( ORIG, "^ORIG", "^ORIG:") \ + _( RING, "RING", "RING\r") \ +\ + _( RSSI, "^RSSI", "^RSSI:") \ + _( SMMEMFULL, "^SMMEMFULL", "^SMMEMFULL:") \ + _( SMS_PROMPT, "> ", "> ") \ + _( SRVST, "^SRVST", "^SRVST:") \ +\ + _( CVOICE, "^CVOICE", "^CVOICE:") \ + _( CMGS, "+CMGS", "+CMGS:") \ + _( CPMS, "+CPMS", "+CPMS:") \ + _( CSCA, "+CSCA", "+CSCA:") \ +\ + _( CLCC, "+CLCC", "+CLCC:") \ + _( CCWA, "+CCWA", "+CCWA:") \ +/* AT_RESPONSES_TABLE */ - RES_CONF, - RES_CONN, - RES_COPS, - RES_CPIN, - RES_CREG, - RES_CSQ, - RES_CSSI, - RES_CSSU, - - RES_CUSD, - RES_ERROR, - RES_MODE, - RES_NO_CARRIER, +typedef enum { - RES_NO_DIALTONE, - RES_OK, - RES_ORIG, - RES_RING, + /* Hackish way to force RES_PARSE_ERROR = -1 for compatibility */ + COMPATIBILITY_RES_START_AT_MINUSONE = -2, - RES_RSSI, - RES_SMMEMFULL, - RES_SMS_PROMPT, - RES_SRVST, + AT_RESPONSES_TABLE(AT_RES_AS_ENUM) - RES_CVOICE, - RES_CMGS, - RES_CPMS, - RES_CSCA, - RES_CLCC, - RES_CCWA, + /* Hackish way to maintain MAX and MIN responses for compatibility */ + RES_MIN = RES_PARSE_ERROR, RES_MAX = RES_CCWA, } at_res_t; From fe26c84f57a8490ca87d922b64b7b126c9a35b4e Mon Sep 17 00:00:00 2001 From: Damjan Janevski Date: Wed, 17 Jun 2020 00:19:57 +0200 Subject: [PATCH 091/117] Use Asterisk function `ast_unescape_c` on SMS text when sending via AMI This change was warranted because AMI is a text based protocol and the newline can't be included in the payload. Standard backslash escape sequences are used, e.g. '\n' for newline and '\t' for tabulator characters. --- manager.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/manager.c b/manager.c index 1b2224da..f0ddf407 100644 --- a/manager.c +++ b/manager.c @@ -156,7 +156,7 @@ static int manager_send_sms (struct mansession* s, const struct message* m) { const char* device = astman_get_header (m, "Device"); const char* number = astman_get_header (m, "Number"); - const char* message = astman_get_header (m, "Message"); + const char* message = astman_get_header (m, "Message"); /* may contain C-escapes */ const char* validity= astman_get_header (m, "Validity"); const char* report = astman_get_header (m, "Report"); const char* payload = astman_get_header (m, "Payload"); @@ -181,7 +181,17 @@ static int manager_send_sms (struct mansession* s, const struct message* m) return 0; } - int res = send_sms(device, number, message, validity, report, payload, strlen(payload) + 1); + char* unescaped_msg = ast_strdup(message); + + if (unescaped_msg == NULL) + { + astman_send_error (s, m, "Internal memory error"); + return 0; + } + + ast_unescape_c(unescaped_msg); + int res = send_sms(device, number, unescaped_msg, validity, report, payload, strlen(payload) + 1); + ast_free(unescaped_msg); 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); @@ -581,7 +591,7 @@ static const struct dongle_manager " 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 sent.\n" - " *Message: The SMS message that will be sent.\n" + " *Message: The SMS message that will be sent (standard backslash escape sequences are used, e.g. '\\n' for newline).\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" From fd04f638877a8ffe53ea9e17250499a0eecb071d Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Fri, 5 Jun 2020 13:30:19 +0200 Subject: [PATCH 092/117] Add guard against NULL args.payload NULL args.payload causes a crash in the call to strlen(args.payload) while computing arguments for send_sms. --- app.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app.c b/app.c index 5f18b37c..99738b13 100644 --- a/app.c +++ b/app.c @@ -106,6 +106,12 @@ static int app_send_sms_exec (attribute_unused struct ast_channel* channel, cons return -1; } + if (ast_strlen_zero(args.payload)) + { + ast_log(LOG_ERROR, "NULL payload for message -- SMS will not be sent\n"); + return -1; + } + 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; From e700abcc5edb1c829ec33f1046de5bb841b24cc9 Mon Sep 17 00:00:00 2001 From: gituser Date: Wed, 24 Jun 2020 17:05:54 +0700 Subject: [PATCH 093/117] fix ucs-2 empty sms bug --- at_response.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/at_response.c b/at_response.c index 14935771..64bff358 100644 --- a/at_response.c +++ b/at_response.c @@ -1485,9 +1485,9 @@ static int at_response_cusd (struct pvt * pvt, char * str, size_t len) dcs = 0; } - 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 + uint16_t out_ucs2[1024]; 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) { @@ -1503,7 +1503,7 @@ static int at_response_cusd (struct pvt * pvt, char * str, size_t len) } } 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); + res = ucs2_to_utf8(cusd, (cusd_nibbles + 1) / 4, cusd_utf8_str, sizeof(cusd_utf8_str) - 1); } else { res = -1; } From 4a15ac8725f6b3007e0ff2c8edea2c3867ebd8cd Mon Sep 17 00:00:00 2001 From: gituser Date: Sat, 4 Jul 2020 14:24:56 +0700 Subject: [PATCH 094/117] Better type safe sms inbox --- at_command.c | 11 ++++------- chan_dongle.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ chan_dongle.h | 11 ++++++++++- 3 files changed, 58 insertions(+), 8 deletions(-) diff --git a/at_command.c b/at_command.c index b6b2f92e..3e419c98 100644 --- a/at_command.c +++ b/at_command.c @@ -695,13 +695,13 @@ EXPORT_DEF void at_retrieve_next_sms(struct cpvt *cpvt) pvt->incoming_sms_index = -1U; /* clear this message index from inbox */ - pvt->incoming_sms_inbox[i / 32] &= ~(1U << (i % 32)); + sms_inbox_clear(pvt, i); } /* get next message to fetch from inbox */ for (i = 0; i != SMS_INDEX_MAX; i++) { - if (pvt->incoming_sms_inbox[i / 32] & (1U << (i % 32))) + if (is_sms_inbox_set(pvt, i)) break; } @@ -727,15 +727,12 @@ EXPORT_DEF int at_enqueue_retrieve_sms(struct cpvt *cpvt, int index) }; unsigned cmdsno = ITEMS_OF(cmds); - if (index < 0 || index >= SMS_INDEX_MAX) { - ast_log (LOG_WARNING, "[%s] SMS index [%d] too big\n", PVT_ID(pvt), index); + /* set that we want to receive this message */ + if (!sms_inbox_set(pvt, index)) { chan_dongle_err = E_UNKNOWN; return -1; } - /* set that we want to receive this message */ - pvt->incoming_sms_inbox[index / 32] |= 1U << (index % 32); - /* check if message is already being received */ if (pvt->incoming_sms_index != -1U) { ast_debug (4, "[%s] SMS retrieve of [%d] already in progress\n", diff --git a/chan_dongle.c b/chan_dongle.c index 2d6bfc2b..1e69613f 100644 --- a/chan_dongle.c +++ b/chan_dongle.c @@ -349,6 +349,50 @@ static void disconnect_dongle (struct pvt* pvt) manager_event_device_status(PVT_ID(pvt), "Disconnect"); } +#define SMS_INBOX_BIT(index) ((sms_inbox_item_type)(1) << (index % SMS_INBOX_ITEM_BITS)) +#define SMS_INBOX_INDEX(index) (index / SMS_INBOX_ITEM_BITS) + +static int is_sms_inbox_index_valid(const struct pvt* pvt, int index) +{ + if (index < 0 || index >= (int) SMS_INDEX_MAX) { + ast_log(LOG_WARNING, "[%s] SMS index [%d] out of range\n", PVT_ID(pvt), index); + return 0; + } + + return 1; +} + +EXPORT_DEF int sms_inbox_set(struct pvt* pvt, int index) +{ + if (!is_sms_inbox_index_valid(pvt, index)) { + return 0; + } + + pvt->incoming_sms_inbox[SMS_INBOX_INDEX(index)] |= SMS_INBOX_BIT(index); + return 1; +} + +EXPORT_DEF int sms_inbox_clear(struct pvt* pvt, int index) +{ + if (!is_sms_inbox_index_valid(pvt, index)) { + return 0; + } + + pvt->incoming_sms_inbox[SMS_INBOX_INDEX(index)] &= ~SMS_INBOX_BIT(index); + return 1; +} + +EXPORT_DEF int is_sms_inbox_set(const struct pvt* pvt, int index) +{ + if (!is_sms_inbox_index_valid(pvt, index)) { + return 0; + } + + return pvt->incoming_sms_inbox[SMS_INBOX_INDEX(index)] & SMS_INBOX_BIT(index); +} + +#undef SMS_INBOX_INDEX +#undef SMS_INBOX_BIT /* anybody wrote some to device before me, and not read results, clean pending results here */ #/* */ diff --git a/chan_dongle.h b/chan_dongle.h index f0a017bb..20f87735 100644 --- a/chan_dongle.h +++ b/chan_dongle.h @@ -102,6 +102,11 @@ typedef struct pvt_stat struct at_queue_task; +typedef unsigned int sms_inbox_item_type; + +#define SMS_INBOX_ITEM_BITS (sizeof(sms_inbox_item_type) * 8) +#define SMS_INBOX_ARRAY_SIZE ((SMS_INDEX_MAX + SMS_INBOX_ITEM_BITS - 1) / SMS_INBOX_ITEM_BITS) + typedef struct pvt { AST_LIST_ENTRY (pvt) entry; /*!< linked list pointers */ @@ -163,7 +168,7 @@ typedef struct pvt char sms_scenter[20]; unsigned int incoming_sms_index; - unsigned int incoming_sms_inbox[(SMS_INDEX_MAX + 31) / 32]; + sms_inbox_item_type incoming_sms_inbox[SMS_INBOX_ARRAY_SIZE]; volatile unsigned int connected:1; /*!< do we have an connection to a device */ unsigned int initialized:1; /*!< whether a service level connection exists or not */ @@ -222,6 +227,10 @@ typedef struct public_state EXPORT_DECL public_state_t * gpublic; +EXPORT_DEF int sms_inbox_set(struct pvt* pvt, int index); +EXPORT_DEF int sms_inbox_clear(struct pvt* pvt, int index); +EXPORT_DEF int is_sms_inbox_set(const struct pvt* pvt, int index); + EXPORT_DECL void clean_read_data(const char * devname, int fd); EXPORT_DECL int pvt_get_pseudo_call_idx(const struct pvt * pvt); EXPORT_DECL int ready4voice_call(const struct pvt* pvt, const struct cpvt * current_cpvt, int opts); From 4652c90431e51722ee027e1444480e43d420594f Mon Sep 17 00:00:00 2001 From: gituser Date: Sat, 4 Jul 2020 15:07:36 +0700 Subject: [PATCH 095/117] Suppress errors on initial sms poll --- at_command.c | 10 ++++-- at_command.h | 9 +++-- at_queue.h | 12 +++++-- at_response.c | 92 +++++++++++++++++++++++++++++++-------------------- 4 files changed, 79 insertions(+), 44 deletions(-) diff --git a/at_command.c b/at_command.c index 3e419c98..4882d827 100644 --- a/at_command.c +++ b/at_command.c @@ -683,7 +683,7 @@ EXPORT_DEF int at_enqueue_user_cmd(struct cpvt *cpvt, const char *input) * \brief Start reading next SMS, if any * \param cpvt -- cpvt structure */ -EXPORT_DEF void at_retrieve_next_sms(struct cpvt *cpvt) +EXPORT_DEF void at_retrieve_next_sms(struct cpvt *cpvt, at_cmd_suppress_error_t suppress_error) { pvt_t *pvt = cpvt->pvt; unsigned int i; @@ -706,7 +706,7 @@ EXPORT_DEF void at_retrieve_next_sms(struct cpvt *cpvt) } if (i == SMS_INDEX_MAX || - at_enqueue_retrieve_sms(cpvt, i) != 0) + at_enqueue_retrieve_sms(cpvt, i, suppress_error) != 0) { pvt_try_restate(pvt); } @@ -718,7 +718,7 @@ EXPORT_DEF void at_retrieve_next_sms(struct cpvt *cpvt) * \param index -- index of message in store * \return 0 on success */ -EXPORT_DEF int at_enqueue_retrieve_sms(struct cpvt *cpvt, int index) +EXPORT_DEF int at_enqueue_retrieve_sms(struct cpvt *cpvt, int index, at_cmd_suppress_error_t suppress_error) { pvt_t *pvt = cpvt->pvt; int err; @@ -727,6 +727,10 @@ EXPORT_DEF int at_enqueue_retrieve_sms(struct cpvt *cpvt, int index) }; unsigned cmdsno = ITEMS_OF(cmds); + if (suppress_error == SUPPRESS_ERROR_ENABLED) { + cmds[0].flags |= ATQ_CMD_FLAG_SUPPRESS_ERROR; + } + /* set that we want to receive this message */ if (!sms_inbox_set(pvt, index)) { chan_dongle_err = E_UNKNOWN; diff --git a/at_command.h b/at_command.h index 78980e12..32535d65 100644 --- a/at_command.h +++ b/at_command.h @@ -80,6 +80,11 @@ typedef enum { AT_COMMANDS_TABLE(AT_CMD_AS_ENUM) } at_cmd_t; +typedef enum { + SUPPRESS_ERROR_DISABLED, + SUPPRESS_ERROR_ENABLED +} at_cmd_suppress_error_t; + /*! * \brief Get the string representation of the given AT command * \param cmd -- the command to process @@ -108,8 +113,8 @@ 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); -EXPORT_DECL void at_retrieve_next_sms(struct cpvt *cpvt); -EXPORT_DECL int at_enqueue_retrieve_sms(struct cpvt *cpvt, int index); +EXPORT_DECL void at_retrieve_next_sms(struct cpvt *cpvt, at_cmd_suppress_error_t suppress_error); +EXPORT_DECL int at_enqueue_retrieve_sms(struct cpvt *cpvt, int index, at_cmd_suppress_error_t suppress_error); EXPORT_DECL int at_enqueue_delete_sms(struct cpvt *cpvt, int index); 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_queue.h b/at_queue.h index d6f5b873..2ad4e82a 100644 --- a/at_queue.h +++ b/at_queue.h @@ -20,9 +20,10 @@ typedef struct at_queue_cmd at_res_t res; /*!< expected response code, can be RES_OK, RES_CMGR, RES_SMS_PROMPT */ unsigned flags; /*!< flags */ -#define ATQ_CMD_FLAG_DEFAULT 0x00 /*!< empty flags */ -#define ATQ_CMD_FLAG_STATIC 0x01 /*!< data is static no try deallocate */ -#define ATQ_CMD_FLAG_IGNORE 0x02 /*!< ignore response non match condition */ +#define ATQ_CMD_FLAG_DEFAULT 0x00 /*!< empty flags */ +#define ATQ_CMD_FLAG_STATIC 0x01 /*!< data is static no try deallocate */ +#define ATQ_CMD_FLAG_IGNORE 0x02 /*!< ignore response non match condition */ +#define ATQ_CMD_FLAG_SUPPRESS_ERROR 0x04 /*!< don't print error message if command fails */ struct timeval timeout; /*!< timeout value, started at time when command actually written on device */ #define ATQ_CMD_TIMEOUT_SHORT 1 /*!< timeout value 1 sec */ @@ -95,6 +96,11 @@ EXPORT_DECL const at_queue_cmd_t * at_queue_head_cmd(const struct pvt * pvt); EXPORT_DECL int at_queue_timeout(const struct pvt * pvt); EXPORT_DECL int at_queue_run (struct pvt * pvt); +INLINE_DECL at_cmd_suppress_error_t at_cmd_suppress_error_mode(int flags) +{ + return ((flags & ATQ_CMD_FLAG_SUPPRESS_ERROR) ? SUPPRESS_ERROR_ENABLED : SUPPRESS_ERROR_DISABLED); +} + static inline const at_queue_cmd_t * at_queue_task_cmd (const at_queue_task_t * task) { return task ? &task->cmds[task->cindex] : NULL; diff --git a/at_response.c b/at_response.c index 193d15ac..daf08f18 100644 --- a/at_response.c +++ b/at_response.c @@ -230,7 +230,7 @@ static int at_response_ok (struct pvt* pvt, at_res_t res) case CMD_AT_CMGR: ast_debug (1, "[%s] SMS message see later\n", PVT_ID(pvt)); - at_retrieve_next_sms(&pvt->sys_chan); + at_retrieve_next_sms(&pvt->sys_chan, at_cmd_suppress_error_mode(ecmd->flags)); break; case CMD_AT_CMGD: @@ -265,6 +265,26 @@ static int at_response_ok (struct pvt* pvt, at_res_t res) return 0; } +static void log_cmd_response_error(const struct pvt* pvt, const at_queue_cmd_t *ecmd, const char *fmt, ...) +{ + va_list ap; + + if (at_cmd_suppress_error_mode(ecmd->flags) == SUPPRESS_ERROR_ENABLED) { + if (DEBUG_ATLEAST(1)) { + ast_log(AST_LOG_DEBUG, "[%s] Command response error suppressed:\n", PVT_ID(pvt)); + va_start(ap, fmt); + ast_log_ap(AST_LOG_DEBUG, fmt, ap); + va_end(ap); + } + + return; + } + + va_start(ap, fmt); + ast_log_ap(LOG_ERROR, fmt, ap); + va_end(ap); +} + /*! * \brief Handle ERROR response * \param pvt -- pvt structure @@ -286,7 +306,7 @@ static int at_response_error (struct pvt* pvt, at_res_t res) case CMD_AT_Z: case CMD_AT_E: case CMD_AT_CLCC: - ast_log (LOG_ERROR, "[%s] Command '%s' failed\n", PVT_ID(pvt), at_cmd2str (ecmd->cmd)); + log_cmd_response_error(pvt, ecmd, "[%s] Command '%s' failed\n", PVT_ID(pvt), at_cmd2str (ecmd->cmd)); /* mean disconnected from device */ goto e_return; @@ -295,44 +315,44 @@ static int at_response_error (struct pvt* pvt, at_res_t res) case CMD_AT_CCWA_SET: case CMD_AT_CCWA_STATUS: case CMD_AT_CNUM: - ast_log (LOG_ERROR, "[%s] Command '%s' failed\n", PVT_ID(pvt), at_cmd2str (ecmd->cmd)); + log_cmd_response_error(pvt, ecmd, "[%s] Command '%s' failed\n", PVT_ID(pvt), at_cmd2str (ecmd->cmd)); /* mean ignore error */ break; case CMD_AT_CGMI: - ast_log (LOG_ERROR, "[%s] Getting manufacturer info failed\n", PVT_ID(pvt)); + log_cmd_response_error(pvt, ecmd, "[%s] Getting manufacturer info failed\n", PVT_ID(pvt)); goto e_return; case CMD_AT_CGMM: - ast_log (LOG_ERROR, "[%s] Getting model info failed\n", PVT_ID(pvt)); + log_cmd_response_error(pvt, ecmd, "[%s] Getting model info failed\n", PVT_ID(pvt)); goto e_return; case CMD_AT_CGMR: - ast_log (LOG_ERROR, "[%s] Getting firmware info failed\n", PVT_ID(pvt)); + log_cmd_response_error(pvt, ecmd, "[%s] Getting firmware info failed\n", PVT_ID(pvt)); goto e_return; case CMD_AT_CMEE: - ast_log (LOG_ERROR, "[%s] Setting error verbosity level failed\n", PVT_ID(pvt)); + log_cmd_response_error(pvt, ecmd, "[%s] Setting error verbosity level failed\n", PVT_ID(pvt)); goto e_return; case CMD_AT_CGSN: - ast_log (LOG_ERROR, "[%s] Getting IMEI number failed\n", PVT_ID(pvt)); + log_cmd_response_error(pvt, ecmd, "[%s] Getting IMEI number failed\n", PVT_ID(pvt)); goto e_return; case CMD_AT_CIMI: - ast_log (LOG_ERROR, "[%s] Getting IMSI number failed\n", PVT_ID(pvt)); + log_cmd_response_error(pvt, ecmd, "[%s] Getting IMSI number failed\n", PVT_ID(pvt)); goto e_return; case CMD_AT_CPIN: - ast_log (LOG_ERROR, "[%s] Error checking PIN state\n", PVT_ID(pvt)); + log_cmd_response_error(pvt, ecmd, "[%s] Error checking PIN state\n", PVT_ID(pvt)); goto e_return; case CMD_AT_COPS_INIT: - ast_log (LOG_ERROR, "[%s] Error setting operator select parameters\n", PVT_ID(pvt)); + log_cmd_response_error(pvt, ecmd, "[%s] Error setting operator select parameters\n", PVT_ID(pvt)); goto e_return; case CMD_AT_CREG_INIT: - ast_log (LOG_ERROR, "[%s] Error enabling registration info\n", PVT_ID(pvt)); + log_cmd_response_error(pvt, ecmd, "[%s] Error enabling registration info\n", PVT_ID(pvt)); goto e_return; case CMD_AT_CREG: @@ -350,18 +370,18 @@ static int at_response_error (struct pvt* pvt, at_res_t res) /* continue initialization in other job at cmd CMD_AT_CMGF */ if (at_enqueue_initialization(task->cpvt, CMD_AT_CMGF)) { - ast_log (LOG_ERROR, "[%s] Error schedule initialization commands\n", PVT_ID(pvt)); + log_cmd_response_error(pvt, ecmd, "[%s] Error schedule initialization commands\n", PVT_ID(pvt)); goto e_return; } } break; /* case CMD_AT_CLIP: - ast_log (LOG_ERROR, "[%s] Error enabling calling line indication\n", PVT_ID(pvt)); + log_cmd_response_error(pvt, ecmd, "[%s] Error enabling calling line indication\n", PVT_ID(pvt)); goto e_return; */ case CMD_AT_CSSN: - ast_log (LOG_ERROR, "[%s] Error Supplementary Service Notification activation failed\n", PVT_ID(pvt)); + log_cmd_response_error(pvt, ecmd, "[%s] Error Supplementary Service Notification activation failed\n", PVT_ID(pvt)); goto e_return; case CMD_AT_CMGF: @@ -379,7 +399,7 @@ static int at_response_error (struct pvt* pvt, at_res_t res) /* continue initialization in other job at cmd CMD_AT_CSQ */ if (at_enqueue_initialization(task->cpvt, CMD_AT_CSQ)) { - ast_log (LOG_ERROR, "[%s] Error querying signal strength\n", PVT_ID(pvt)); + log_cmd_response_error(pvt, ecmd, "[%s] Error querying signal strength\n", PVT_ID(pvt)); goto e_return; } @@ -400,17 +420,17 @@ static int at_response_error (struct pvt* pvt, at_res_t res) case CMD_AT_A: case CMD_AT_CHLD_2x: - ast_log (LOG_ERROR, "[%s] Answer failed for call idx %d\n", PVT_ID(pvt), task->cpvt->call_idx); + log_cmd_response_error(pvt, ecmd, "[%s] Answer failed for call idx %d\n", PVT_ID(pvt), task->cpvt->call_idx); queue_hangup (task->cpvt->channel, 0); break; case CMD_AT_CHLD_3: - ast_log (LOG_ERROR, "[%s] Can't begin conference call idx %d\n", PVT_ID(pvt), task->cpvt->call_idx); + log_cmd_response_error(pvt, ecmd, "[%s] Can't begin conference call idx %d\n", PVT_ID(pvt), task->cpvt->call_idx); queue_hangup(task->cpvt->channel, 0); break; case CMD_AT_CLIR: - ast_log (LOG_ERROR, "[%s] Setting CLIR failed\n", PVT_ID(pvt)); + log_cmd_response_error(pvt, ecmd, "[%s] Setting CLIR failed\n", PVT_ID(pvt)); break; case CMD_AT_CHLD_2: @@ -420,26 +440,26 @@ static int at_response_error (struct pvt* pvt, at_res_t res) } /* fall through */ case CMD_AT_D: - ast_log (LOG_ERROR, "[%s] Dial failed\n", PVT_ID(pvt)); + log_cmd_response_error(pvt, ecmd, "[%s] Dial failed\n", PVT_ID(pvt)); queue_control_channel (task->cpvt, AST_CONTROL_CONGESTION); break; case CMD_AT_DDSETEX: - ast_log (LOG_ERROR, "[%s] AT^DDSETEX failed\n", PVT_ID(pvt)); + log_cmd_response_error(pvt, ecmd, "[%s] AT^DDSETEX failed\n", PVT_ID(pvt)); break; case CMD_AT_CHUP: case CMD_AT_CHLD_1x: - ast_log (LOG_ERROR, "[%s] Error sending hangup for call idx %d\n", PVT_ID(pvt), task->cpvt->call_idx); + log_cmd_response_error(pvt, ecmd, "[%s] Error sending hangup for call idx %d\n", PVT_ID(pvt), task->cpvt->call_idx); break; case CMD_AT_CMGR: - ast_log (LOG_ERROR, "[%s] Error reading SMS message\n", PVT_ID(pvt)); - at_retrieve_next_sms(&pvt->sys_chan); + log_cmd_response_error(pvt, ecmd, "[%s] Error reading SMS message\n", PVT_ID(pvt)); + at_retrieve_next_sms(&pvt->sys_chan, at_cmd_suppress_error_mode(ecmd->flags)); break; case CMD_AT_CMGD: - ast_log (LOG_ERROR, "[%s] Error deleting SMS message\n", PVT_ID(pvt)); + log_cmd_response_error(pvt, ecmd, "[%s] Error deleting SMS message\n", PVT_ID(pvt)); break; case CMD_AT_CMGS: @@ -469,11 +489,11 @@ static int at_response_error (struct pvt* pvt, at_res_t res) } ast_verb (3, "[%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)); + log_cmd_response_error(pvt, ecmd, "[%s] Error sending SMS message %p %s\n", PVT_ID(pvt), task, at_cmd2str (ecmd->cmd)); break; case CMD_AT_DTMF: - ast_log (LOG_ERROR, "[%s] Error sending DTMF\n", PVT_ID(pvt)); + log_cmd_response_error(pvt, ecmd, "[%s] Error sending DTMF\n", PVT_ID(pvt)); break; case CMD_AT_COPS: @@ -487,11 +507,11 @@ static int at_response_error (struct pvt* pvt, at_res_t res) case CMD_AT_CUSD: 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); + log_cmd_response_error(pvt, ecmd, "[%s] Error sending USSD %p\n", PVT_ID(pvt), task); break; default: - ast_log (LOG_ERROR, "[%s] Received 'ERROR' for unhandled command '%s'\n", PVT_ID(pvt), at_cmd2str (ecmd->cmd)); + log_cmd_response_error(pvt, ecmd, "[%s] Received 'ERROR' for unhandled command '%s'\n", PVT_ID(pvt), at_cmd2str (ecmd->cmd)); break; } at_queue_handle_result (pvt, res); @@ -500,16 +520,16 @@ static int at_response_error (struct pvt* pvt, at_res_t res) { switch (ecmd->cmd) { case CMD_AT_CMGR: - at_retrieve_next_sms(&pvt->sys_chan); + at_retrieve_next_sms(&pvt->sys_chan, at_cmd_suppress_error_mode(ecmd->flags)); break; default: - ast_log (LOG_ERROR, "[%s] Received 'ERROR' when expecting '%s', ignoring\n", PVT_ID(pvt), at_res2str (ecmd->res)); + log_cmd_response_error(pvt, ecmd, "[%s] Received 'ERROR' when expecting '%s', ignoring\n", PVT_ID(pvt), at_res2str (ecmd->res)); break; } } else { - ast_log (LOG_ERROR, "[%s] Received unexpected 'ERROR'\n", PVT_ID(pvt)); + log_cmd_response_error(pvt, ecmd, "[%s] Received unexpected 'ERROR'\n", PVT_ID(pvt)); } return 0; @@ -1130,7 +1150,7 @@ at_poll_sms (struct pvt *pvt) for (i = 0; i != SMS_INDEX_MAX; i++) { - if (at_enqueue_retrieve_sms(&pvt->sys_chan, i)) + if (at_enqueue_retrieve_sms(&pvt->sys_chan, i, SUPPRESS_ERROR_ENABLED)) { ast_log (LOG_ERROR, "[%s] Error sending CMGR to retrieve SMS message #%d\n", PVT_ID(pvt), i); return -1; @@ -1168,7 +1188,7 @@ static int at_response_cmti (struct pvt* pvt, const char* str) { ast_debug (1, "[%s] Incoming SMS message\n", PVT_ID(pvt)); - if (at_enqueue_retrieve_sms(&pvt->sys_chan, index)) + if (at_enqueue_retrieve_sms(&pvt->sys_chan, index, SUPPRESS_ERROR_DISABLED)) { ast_log (LOG_ERROR, "[%s] Error sending CMGR to retrieve SMS message\n", PVT_ID(pvt)); return -1; @@ -1209,7 +1229,7 @@ static int at_response_cdsi (struct pvt* pvt, const char* str) { ast_debug (1, "[%s] Incoming SMS message\n", PVT_ID(pvt)); - if (at_enqueue_retrieve_sms(&pvt->sys_chan, index)) + if (at_enqueue_retrieve_sms(&pvt->sys_chan, index, SUPPRESS_ERROR_DISABLED)) { ast_log (LOG_ERROR, "[%s] Error sending CMGR to retrieve SMS message\n", PVT_ID(pvt)); return -1; @@ -1349,7 +1369,7 @@ static int at_response_cmgr (struct pvt* pvt, const char * str, size_t len) at_enqueue_delete_sms(&pvt->sys_chan, pvt->incoming_sms_index); } receive_next_no_delete: - at_retrieve_next_sms(&pvt->sys_chan); + at_retrieve_next_sms(&pvt->sys_chan, at_cmd_suppress_error_mode(ecmd->flags)); } else { From bde88380d9b59d8fa016bf7b6d64f534eb58162e Mon Sep 17 00:00:00 2001 From: h31p Date: Wed, 26 Aug 2020 17:42:48 +0300 Subject: [PATCH 096/117] FreeBSD has slightly another place for lock files --- chan_dongle.c | 4 ++++ tools/tty.c | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/chan_dongle.c b/chan_dongle.c index 1e69613f..c0f649ca 100644 --- a/chan_dongle.c +++ b/chan_dongle.c @@ -119,7 +119,11 @@ static int lock_build(const char * devname, char * buf, unsigned length) basename = devname; /* NOTE: use system system wide lock directory */ + #if defined(__FreeBSD__) + return snprintf(buf, length, "/var/spool/lock/LCK..%s", basename); + #else return snprintf(buf, length, "/var/lock/LCK..%s", basename); + #endif } #/* return 0 on error */ diff --git a/tools/tty.c b/tools/tty.c index 6dde0c36..4ce2877b 100644 --- a/tools/tty.c +++ b/tools/tty.c @@ -44,7 +44,11 @@ static int lock_build(const char * devname, char * buf, unsigned length) basename = devname; /* TODO: use asterisk build settings for /var/lock */ + #if defined(__FreeBSD__) + return snprintf(buf, length, "/var/spool/lock/LCK..%s", basename); + #else return snprintf(buf, length, "/var/lock/LCK..%s", basename); + #endif } #/* return 0 on error */ From fc4d5d88c7c2ccbde6457bf4280ff3a43931ef34 Mon Sep 17 00:00:00 2001 From: Ben Hardill Date: Wed, 2 Sep 2020 15:19:00 +0100 Subject: [PATCH 097/117] A better change to the logging --- at_response.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/at_response.c b/at_response.c index daf08f18..125a688a 100644 --- a/at_response.c +++ b/at_response.c @@ -268,20 +268,23 @@ static int at_response_ok (struct pvt* pvt, at_res_t res) static void log_cmd_response_error(const struct pvt* pvt, const at_queue_cmd_t *ecmd, const char *fmt, ...) { va_list ap; + char tempbuff[512]; if (at_cmd_suppress_error_mode(ecmd->flags) == SUPPRESS_ERROR_ENABLED) { if (DEBUG_ATLEAST(1)) { ast_log(AST_LOG_DEBUG, "[%s] Command response error suppressed:\n", PVT_ID(pvt)); va_start(ap, fmt); - ast_log_ap(AST_LOG_DEBUG, fmt, ap); + vsnprintf(tempbuff, 512, fmt, ap); va_end(ap); + ast_log(AST_LOG_DEBUG, "%s", tempbuff); } return; } va_start(ap, fmt); - ast_log_ap(LOG_ERROR, fmt, ap); + vsnprintf(tempbuff, 512, fmt, ap); + ast_log(LOG_ERROR, "%s", tempbuff); va_end(ap); } From f1517b427f7542f278ba6b8e504c5debb563bd9a Mon Sep 17 00:00:00 2001 From: Mostafa Ghadamyari Date: Mon, 30 Nov 2020 21:04:47 +0330 Subject: [PATCH 098/117] Updated interfaces for E171 The correct device interfaces for HUAWEI E171 modem is 3,2 . the 1,2 interface order does not work. Model: E171 Firmware: 21.156.00.00.143 IMEI: 861496015775578 --- pdiscovery.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pdiscovery.c b/pdiscovery.c index adb0b974..f4c12507 100644 --- a/pdiscovery.c +++ b/pdiscovery.c @@ -66,7 +66,7 @@ static const struct pdiscovery_device device_ids[] = { // { 0x12d1, 0x1465, { 2, 1, /* 0 */ } }, /* K3520 */ { 0x12d1, 0x140c, { 3, 2, /* 0 */ } }, /* E17xx */ { 0x12d1, 0x1436, { 4, 3, /* 0 */ } }, /* E1750 */ - { 0x12d1, 0x1506, { 1, 2, /* 0 */ } }, /* E171 firmware 21.x : thanks Sergey Ivanov */ + { 0x12d1, 0x1506, { 3, 2, /* 0 */ } }, /* E171 firmware 21.x : thanks Sergey Ivanov */ }; static struct discovery_cache cache; From 2bd3fc2cc31562826c8341c5584fe26def9ca808 Mon Sep 17 00:00:00 2001 From: Mostafa Ghadamyari Date: Wed, 23 Dec 2020 18:53:44 +0330 Subject: [PATCH 099/117] Added E153Du-1 device id ports Updated ports for E153Du-1 IMEI: 868672001033577 --- pdiscovery.c | 1 + 1 file changed, 1 insertion(+) diff --git a/pdiscovery.c b/pdiscovery.c index f4c12507..006c5d84 100644 --- a/pdiscovery.c +++ b/pdiscovery.c @@ -65,6 +65,7 @@ static const struct pdiscovery_device device_ids[] = { { 0x12d1, 0x1001, { 2, 1, /* 0 */ } }, /* E1550 and generic */ // { 0x12d1, 0x1465, { 2, 1, /* 0 */ } }, /* K3520 */ { 0x12d1, 0x140c, { 3, 2, /* 0 */ } }, /* E17xx */ + { 0x12d1, 0x14ac, { 4, 3, /* 0 */ } }, /* E153Du-1 : thanks mghadam */ { 0x12d1, 0x1436, { 4, 3, /* 0 */ } }, /* E1750 */ { 0x12d1, 0x1506, { 3, 2, /* 0 */ } }, /* E171 firmware 21.x : thanks Sergey Ivanov */ }; From ca9b69276738af1d571ec120e103d317701760c2 Mon Sep 17 00:00:00 2001 From: 129tyc <129tyc@gmail.com> Date: Mon, 19 Apr 2021 16:30:22 +0800 Subject: [PATCH 100/117] fix Cannot parse UCS-2 error (#131) Thanks @129tyc! --- at_parse.c | 2 +- at_response.c | 2 +- test/parse.c | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/at_parse.c b/at_parse.c index ee85acb0..3d88fffd 100644 --- a/at_parse.c +++ b/at_parse.c @@ -403,7 +403,7 @@ EXPORT_DEF int at_parse_cmgr(char *str, size_t len, int *tpdu_type, char *sca, s /* tpdu_parse_deliver sets chan_dongle_err */ return -1; } - res = ucs2_to_utf8(msg16_tmp, res, msg, res * 2 + 2); + res = ucs2_to_utf8(msg16_tmp, res, msg, *msg_len); if (res < 0) { chan_dongle_err = E_PARSE_UCS2; return -1; diff --git a/at_response.c b/at_response.c index 125a688a..a4db91c2 100644 --- a/at_response.c +++ b/at_response.c @@ -1266,7 +1266,7 @@ static int at_response_cmgr (struct pvt* pvt, const char * str, size_t len) char msg[4096]; int res; char text_base64[40800]; - size_t msg_len; + size_t msg_len = sizeof(msg); int tpdu_type; pdu_udh_t udh; pdu_udh_init(&udh); diff --git a/test/parse.c b/test/parse.c index db391106..731b3a93 100644 --- a/test/parse.c +++ b/test/parse.c @@ -305,6 +305,7 @@ void test_parse_cmgr() int failidx = 0; result.str = input = strdup(cases[idx].input); result.msg_utf8 = buf; + result.msg_len = sizeof(buf); fprintf(stderr, "/* %u */ %s(\"%s\")...", idx, "at_parse_cmgr", input); result.res = at_parse_cmgr( From 5a0ee5318bc2d0ebce8593387bf4af633a9dad2e Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Sun, 19 Sep 2021 11:13:28 +0200 Subject: [PATCH 101/117] Abort compilation on warnings --- configure.ac | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/configure.ac b/configure.ac index 8725699c..f81a8f39 100644 --- a/configure.ac +++ b/configure.ac @@ -187,6 +187,10 @@ AC_SUBST([TARGET]) AC_CC_OPT([-fPIC],[-fPIC]) AC_CC_OPT([-Wall],[-Wall]) AC_CC_OPT([-Wextra],[-Wextra]) +AC_CC_OPT([-Werror=all],[-Werror=all]) +AC_CC_OPT([-Werror=extra],[-Werror=extra]) +AC_CC_OPT([-Werror=discarded-qualifiers],[-Werror=discarded-qualifiers]) +AC_CC_OPT([-Wno-error=format-truncation],[-Wno-error=format-truncation]) AC_CC_OPT([-MD -MT conftest.o -MF /dev/null -MP],[-MD -MT \$@ -MF .\$(subst /,_,\$@).d -MP]) AC_DEFUN([AC_CHECK_DESTDIR], [ From 222039f42ebd74f9eca0f905e37c4da223506771 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Sun, 19 Sep 2021 11:17:06 +0200 Subject: [PATCH 102/117] Fix compiler warnings; add casts, add const when easy, remove unused --- at_command.c | 3 +-- at_parse.c | 16 +++++++++------- at_response.c | 13 ++++++++----- cli.c | 2 -- pdu.c | 41 ++++++++++++++++++++++++++--------------- pdu.h | 19 +++++++++++++------ smsdb.c | 4 ++++ 7 files changed, 61 insertions(+), 37 deletions(-) diff --git a/at_command.c b/at_command.c index 4882d827..f625bdec 100644 --- a/at_command.c +++ b/at_command.c @@ -132,7 +132,6 @@ EXPORT_DEF int at_enqueue_initialization(struct cpvt *cpvt, at_cmd_t from_comman // 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,2,0\r"; @@ -355,7 +354,7 @@ EXPORT_DEF int at_enqueue_ussd(struct cpvt *cpvt, const char *code) chan_dongle_err = E_ENCODE_GSM7; return -1; } - res = gsm7_pack(code16, res, code_packed, sizeof(code_packed), 0); + res = gsm7_pack(code16, res, (char*)code_packed, sizeof(code_packed), 0); if (res < 0) { chan_dongle_err = E_PACK_GSM7; return -1; diff --git a/at_parse.c b/at_parse.c index 3d88fffd..3f0b4af9 100644 --- a/at_parse.c +++ b/at_parse.c @@ -354,8 +354,8 @@ EXPORT_DEF int at_parse_cmgr(char *str, size_t len, int *tpdu_type, char *sca, s char delimiters[] = ",,\n"; char *marks[STRLEN(delimiters)]; char *end; - size_t tpdu_length; - int16_t msg16_tmp[256]; + ssize_t tpdu_length; + uint16_t msg16_tmp[256]; if (mark_line(str, delimiters, marks) != ITEMS_OF(marks)) { chan_dongle_err = E_PARSE_CMGR_LINE; @@ -367,13 +367,13 @@ EXPORT_DEF int at_parse_cmgr(char *str, size_t len, int *tpdu_type, char *sca, s } str = marks[2] + 1; - int pdu_length = (unhex(str, str) + 1) / 2; + int pdu_length = (unhex(str, (uint8_t*)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); + res = pdu_parse_sca((const uint8_t*)str + i, pdu_length - i, sca, sca_len); if (res < 0) { /* tpdu_parse_sca sets chan_dongle_err */ return -1; @@ -383,7 +383,7 @@ EXPORT_DEF int at_parse_cmgr(char *str, size_t len, int *tpdu_type, char *sca, s chan_dongle_err = E_INVALID_TPDU_LENGTH; return -1; } - res = tpdu_parse_type(str + i, pdu_length - i, tpdu_type); + res = tpdu_parse_type((const uint8_t*)str + i, pdu_length - i, tpdu_type); if (res < 0) { /* tpdu_parse_type sets chan_dongle_err */ return -1; @@ -391,14 +391,16 @@ EXPORT_DEF int at_parse_cmgr(char *str, size_t len, int *tpdu_type, char *sca, s 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); + res = tpdu_parse_status_report((const uint8_t*)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); + res = tpdu_parse_deliver((const uint8_t*)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; diff --git a/at_response.c b/at_response.c index a4db91c2..2a9eb2f2 100644 --- a/at_response.c +++ b/at_response.c @@ -1258,7 +1258,7 @@ static int at_response_cdsi (struct pvt* pvt, const char* str) * \retval -1 error */ -static int at_response_cmgr (struct pvt* pvt, const char * str, size_t len) +static int at_response_cmgr(struct pvt* pvt, char * str, size_t len) { char oa[512] = "", sca[512] = ""; char scts[64], dt[64]; @@ -1467,7 +1467,7 @@ static int at_response_cusd (struct pvt * pvt, char * str, size_t len) ast_verb (1, "[%s] USSD DCS=%d (0: gsm7, 1: ascii, 2: ucs2)\n", PVT_ID(pvt), dcs); if (dcs == 0) { // GSM-7 uint16_t out_ucs2[1024]; - int cusd_nibbles = unhex(cusd, cusd); + int cusd_nibbles = unhex(cusd, (uint8_t*)cusd); res = gsm7_unpack_decode(cusd, cusd_nibbles, out_ucs2, sizeof(out_ucs2) / 2, 0, 0, 0); if (res < 0) { return -1; @@ -1475,14 +1475,15 @@ static int at_response_cusd (struct pvt * pvt, char * str, size_t len) 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) { + if (res > (ssize_t)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(cusd, (cusd_nibbles + 1) / 4, cusd_utf8_str, sizeof(cusd_utf8_str) - 1); + int cusd_nibbles = unhex(cusd, (uint8_t*)cusd); + res = ucs2_to_utf8((const uint16_t*)cusd, (cusd_nibbles + 1) / 4, + cusd_utf8_str, sizeof(cusd_utf8_str) - 1); } else { res = -1; } @@ -1953,6 +1954,8 @@ int at_response (struct pvt* pvt, const struct iovec iov[2], int iovcnt, at_res_ ast_log (LOG_ERROR, "[%s] Error parsing result\n", PVT_ID(pvt)); return -1; + case COMPATIBILITY_RES_START_AT_MINUSONE: + /* ??? */ case RES_UNKNOWN: if (ecmd) { diff --git a/cli.c b/cli.c index 34c2750d..c1c96846 100644 --- a/cli.c +++ b/cli.c @@ -389,8 +389,6 @@ static char* cli_show_version (struct ast_cli_entry* e, int cmd, struct ast_cli_ static char* cli_cmd (struct ast_cli_entry* e, int cmd, struct ast_cli_args* a) { - const char * msg; - switch (cmd) { case CLI_INIT: diff --git a/pdu.c b/pdu.c index 83c6ee75..94f74271 100644 --- a/pdu.c +++ b/pdu.c @@ -377,7 +377,7 @@ static int pdu_store_number(uint8_t* buffer, int toa, const char *number, unsign int i = 0; buffer[i++] = toa; - int j; + unsigned j; for (j = 0; j + 1 < length; j += 2) { uint8_t a = pdu_digit2code(number[j]); uint8_t b = pdu_digit2code(number[j + 1]); @@ -398,7 +398,8 @@ static int pdu_store_number(uint8_t* buffer, int toa, const char *number, unsign } #/* reverse of pdu_store_number() */ -static int pdu_parse_number(uint8_t *pdu, size_t pdu_length, unsigned digits, char *number, size_t num_len) +static int pdu_parse_number(const uint8_t *pdu, size_t pdu_length, + unsigned digits, char *number, size_t num_len) { if (num_len < digits + 2) { return -ENOMEM; @@ -414,7 +415,7 @@ static int pdu_parse_number(uint8_t *pdu, size_t pdu_length, unsigned digits, ch 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); + res = gsm7_unpack_decode((const char*)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; @@ -423,7 +424,7 @@ static int pdu_parse_number(uint8_t *pdu, size_t pdu_length, unsigned digits, ch if ((toa & TP_A_TON) == TP_A_TON_INTERNATIONAL) { *number++ = '+'; } - for (int j = 0; j < syms / 2; ++j) { + for (unsigned j = 0; j < syms / 2; ++j) { int c = pdu[i]; *number++ = pdu_code2digit(c & 0xf); char o = c >> 4; @@ -437,7 +438,7 @@ static int pdu_parse_number(uint8_t *pdu, size_t pdu_length, unsigned digits, ch } #/* */ -static int pdu_parse_timestamp(uint8_t *pdu, size_t length, char *out) +static int pdu_parse_timestamp(const uint8_t *pdu, size_t length, char *out) { int d, m, y, h, i, s, o, os; if (length >= 7) { @@ -636,7 +637,9 @@ EXPORT_DEF ssize_t pdu_build(uint8_t *buffer, size_t length, size_t *tpdulen, co 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 += ( + gsm7_pack(msg, msg_reallen, (char*)buffer + len, length - len - 1, + msg_padding) + 1) / 2; } /* also check message limit in 176 octets of TPDU */ @@ -660,7 +663,7 @@ EXPORT_DEF ssize_t pdu_build(uint8_t *buffer, size_t length, size_t *tpdulen, co * \param timestamp -- 25 bytes for timestamp string * \return 0 on success */ -EXPORT_DEF int pdu_parse_sca(uint8_t *pdu, size_t pdu_length, char *sca, size_t sca_len) +EXPORT_DEF int pdu_parse_sca(const uint8_t *pdu, size_t pdu_length, char *sca, size_t sca_len) { int i = 0; int sca_digits = (pdu[i++] - 1) * 2; @@ -672,7 +675,7 @@ EXPORT_DEF int pdu_parse_sca(uint8_t *pdu, size_t pdu_length, char *sca, size_t i += field_len; return i; } -EXPORT_DEF int tpdu_parse_type(uint8_t *pdu, size_t pdu_length, int *type) +EXPORT_DEF int tpdu_parse_type(const uint8_t *pdu, size_t pdu_length, int *type) { if (pdu_length < 1) { chan_dongle_err = E_INVALID_TPDU_TYPE; @@ -681,9 +684,11 @@ EXPORT_DEF int tpdu_parse_type(uint8_t *pdu, size_t pdu_length, int *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) +EXPORT_DEF int tpdu_parse_status_report(const 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; + unsigned i = 0; + int field_len; if (i + 2 > pdu_length) { chan_dongle_err = E_UNKNOWN; return -1; @@ -706,9 +711,12 @@ EXPORT_DEF int tpdu_parse_status_report(uint8_t *pdu, size_t pdu_length, int *mr *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) +EXPORT_DEF int tpdu_parse_deliver(const 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, udl, udhl, msg_padding = 0; + unsigned i = 0; + int field_len, oa_digits, pid, dcs, alphabet, udl, udhl; + int msg_padding = 0; if (i + 1 > pdu_length) { chan_dongle_err = E_UNKNOWN; @@ -822,8 +830,8 @@ EXPORT_DEF int tpdu_parse_deliver(uint8_t *pdu, size_t pdu_length, int tpdu_type } /* calculate number of octets in UD */ - int udl_nibbles; - int udl_bytes = udl; + int udl_nibbles = -1; /* only used for 7bit alphabet */ + unsigned udl_bytes = udl; if (alphabet == PDU_DCS_ALPHABET_7BIT) { udl_nibbles = (udl * 7 + 3) / 4; udl_bytes = (udl_nibbles + 1) / 2; @@ -918,7 +926,10 @@ EXPORT_DEF int tpdu_parse_deliver(uint8_t *pdu, size_t pdu_length, int tpdu_type 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); + out_len = gsm7_unpack_decode( + (const char*)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; diff --git a/pdu.h b/pdu.h index 1af838fb..d9a6316c 100644 --- a/pdu.h +++ b/pdu.h @@ -38,11 +38,18 @@ typedef struct pdu_part } pdu_part_t; EXPORT_DECL void pdu_udh_init(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); + +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(const uint8_t *pdu, size_t pdu_length, char *sca, size_t sca_len); +EXPORT_DECL int tpdu_parse_type(const uint8_t *pdu, size_t pdu_length, int *type); +EXPORT_DECL int tpdu_parse_status_report(const 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(const 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 417cb515..9fb773d0 100644 --- a/smsdb.c +++ b/smsdb.c @@ -290,12 +290,14 @@ static int smsdb_commit_transaction(void) return res; } +#if 0 static int smsdb_rollback_transaction(void) { int res = db_execute_sql("ROLLBACK", NULL, NULL); ast_mutex_unlock(&dblock); return res; } +#endif /*! @@ -388,6 +390,7 @@ EXPORT_DEF int smsdb_put(const char *id, const char *addr, int ref, int parts, i return res; } +#if 0 static int smsdb_purge() { int res = 0; @@ -399,6 +402,7 @@ static int smsdb_purge() return res; } +#endif EXPORT_DEF int smsdb_get_refid(const char *id, const char *addr) { From 81b0fdac54e6379e969c10129a23ac69c96bbcb4 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Sun, 19 Sep 2021 11:56:09 +0200 Subject: [PATCH 103/117] Try GitHub Actions instead of "Free Plan Will Always Be Free"-Travis --- .../workflows/thanks-for-nothing-travis.yml | 43 +++++++++++++++++++ .travis.yml | 31 ------------- 2 files changed, 43 insertions(+), 31 deletions(-) create mode 100644 .github/workflows/thanks-for-nothing-travis.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/thanks-for-nothing-travis.yml b/.github/workflows/thanks-for-nothing-travis.yml new file mode 100644 index 00000000..ba0cc385 --- /dev/null +++ b/.github/workflows/thanks-for-nothing-travis.yml @@ -0,0 +1,43 @@ +name: Thanks for nothing, Travis + +on: [push, pull_request] + +jobs: + + build: + + strategy: + matrix: + include: + - title: Asterisk 13 + asterisk_version: 13 + - title: Asterisk 14 + asterisk_version: 14 + - title: Asterisk 16 + asterisk_version: 16 + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Download ${{ matrix.title }} prerequisites + env: + ASTVER: ${{ matrix.asterisk_version }} + run: + wget http://junk.devs.nu/a/asterisk/asterisk-$ASTVER-include.tar.bz2 && + tar jxf asterisk-$ASTVER-include.tar.bz2 + + - name: Bootstrap and configure for ${{ matrix.title }} + env: + ASTVER: ${{ matrix.asterisk_version }} + run: + ./bootstrap && + ./configure --with-astversion=$ASTVER + --with-asterisk=./asterisk-$ASTVER/include DESTDIR=/tmp + + - name: Build for ${{ matrix.title }} + run: make clean all + + - name: Check + run: make check diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index f271ccde..00000000 --- a/.travis.yml +++ /dev/null @@ -1,31 +0,0 @@ -# Config - -language: c -sudo: false -dist: trusty -cache: bundler -notifications: - email: false - -env: - - ASTVER=14 - -# Extend build matrix -matrix: - include: - - env: ASTVER=16 - - env: ASTVER=13 - -before_install: - - wget http://junk.devs.nu/a/asterisk/asterisk-$ASTVER-include.tar.bz2 - - tar jxf asterisk-$ASTVER-include.tar.bz2 - -before_script: - - aclocal - - autoconf - - automake -a || true # do install stuff, don't create makefile - - ./configure --with-astversion=$ASTVER --with-asterisk=./asterisk-$ASTVER/include DESTDIR=/tmp - -script: - - make clean all - - make check From a35294bd3e4a035c3cb6f7dbb604c28b1d1f5650 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Sun, 19 Sep 2021 13:22:47 +0200 Subject: [PATCH 104/117] Fix make check compiler warnings and check errors msg had been partially removed in 868bbd4. --- test/gen.c | 7 +++- test/parse.c | 115 ++++++++++++++++++++++++++------------------------- 2 files changed, 63 insertions(+), 59 deletions(-) diff --git a/test/gen.c b/test/gen.c index eef37a3e..a552db08 100644 --- a/test/gen.c +++ b/test/gen.c @@ -21,6 +21,7 @@ typedef struct result const char *in; const char *out[16]; } result_t; + void test_pdu_build() { result_t res[] = { @@ -60,7 +61,7 @@ void test_pdu_build() 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) { + for (unsigned 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); @@ -80,7 +81,9 @@ void test_pdu_build() 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); + if (res[i].out[cnt]) { + fprintf(stderr, "Check %d unsuccessful: Expected %s; Got (null)\n", i, res[i].out[cnt]); + } } } diff --git a/test/parse.c b/test/parse.c index 731b3a93..8d336d1b 100644 --- a/test/parse.c +++ b/test/parse.c @@ -12,9 +12,9 @@ int faults = 0; /* We call ast_log from pdu.c, so we'll fake an implementation here. */ void ast_log(int level, const char* fmt, ...) { - /* Silence compiler warnings */ - (void)level; - (void)fmt; + /* Silence compiler warnings */ + (void)level; + (void)fmt; } #/* */ @@ -192,10 +192,10 @@ void test_gsm7() 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); + int packedsize = gsm7_pack(buf16, res, (char*)pdu, res * 2, j); + hexify(pdu, (packedsize + 1) / 2, (char*)pdu); + res = unhex((const char*)pdu, pdu); + res = gsm7_unpack_decode((const char*)pdu, packedsize, buf16, res, j, 0, 0); char rev[256]; res = ucs2_to_utf8(buf16, res, rev, 256); rev[res] = 0; @@ -210,92 +210,92 @@ void test_gsm7() } void test_parse_cmgr() { + struct check_result { + const int res; + const char *str; + const char *oa; + const char *msg_utf8; + }; struct result { - char * res; - char * str; - char * oa; - char * msg; - char * msg_utf8; - char * sca; + int res; + char *str; + char *oa; + char *msg_utf8; + char *sca; int tpdu_type; pdu_udh_t udh; int mr, st; - char scts[256], dt[256]; + char scts[256]; + char dt[256]; size_t msg_len; }; static const struct test_case { - const char * input; - struct result result; + const char *input; + struct check_result result; } cases[] = { { "+CMGR: 0,,106\r\n07911111111100F3040B911111111111F200000121702214952163B1582C168BC562B1984C2693C96432994C369BCD66B3D96C369BD168341A8D46A3D168B55AAD56ABD56AB59ACD66B3D96C369BCD76BBDD6EB7DBED76BBE170381C0E87C3E170B95C2E97CBE572B91C0C0683C16030180C", { - 0, - "B1582C168BC562B1984C2693C96432994C369BCD66B3D96C369BD168341A8D46A3D168B55AAD56ABD56AB59ACD66B3D96C369BCD76BBDD6EB7DBED76BBE170381C0E87C3E170B95C2E97CBE572B91C0C0683C16030180C", - "+11111111112", - "B1582C168BC562B1984C2693C96432994C369BCD66B3D96C369BD168341A8D46A3D168B55AAD56ABD56AB59ACD66B3D96C369BCD76BBDD6EB7DBED76BBE170381C0E87C3E170B95C2E97CBE572B91C0C0683C16030180C", - "111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999000000000" + .res = 0, + .str = "B1582C168BC562B1984C2693C96432994C369BCD66B3D96C369BD168341A8D46A3D168B55AAD56ABD56AB59ACD66B3D96C369BCD76BBDD6EB7DBED76BBE170381C0E87C3E170B95C2E97CBE572B91C0C0683C16030180C", + .oa = "+11111111112", + .msg_utf8 = "111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999000000000" } }, { "+CMGR: 0,,159\r\n07919740430900F3440B912222222220F20008012180004390218C0500030003010031003100310031003100310031003100310031003200320032003200320032003200320032003200330033003300330033003300330033003300330034003400340034003400340034003400340034003500350035003500350035003500350035003500360036003600360036003600360036003600360037003700370037003700370037", { - 0, - "0031003100310031003100310031003100310031003200320032003200320032003200320032003200330033003300330033003300330033003300330034003400340034003400340034003400340034003500350035003500350035003500350035003500360036003600360036003600360036003600360037003700370037003700370037", - "+22222222022", - "0031003100310031003100310031003100310031003200320032003200320032003200320032003200330033003300330033003300330033003300330034003400340034003400340034003400340034003500350035003500350035003500350035003500360036003600360036003600360036003600360037003700370037003700370037", - "1111111111222222222233333333334444444444555555555566666666667777777" + .res = 0, + .str = "0031003100310031003100310031003100310031003200320032003200320032003200320032003200330033003300330033003300330033003300330034003400340034003400340034003400340034003500350035003500350035003500350035003500360036003600360036003600360036003600360037003700370037003700370037", + .oa = "+22222222022", + .msg_utf8 = "1111111111222222222233333333334444444444555555555566666666667777777" } }, { "+CMGR: 0,,159\r\n07913306000000F0440B913306000000F0000061011012939280A0050003CA020182E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3", { - 0, - "82E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3", - "+33600000000", - "82E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3", - "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" + .res = 0, + .str = "82E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3E13028180E87C3A060381C0E8382E170380C0A86C3", + .oa = "+33600000000", + .msg_utf8 = "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", { - 0, - "C26150301C0E8741C170381C0605C3E17018", - "+33600000000", - "C26150301C0E8741C170381C0605C3E17018", - "aa Aaaaa Aaaaa Aaaaa" + .res = 0, + .str = "C26150301C0E8741C170381C0605C3E17018", + .oa = "+33600000000", + .msg_utf8 = "aa Aaaaa Aaaaa Aaaaa" } }, { "+CMGR: 0,,158\r\n07916407970970F6400A912222222222000041903021825180A0050003000301A9E5391D14060941439015240409414290102404094142901024040941429010240409414290106405594142901564055941429012A40429AD4AABD22A7481AC56101264455A915624C80AB282AC20A1D06A0559415610D20A4282AC2024C80AB282AC202BC80AB282AC2E9012B4042D414A90D2055282942E90D20502819420254809528294", { - 0, - "A9E5391D14060941439015240409414290102404094142901024040941429010240409414290106405594142901564055941429012A40429AD4AABD22A7481AC56101264455A915624C80AB282AC20A1D06A0559415610D20A4282AC2024C80AB282AC202BC80AB282AC2E9012B4042D414A90D2055282942E90D20502819420254809528294", - "+2222222222", - "A9E5391D14060941439015240409414290102404094142901024040941429010240409414290106405594142901564055941429012A40429AD4AABD22A7481AC56101264455A915624C80AB282AC20A1D06A0559415610D20A4282AC2024C80AB282AC202BC80AB282AC2E9012B4042D414A90D2055282942E90D20502819420254809528294", - "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" + .res = 0, + .str = "A9E5391D14060941439015240409414290102404094142901024040941429010240409414290106405594142901564055941429012A40429AD4AABD22A7481AC56101264455A915624C80AB282AC20A1D06A0559415610D20A4282AC2024C80AB282AC202BC80AB282AC2E9012B4042D414A90D2055282942E90D20502819420254809528294", + .oa = "+2222222222", + .msg_utf8 = "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", { - 0, - "A09976D7E9E5390B640FB3D364103DCD668364B3562CD692C1623417", - "Ufone", /* 55F3DB5D062 */ - "A09976D7E9E5390B640FB3D364103DCD668364B3562CD692C1623417", - "Minutes, valid till 23-11-2014." + .res = 0, + .str = "A09976D7E9E5390B640FB3D364103DCD668364B3562CD692C1623417", + .oa = "Ufone", /* 55F3DB5D062 */ + .msg_utf8 = "Minutes, valid till 23-11-2014.", } }, { "+CMGR: 0,,137\r\n07919333851805320409D034186C360300F0713032810105408849A7F1099A36A720D9EC059BB140319C2E06D38186EF39FD0D1AA3D3E176981E06155D20184B467381926CD0585E26A7E96F1001547481683816ACE60241CB7250DA6D7E83E67550D95E76D3EB61761AF486EBD36F771A14A6D3D3F632A80C12BFDDF539485E9EA7C9F534688C4E87DB61100D968BD95C", { - 0, - "49A7F1099A36A720D9EC059BB140319C2E06D38186EF39FD0D1AA3D3E176981E06155D20184B467381926CD0585E26A7E96F1001547481683816ACE60241CB7250DA6D7E83E67550D95E76D3EB61761AF486EBD36F771A14A6D3D3F632A80C12BFDDF539485E9EA7C9F534688C4E87DB61100D968BD95C", - "40033", /* 09D034186C3603 */ - "49A7F1099A36A720D9EC059BB140319C2E06D38186EF39FD0D1AA3D3E176981E06155D20184B467381926CD0585E26A7E96F1001547481683816ACE60241CB7250DA6D7E83E67550D95E76D3EB61761AF486EBD36F771A14A6D3D3F632A80C12BFDDF539485E9EA7C9F534688C4E87DB61100D968BD95C", - "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." - } + .res = 0, + .str = "49A7F1099A36A720D9EC059BB140319C2E06D38186EF39FD0D1AA3D3E176981E06155D20184B467381926CD0585E26A7E96F1001547481683816ACE60241CB7250DA6D7E83E67550D95E76D3EB61761AF486EBD36F771A14A6D3D3F632A80C12BFDDF539485E9EA7C9F534688C4E87DB61100D968BD95C", + .oa = "40033", /* 09D034186C3603 */ + .msg_utf8 = "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.", + }, }, }; unsigned idx = 0; char * input; struct result result; - char oa[200], sca[200]; + char oa[200]; + char sca[200] = {}; const char * msg; result.oa = oa; @@ -309,7 +309,9 @@ 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.tpdu_type, &result.sca, sizeof(sca), result.oa, sizeof(oa), result.scts, &result.mr, &result.st, result.dt, + 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 && result.res == cases[idx].result.res && @@ -323,9 +325,8 @@ void test_parse_cmgr() msg = "FAIL"; faults++; } - fprintf(stderr, " = '%s' ('%s','%s') [fail@%d]\n[text=%s] %s\n", - result.res, result.oa, - result.msg, failidx, result.msg_utf8, msg); + fprintf(stderr, " = %d ('%s') [fail@%d]\n[text=%s] %s\n", + result.res, result.oa, failidx, result.msg_utf8, msg); free(input); } fprintf(stderr, "\n"); From 46be41b3810d84cf1c90b8771c1058a3b870ee68 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Sun, 19 Sep 2021 13:46:38 +0200 Subject: [PATCH 105/117] Better variable locality in at_enqueue_initialization (No random "cmd12" constants.) --- at_command.c | 112 +++++++++++++++++++++++++-------------------------- 1 file changed, 54 insertions(+), 58 deletions(-) diff --git a/at_command.c b/at_command.c index f625bdec..a9e76e67 100644 --- a/at_command.c +++ b/at_command.c @@ -110,66 +110,62 @@ static int __attribute__ ((format(printf, 4, 5))) at_enqueue_generic(struct cpvt */ EXPORT_DEF int at_enqueue_initialization(struct cpvt *cpvt, at_cmd_t from_command) { - static const char cmd2[] = "ATZ\r"; - static const char cmd3[] = "ATE0\r"; - - static const char cmd5[] = "AT+CGMI\r"; - static const char cmd6[] = "AT+CSCA?\r"; - static const char cmd7[] = "AT+CGMM\r"; - static const char cmd8[] = "AT+CGMR\r"; - - static const char cmd9[] = "AT+CMEE=0\r"; - static const char cmd10[] = "AT+CGSN\r"; - static const char cmd11[] = "AT+CIMI\r"; - static const char cmd12[] = "AT+CPIN?\r"; - - static const char cmd13[] = "AT+COPS=0,0\r"; - static const char cmd14[] = "AT+CREG=2\r"; - static const char cmd15[] = "AT+CREG?\r"; - static const char cmd16[] = "AT+CNUM\r"; - - 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 cmd22[] = "AT+CPMS=\"SM\",\"SM\",\"SM\"\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[] = { + /* AT */ ATQ_CMD_DECLARE_ST(CMD_AT, cmd_at), - ATQ_CMD_DECLARE_ST(CMD_AT_Z, cmd2), /* optional, reload configuration */ - ATQ_CMD_DECLARE_ST(CMD_AT_E, cmd3), /* disable echo */ - ATQ_CMD_DECLARE_DYN(CMD_AT_U2DIAG), /* optional, Enable or disable some devices */ - ATQ_CMD_DECLARE_ST(CMD_AT_CGMI, cmd5), /* Getting manufacturer info */ - - ATQ_CMD_DECLARE_ST(CMD_AT_CGMM, cmd7), /* Get Product name */ - ATQ_CMD_DECLARE_ST(CMD_AT_CGMR, cmd8), /* Get software version */ - ATQ_CMD_DECLARE_ST(CMD_AT_CMEE, cmd9), /* set MS Error Report to 'ERROR' only TODO: change to 1 or 2 and add support in response handlers */ - - ATQ_CMD_DECLARE_ST(CMD_AT_CGSN, cmd10), /* IMEI Read */ - ATQ_CMD_DECLARE_ST(CMD_AT_CIMI, cmd11), /* IMSI Read */ - ATQ_CMD_DECLARE_ST(CMD_AT_CPIN, cmd12), /* check is password authentication requirement and the remainder validation times */ - ATQ_CMD_DECLARE_ST(CMD_AT_COPS_INIT, cmd13), /* Read operator name */ - - ATQ_CMD_DECLARE_STI(CMD_AT_CREG_INIT,cmd14), /* GSM registration status setting */ - ATQ_CMD_DECLARE_ST(CMD_AT_CREG, cmd15), /* GSM registration status */ - ATQ_CMD_DECLARE_STI(CMD_AT_CNUM, cmd16), /* Get Subscriber number */ - ATQ_CMD_DECLARE_ST(CMD_AT_CVOICE, cmd17), /* read the current voice mode, and return sampling rate、data bit、frame period */ - - 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_ST(CMD_AT_CMGF, cmd20), /* Set Message Format */ - -// 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 */ - ATQ_CMD_DECLARE_ST(CMD_AT_CNMI, cmd23), /* New SMS Notification Setting +CNMI=[[,[,[,[,]]]]] */ - ATQ_CMD_DECLARE_ST(CMD_AT_CSQ, cmd24), /* Query Signal quality */ - }; + /* optional, reload configuration */ + ATQ_CMD_DECLARE_ST(CMD_AT_Z, "ATZ\r"), + /* disable echo */ + ATQ_CMD_DECLARE_ST(CMD_AT_E, "ATE0\r"), + /* optional, Enable or disable some devices */ + ATQ_CMD_DECLARE_DYN(CMD_AT_U2DIAG), + /* Get manufacturer info */ + ATQ_CMD_DECLARE_ST(CMD_AT_CGMI, "AT+CGMI\r"), + + /* Get Product name */ + ATQ_CMD_DECLARE_ST(CMD_AT_CGMM, "AT+CGMM\r"), + /* Get software version */ + ATQ_CMD_DECLARE_ST(CMD_AT_CGMR, "AT+CGMR\r"), + /* set MS Error Report to 'ERROR' only. + * TODO: change to 1 or 2 and add support in response handlers */ + ATQ_CMD_DECLARE_ST(CMD_AT_CMEE, "AT+CMEE=0\r"), + + /* IMEI Read */ + ATQ_CMD_DECLARE_ST(CMD_AT_CGSN, "AT+CGSN\r"), + /* IMSI Read */ + ATQ_CMD_DECLARE_ST(CMD_AT_CIMI, "AT+CIMI\r"), + /* check is password authentication requirement and the + * remainder validation times */ + ATQ_CMD_DECLARE_ST(CMD_AT_CPIN, "AT+CPIN?\r"), + /* Read operator name */ + ATQ_CMD_DECLARE_ST(CMD_AT_COPS_INIT, "AT+COPS=0,0\r"), + + /* GSM registration status setting */ + ATQ_CMD_DECLARE_STI(CMD_AT_CREG_INIT, "AT+CREG=2\r"), + /* GSM registration status */ + ATQ_CMD_DECLARE_ST(CMD_AT_CREG, "AT+CREG?\r"), + /* Get Subscriber number */ + ATQ_CMD_DECLARE_STI(CMD_AT_CNUM, "AT+CNUM\r"), + /* read the current voice mode, and return sampling + * rate、data bit、frame period */ + ATQ_CMD_DECLARE_ST(CMD_AT_CVOICE, "AT^CVOICE?\r"), + + /* Get SMS Service center address */ + ATQ_CMD_DECLARE_ST(CMD_AT_CSCA, "AT+CSCA?\r"), + /* activate Supplementary Service Notification with CSSI and CSSU */ + ATQ_CMD_DECLARE_ST(CMD_AT_CSSN, "AT+CSSN=1,1\r"), + /* Set Message Format */ + ATQ_CMD_DECLARE_ST(CMD_AT_CMGF, "AT+CMGF=0\r"), + + /* SMS Storage Selection */ + ATQ_CMD_DECLARE_ST(CMD_AT_CPMS, "AT+CPMS=\"SM\",\"SM\",\"SM\"\r"), + /* New SMS Notification Setting +CNMI=[[,[,[,[,]]]]] */ + /* pvt->initialized = 1 after successful of CMD_AT_CNMI */ + ATQ_CMD_DECLARE_ST(CMD_AT_CNMI, "AT+CNMI=2,1,0,2,0\r"), + /* Query Signal quality */ + ATQ_CMD_DECLARE_ST(CMD_AT_CSQ, "AT+CSQ\r"), + }; + unsigned in, out; int begin = -1; int err; From 6073c91fcf0a46a1525d500c274fa5ab96af7dda Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Sun, 19 Sep 2021 14:36:16 +0200 Subject: [PATCH 106/117] Add Quectel support Totally untested. Might close #144. Based on this patch: From d332203e6fb869ee9f7f48cd6fd794394888d284 Mon Sep 17 00:00:00 2001 From: Sebastian Kemper Date: Thu, 9 Sep 2021 11:14:30 +0200 Subject: [PATCH] Dongle to Quectel changes Signed-off-by: Sebastian Kemper --- at_command.c | 4 ++-- pdiscovery.c | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/at_command.c b/at_command.c index 4882d82..684e821 100644 --- a/at_command.c +++ b/at_command.c @@ -33,7 +33,7 @@ static const char cmd_at[] = "AT\r"; static const char cmd_chld1x[] = "AT+CHLD=1%d\r"; 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"; +static const char cmd_ddsetex2[] = "AT+QPCMV=1,0\r"; /*! * \brief Format and fill generic command @@ -128,7 +128,7 @@ EXPORT_DEF int at_enqueue_initialization(struct cpvt *cpvt, at_cmd_t from_comman static const char cmd15[] = "AT+CREG?\r"; static const char cmd16[] = "AT+CNUM\r"; - static const char cmd17[] = "AT^CVOICE?\r"; + static const char cmd17[] = "AT+QPCMV?\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"; diff --git a/pdiscovery.c b/pdiscovery.c index 006c5d8..c599c23 100644 --- a/pdiscovery.c +++ b/pdiscovery.c @@ -68,6 +68,7 @@ static const struct pdiscovery_device device_ids[] = { { 0x12d1, 0x14ac, { 4, 3, /* 0 */ } }, /* E153Du-1 : thanks mghadam */ { 0x12d1, 0x1436, { 4, 3, /* 0 */ } }, /* E1750 */ { 0x12d1, 0x1506, { 3, 2, /* 0 */ } }, /* E171 firmware 21.x : thanks Sergey Ivanov */ + { 0x2c7c, 0x0125, { 1, 4, /* 0 */ } }, /* Dongle EC25-A LTE modem */ }; static struct discovery_cache cache; --- at_command.c | 26 ++++++++++++++++++-------- at_command.h | 4 ++++ at_response.c | 35 ++++++++++++++++++++++++----------- chan_dongle.h | 1 + pdiscovery.c | 1 + 5 files changed, 48 insertions(+), 19 deletions(-) diff --git a/at_command.c b/at_command.c index a9e76e67..7e7ad0da 100644 --- a/at_command.c +++ b/at_command.c @@ -34,6 +34,7 @@ static const char cmd_chld1x[] = "AT+CHLD=1%d\r"; 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"; +static const char cmd_qpcmv10[] = "AT+QPCMV=1,0\r"; /*! * \brief Format and fill generic command @@ -147,8 +148,9 @@ EXPORT_DEF int at_enqueue_initialization(struct cpvt *cpvt, at_cmd_t from_comman /* Get Subscriber number */ ATQ_CMD_DECLARE_STI(CMD_AT_CNUM, "AT+CNUM\r"), /* read the current voice mode, and return sampling - * rate、data bit、frame period */ + * rate, data bit, frame period */ ATQ_CMD_DECLARE_ST(CMD_AT_CVOICE, "AT^CVOICE?\r"), + ATQ_CMD_DECLARE_ST(CMD_AT_QPCMV, "AT+QPCMV?\r"), /* for Quectel */ /* Get SMS Service center address */ ATQ_CMD_DECLARE_ST(CMD_AT_CSCA, "AT+CSCA?\r"), @@ -533,10 +535,13 @@ EXPORT_DEF int at_enqueue_dial(struct cpvt *cpvt, const char *number, int clir) ATQ_CMD_INIT_ST(cmds[cmdsno], CMD_AT_CLCC, cmd_clcc); cmdsno++; - ATQ_CMD_INIT_ST(cmds[cmdsno], CMD_AT_DDSETEX, cmd_ddsetex2); + if (pvt->has_voice_quectel) { + ATQ_CMD_INIT_ST(cmds[cmdsno], CMD_AT_DDSETEX, cmd_qpcmv10); + } else { + ATQ_CMD_INIT_ST(cmds[cmdsno], CMD_AT_DDSETEX, cmd_ddsetex2); + } cmdsno++; - if (at_queue_insert(cpvt, cmds, cmdsno, 1) != 0) { chan_dongle_err = E_QUEUE; return -1; @@ -553,13 +558,18 @@ EXPORT_DEF int at_enqueue_dial(struct cpvt *cpvt, const char *number, int clir) */ 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 count = ITEMS_OF(cmds); + pvt_t* pvt = cpvt->pvt; + at_queue_cmd_t cmds[2]; + unsigned count = 2; /* AT_A + setup-voice */ const char * cmd1; + ATQ_CMD_INIT_DYN(cmds[0], CMD_AT_A); + if (pvt->has_voice_quectel) { + ATQ_CMD_INIT_ST(cmds[1], CMD_AT_DDSETEX, cmd_qpcmv10); + } else { + ATQ_CMD_INIT_ST(cmds[1], CMD_AT_DDSETEX, cmd_ddsetex2); + } + if(cpvt->state == CALL_STATE_INCOMING) { /* FIXME: channel number? */ diff --git a/at_command.h b/at_command.h index 32535d65..0aeb4411 100644 --- a/at_command.h +++ b/at_command.h @@ -60,7 +60,11 @@ _( AT_CVOICE, "AT^CVOICE") \ _( AT_D, "ATD") \ \ + /* set the destination port of voice after a voice call is + * established */ \ _( AT_DDSETEX, "AT^DDSETEX") \ + /* same, but for Quectel EC25 */ \ + _( AT_QPCMV, "AT+QPCMV") \ _( AT_DTMF, "AT^DTMF") \ _( AT_E, "ATE") \ \ diff --git a/at_response.c b/at_response.c index 2a9eb2f2..23bf7ce1 100644 --- a/at_response.c +++ b/at_response.c @@ -121,10 +121,16 @@ static int at_response_ok (struct pvt* pvt, at_res_t res) ast_debug (1, "[%s] Subscriber phone number query successed\n", PVT_ID(pvt)); break; + /* These two are expected to be called in order */ case CMD_AT_CVOICE: ast_debug (1, "[%s] Dongle has voice support\n", PVT_ID(pvt)); - pvt->has_voice = 1; + pvt->has_voice_quectel = 0; + break; + case CMD_AT_QPCMV: + ast_debug (1, "[%s] Dongle has Quectel voice support\n", PVT_ID(pvt)); + pvt->has_voice = 1; + pvt->has_voice_quectel = 1; break; /* case CMD_AT_CLIP: @@ -362,19 +368,26 @@ static int at_response_error (struct pvt* pvt, at_res_t res) ast_debug (1, "[%s] Error getting registration info\n", PVT_ID(pvt)); break; + /* These two are expected to be called in order */ case CMD_AT_CVOICE: - ast_debug (1, "[%s] Dongle has NO voice support\n", PVT_ID(pvt)); - ast_log (LOG_WARNING, "[%s] Dongle has NO voice support\n", PVT_ID(pvt)); - + ast_debug(1, "[%s] Dongle has NO (CVOICE) voice support\n", PVT_ID(pvt)); pvt->has_voice = 0; + break; + case CMD_AT_QPCMV: + ast_debug(1, "[%s] Dongle has NO (QPCMV) voice support\n", PVT_ID(pvt)); + pvt->has_voice_quectel = 0; - if (!pvt->initialized) - { - /* continue initialization in other job at cmd CMD_AT_CMGF */ - if (at_enqueue_initialization(task->cpvt, CMD_AT_CMGF)) + if (pvt->has_voice == 0) { + ast_log(LOG_WARNING, "[%s] Dongle has NO voice support\n", PVT_ID(pvt)); + + if (!pvt->initialized) { - log_cmd_response_error(pvt, ecmd, "[%s] Error schedule initialization commands\n", PVT_ID(pvt)); - goto e_return; + /* continue initialization in other job at cmd CMD_AT_CMGF */ + if (at_enqueue_initialization(task->cpvt, CMD_AT_CMGF)) + { + log_cmd_response_error(pvt, ecmd, "[%s] Error schedule initialization commands\n", PVT_ID(pvt)); + goto e_return; + } } } break; @@ -448,7 +461,7 @@ static int at_response_error (struct pvt* pvt, at_res_t res) break; case CMD_AT_DDSETEX: - log_cmd_response_error(pvt, ecmd, "[%s] AT^DDSETEX failed\n", PVT_ID(pvt)); + log_cmd_response_error(pvt, ecmd, "[%s] %s (setup voice) failed\n", PVT_ID(pvt), at_cmd2str(ecmd->cmd)); break; case CMD_AT_CHUP: diff --git a/chan_dongle.h b/chan_dongle.h index 20f87735..a374981a 100644 --- a/chan_dongle.h +++ b/chan_dongle.h @@ -183,6 +183,7 @@ typedef struct pvt unsigned int has_sms:1; /*!< device has SMS support */ unsigned int has_voice:1; /*!< device has voice call support */ + unsigned int has_voice_quectel:1; /*!< device has Quectel voice call support */ unsigned int has_call_waiting:1; /*!< call waiting enabled on device */ unsigned int group_last_used:1; /*!< mark the last used device */ diff --git a/pdiscovery.c b/pdiscovery.c index 006c5d84..c599c232 100644 --- a/pdiscovery.c +++ b/pdiscovery.c @@ -68,6 +68,7 @@ static const struct pdiscovery_device device_ids[] = { { 0x12d1, 0x14ac, { 4, 3, /* 0 */ } }, /* E153Du-1 : thanks mghadam */ { 0x12d1, 0x1436, { 4, 3, /* 0 */ } }, /* E1750 */ { 0x12d1, 0x1506, { 3, 2, /* 0 */ } }, /* E171 firmware 21.x : thanks Sergey Ivanov */ + { 0x2c7c, 0x0125, { 1, 4, /* 0 */ } }, /* Dongle EC25-A LTE modem */ }; static struct discovery_cache cache; From 5837b98ec5634e1a67402934dfa6be5a3b7de662 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Sun, 19 Sep 2021 17:57:24 +0200 Subject: [PATCH 107/117] Quick fix for mips-openwrt-linux-musl-gcc maybe-uninitialized warning (Definite false positive.) --- smsdb.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/smsdb.c b/smsdb.c index 9fb773d0..a6d8a82a 100644 --- a/smsdb.c +++ b/smsdb.c @@ -628,7 +628,11 @@ EXPORT_DEF ssize_t smsdb_outgoing_part_status(const char *id, const char *addr, { char fullkey[MAX_DB_FIELD + 1]; int fullkey_len; - int res = 0, partid, uid, cur, cnt; + int res = 0; + int partid; + int uid = -1; + int cur; + int cnt; fullkey_len = snprintf(fullkey, sizeof(fullkey), "%s/%s/%d", id, addr, mr); if (fullkey_len < 0) { From df6e3ed25c120e932f0ff08b4cf30c1005813b55 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Sun, 19 Sep 2021 18:12:27 +0200 Subject: [PATCH 108/117] Don't do -Werror by default, use ./configure --enable-warnings-as-errors --- .../workflows/thanks-for-nothing-travis.yml | 1 + configure.ac | 27 ++++++++++++------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/.github/workflows/thanks-for-nothing-travis.yml b/.github/workflows/thanks-for-nothing-travis.yml index ba0cc385..1874ca00 100644 --- a/.github/workflows/thanks-for-nothing-travis.yml +++ b/.github/workflows/thanks-for-nothing-travis.yml @@ -35,6 +35,7 @@ jobs: ./bootstrap && ./configure --with-astversion=$ASTVER --with-asterisk=./asterisk-$ASTVER/include DESTDIR=/tmp + --enable-warnings-as-errors - name: Build for ${{ matrix.title }} run: make clean all diff --git a/configure.ac b/configure.ac index f81a8f39..7338187d 100644 --- a/configure.ac +++ b/configure.ac @@ -45,12 +45,19 @@ AC_DEFINE_UNQUOTED([ASTERISK_VERSION_NUM], [$with_astversion], [The Asterisk version as configured --with-astversion.]) AC_ARG_ENABLE( - [debug], + [debug], AS_HELP_STRING([--enable-debug], [enable code debugging]), [ if test "x$enable_debug" != "xyes" ; then enable_debug="no" ; fi], [ enable_debug="no"] ) +AC_ARG_ENABLE( + [warnings-as-errors], + AS_HELP_STRING([--enable-warnings-as-errors], [abort compilation on errors]), + [ if test "x$enable_warnings_as_errors" != "xyes" ; then enable_warnings_as_errors="no" ; fi], + [ enable_warnings_as_errors="no"] +) + dnl Optionally disable manager. AC_ARG_ENABLE( [manager], @@ -114,7 +121,7 @@ AC_CHECK_HEADER([sqlite3.h],,AC_MSG_ERROR([sqlite3.h header file missing])) AC_DEFINE([ICONV_CONST],[], [Define to const if you has iconv() const declaration of input buffer]) AC_MSG_CHECKING([for iconv use const inbuf]) -AC_EGREP_HEADER([^extern.+iconv[[:space:]]*\(.+const], [iconv.h], +AC_EGREP_HEADER([^extern.+iconv[[:space:]]*\(.+const], [iconv.h], [ AC_DEFINE([ICONV_CONST],[const]) AC_MSG_RESULT([yes]) @@ -123,7 +130,7 @@ AC_EGREP_HEADER([^extern.+iconv[[:space:]]*\(.+const], [iconv.h], ) AC_MSG_CHECKING([for iconv_t in iconv.h]) -AC_EGREP_HEADER([iconv_t], [iconv.h], +AC_EGREP_HEADER([iconv_t], [iconv.h], [ AC_DEFINE([ICONV_T], [iconv_t], , [Define to iconv_t if you has iconv_t in iconv.h]) AC_MSG_RESULT(yes) @@ -135,7 +142,7 @@ AC_EGREP_HEADER([iconv_t], [iconv.h], ]) AC_MSG_CHECKING([for AST_CONTROL_SRCCHANGE in asterisk/frame.h]) -AC_EGREP_HEADER([AST_CONTROL_SRCCHANGE], [asterisk/frame.h], +AC_EGREP_HEADER([AST_CONTROL_SRCCHANGE], [asterisk/frame.h], [ AC_DEFINE([HAVE_AST_CONTROL_SRCCHANGE], [], [Define to 1 if you have HAVE_AST_CONTROL_SRCCHANGE in asterisk/frame.h]) AC_MSG_RESULT([yes]) @@ -187,10 +194,12 @@ AC_SUBST([TARGET]) AC_CC_OPT([-fPIC],[-fPIC]) AC_CC_OPT([-Wall],[-Wall]) AC_CC_OPT([-Wextra],[-Wextra]) -AC_CC_OPT([-Werror=all],[-Werror=all]) -AC_CC_OPT([-Werror=extra],[-Werror=extra]) -AC_CC_OPT([-Werror=discarded-qualifiers],[-Werror=discarded-qualifiers]) -AC_CC_OPT([-Wno-error=format-truncation],[-Wno-error=format-truncation]) +if test "x$enable_warnings_as_errors" = "xyes" ; then + AC_CC_OPT([-Werror=all],[-Werror=all]) + AC_CC_OPT([-Werror=extra],[-Werror=extra]) + AC_CC_OPT([-Werror=discarded-qualifiers],[-Werror=discarded-qualifiers]) + AC_CC_OPT([-Wno-error=format-truncation],[-Wno-error=format-truncation]) +fi AC_CC_OPT([-MD -MT conftest.o -MF /dev/null -MP],[-MD -MT \$@ -MF .\$(subst /,_,\$@).d -MP]) AC_DEFUN([AC_CHECK_DESTDIR], [ @@ -226,7 +235,7 @@ AC_CHECK_FUNCS([memchr memmove memset memmem strcasecmp strchr strncasecmp strto dnl Apply options to defines if test "x$enable_debug" = "xyes" ; then - CFLAGS="$CFLAGS -O0 -g" + CFLAGS="$CFLAGS -O0 -g3" AC_DEFINE([__DEBUG__], [1], [Build with debugging]) else CFLAGS="$CFLAGS -O6" From 5c885614cf6376f956fd890b1fe05a1f94bb2179 Mon Sep 17 00:00:00 2001 From: Deomid Ryabkov Date: Wed, 29 Sep 2021 09:05:05 +0100 Subject: [PATCH 109/117] Remove use_ucs2_encoding (#146) It's no longer used --- at_response.c | 12 ------------ chan_dongle.c | 1 - chan_dongle.h | 3 --- cli.c | 1 - manager.c | 1 - 5 files changed, 18 deletions(-) diff --git a/at_response.c b/at_response.c index 2a9eb2f2..e42abfb5 100644 --- a/at_response.c +++ b/at_response.c @@ -139,12 +139,6 @@ static int at_response_ok (struct pvt* pvt, at_res_t res) ast_debug (1, "[%s] SMS operation mode set to PDU\n", PVT_ID(pvt)); break; - case CMD_AT_CSCS: - ast_debug (1, "[%s] UCS-2 text encoding enabled\n", PVT_ID(pvt)); - - pvt->use_ucs2_encoding = 1; - break; - case CMD_AT_CPMS: ast_debug (1, "[%s] SMS storage location is established\n", PVT_ID(pvt)); break; @@ -415,12 +409,6 @@ static int at_response_error (struct pvt* pvt, at_res_t res) } break; - case CMD_AT_CSCS: - ast_debug (1, "[%s] No UCS-2 encoding support\n", PVT_ID(pvt)); - - pvt->use_ucs2_encoding = 0; - break; - case CMD_AT_A: case CMD_AT_CHLD_2x: log_cmd_response_error(pvt, ecmd, "[%s] Answer failed for call idx %d\n", PVT_ID(pvt), task->cpvt->call_idx); diff --git a/chan_dongle.c b/chan_dongle.c index c0f649ca..1a86380f 100644 --- a/chan_dongle.c +++ b/chan_dongle.c @@ -305,7 +305,6 @@ static void disconnect_dongle (struct pvt* pvt) // else { /* unaffected in case of restart */ - pvt->use_ucs2_encoding = 0; pvt->gsm_reg_status = -1; pvt->rssi = 0; pvt->linkmode = 0; diff --git a/chan_dongle.h b/chan_dongle.h index 20f87735..f2dbe93f 100644 --- a/chan_dongle.h +++ b/chan_dongle.h @@ -148,9 +148,6 @@ typedef struct pvt unsigned long channel_instance; /*!< number of channels created on this device */ unsigned int rings; /*!< ring/ccwa number distributed to at_response_clcc() */ - /* device caps */ - unsigned int use_ucs2_encoding:1; - /* device state */ int gsm_reg_status; int rssi; diff --git a/cli.c b/cli.c index c1c96846..a19ef902 100644 --- a/cli.c +++ b/cli.c @@ -215,7 +215,6 @@ static char* cli_show_device_state (struct ast_cli_entry* e, int cmd, struct ast ast_cli (a->fd, " Cell ID : %s\n", pvt->cell_id); 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, " 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" ); diff --git a/manager.c b/manager.c index f0ddf407..789dcb21 100644 --- a/manager.c +++ b/manager.c @@ -89,7 +89,6 @@ static int manager_show_devices (struct mansession* s, const struct message* m) astman_append (s, "CellID: %s\r\n", pvt->cell_id); 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, "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"); From 3d2a97f26ba84473a0926a9fe9a32e451a56bc36 Mon Sep 17 00:00:00 2001 From: Deomid Ryabkov Date: Wed, 29 Sep 2021 09:56:37 +0100 Subject: [PATCH 110/117] Fix GSM7 decoding on big endian targets (#145) The uint16_t bytes are the other way around. --- char_conv.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/char_conv.c b/char_conv.c index 64ae0cdc..403e1091 100644 --- a/char_conv.c +++ b/char_conv.c @@ -116,7 +116,13 @@ EXPORT_DEF void hexify(const uint8_t *in, size_t in_length, char *out) #/* */ static const uint8_t *get_char_gsm7_encoding(uint16_t c) { - int minor = c >> 8, major = c & 255; +#if __BYTE_ORDER == __BIG_ENDIAN + int minor = c & 0xff; + int major = c >> 8; +#else + int minor = c >> 8; + int major = c & 0xff; +#endif int subtab = LUT_GSM7_REV1[major]; if (subtab == -1) return LUT_GSM7_REV2_INV; return LUT_GSM7_REV2[subtab][minor]; @@ -218,7 +224,11 @@ EXPORT_DEF ssize_t gsm7_unpack_decode(const char *in, size_t in_nibbles, uint16_ esc = 1; } else { esc = 0; +#if __BYTE_ORDER == __BIG_ENDIAN + out[x++] = val; +#else out[x++] = ((val & 0xff) << 8) | (val >> 8); +#endif } } } From 3d046f7d6842298c6838b5ce5b51d495d383b158 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Tue, 5 Oct 2021 20:51:13 +0200 Subject: [PATCH 111/117] Do not choke on failed AT^CVOICE? or AT+QPCMV? during startup ATQ_CMD_DECLARE_STI adds ATQ_CMD_FLAG_IGNORE so the error response on either command does not abort the entire initialization task. Without it, we migh get: [AT+QPCMV?] remove command 'AT+QPCMV' expected response 'OK' real 'ERROR' cmd 16/22 flags 0x01 from queue remove task with 22 command(s) begin with 'AT' expected response 'OK' from queue where the failed AT+QPCMV? aborts the 22-item init task. Closes #148, reported and tested by @AlexHighTower. --- at_command.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/at_command.c b/at_command.c index 7e7ad0da..5f0137ee 100644 --- a/at_command.c +++ b/at_command.c @@ -149,8 +149,8 @@ EXPORT_DEF int at_enqueue_initialization(struct cpvt *cpvt, at_cmd_t from_comman ATQ_CMD_DECLARE_STI(CMD_AT_CNUM, "AT+CNUM\r"), /* read the current voice mode, and return sampling * rate, data bit, frame period */ - ATQ_CMD_DECLARE_ST(CMD_AT_CVOICE, "AT^CVOICE?\r"), - ATQ_CMD_DECLARE_ST(CMD_AT_QPCMV, "AT+QPCMV?\r"), /* for Quectel */ + ATQ_CMD_DECLARE_STI(CMD_AT_CVOICE, "AT^CVOICE?\r"), + ATQ_CMD_DECLARE_STI(CMD_AT_QPCMV, "AT+QPCMV?\r"), /* for Quectel */ /* Get SMS Service center address */ ATQ_CMD_DECLARE_ST(CMD_AT_CSCA, "AT+CSCA?\r"), From 5a9cdc980cf1f296b6f1b143063101920cf25759 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Mon, 17 Jan 2022 09:00:02 +0100 Subject: [PATCH 112/117] Remove probably-useless code at voice init failure > i think these lines can also be removed, since ATQ_CMD_DECLARE_STI > will plow ahead anyway, no need to manually restart the sequence. Dixit, @rojer in #148. I don't know. Sounds plausible. --- at_response.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/at_response.c b/at_response.c index 388a1efa..1832daec 100644 --- a/at_response.c +++ b/at_response.c @@ -373,16 +373,6 @@ static int at_response_error (struct pvt* pvt, at_res_t res) if (pvt->has_voice == 0) { ast_log(LOG_WARNING, "[%s] Dongle has NO voice support\n", PVT_ID(pvt)); - - if (!pvt->initialized) - { - /* continue initialization in other job at cmd CMD_AT_CMGF */ - if (at_enqueue_initialization(task->cpvt, CMD_AT_CMGF)) - { - log_cmd_response_error(pvt, ecmd, "[%s] Error schedule initialization commands\n", PVT_ID(pvt)); - goto e_return; - } - } } break; /* From f73a2b327b4c687e82bfa9a05804a01d953147ef Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Mon, 17 Jan 2022 09:05:11 +0100 Subject: [PATCH 113/117] Add autoheader to ./bootstrap; add HAVE_LIBSQLITE3; error on smsdb init fail --- bootstrap | 1 + chan_dongle.c | 4 +++- config.h.in | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/bootstrap b/bootstrap index b8ca005a..4b65f524 100755 --- a/bootstrap +++ b/bootstrap @@ -2,6 +2,7 @@ aclocal autoconf +autoheader # We don't use Makefile.am, but we *do* use automake to add # "missing" files used by configure. diff --git a/chan_dongle.c b/chan_dongle.c index 1a86380f..06886d87 100644 --- a/chan_dongle.c +++ b/chan_dongle.c @@ -1766,7 +1766,9 @@ static int public_state_init(struct public_state * state) /* register our channel type */ if(ast_channel_register(&channel_tech) == 0) { - smsdb_init(); + if (smsdb_init() != 0) { + ast_log(LOG_ERROR, "Unable to init smsdb\n"); + } cli_register(); app_register(); diff --git a/config.h.in b/config.h.in index 6e0ec346..da0ef9f8 100644 --- a/config.h.in +++ b/config.h.in @@ -21,6 +21,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H +/* Define to 1 if you have the `sqlite3' library (-lsqlite3). */ +#undef HAVE_LIBSQLITE3 + /* Define to 1 if you have the `memchr' function. */ #undef HAVE_MEMCHR From 73788100e18464a533e6692a10e5618aaf7e0131 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Mon, 17 Jan 2022 09:09:14 +0100 Subject: [PATCH 114/117] Add PRI_time_t in case we want to printf time_t values --- config.h.in | 12 ++++++++++++ configure.ac | 10 ++++++++++ 2 files changed, 22 insertions(+) diff --git a/config.h.in b/config.h.in index da0ef9f8..880891f8 100644 --- a/config.h.in +++ b/config.h.in @@ -120,6 +120,18 @@ /* Define to the version of this package. */ #undef PACKAGE_VERSION +/* printf format for time_t */ +#undef PRI_time_t + +/* The size of `int', as computed by sizeof. */ +#undef SIZEOF_INT + +/* The size of `long int', as computed by sizeof. */ +#undef SIZEOF_LONG_INT + +/* The size of `time_t', as computed by sizeof. */ +#undef SIZEOF_TIME_T + /* Define to 1 if you have the ANSI C header files. */ #undef STDC_HEADERS diff --git a/configure.ac b/configure.ac index 7338187d..e6e7ec97 100644 --- a/configure.ac +++ b/configure.ac @@ -163,6 +163,16 @@ dnl AC_CHECK_TYPE(size_t, unsigned long) dnl AC_CHECK_TYPE(ssize_t, long) dnl AC_CHECK_TYPE(uint64_t, unsigned long long) +AC_CHECK_SIZEOF(int) +AC_CHECK_SIZEOF(long int) +AC_CHECK_SIZEOF(time_t) +case "$ac_cv_sizeof_time_t" in +''|0) AC_MSG_ERROR([Could not find time_t type]);; +$ac_cv_sizeof_int) AC_DEFINE([PRI_time_t], ["d"], [printf format for time_t]);; +$ac_cv_sizeof_long_int) AC_DEFINE([PRI_time_t], ["ld"], [printf format for time_t]);; +*) AC_MSG_ERROR([Could not find match size of time_t to printf format]) +esac + dnl checking compiler options AC_DEFUN([AC_CC_OPT], [ my_save_cflags="$CFLAGS" From 57e025dda46d2a9e2eba8cb45b75d1b573efc8fb Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Mon, 17 Jan 2022 09:17:54 +0100 Subject: [PATCH 115/117] Fix formatting in error.h --- error.h | 42 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/error.h b/error.h index 800da610..86c61209 100644 --- a/error.h +++ b/error.h @@ -4,7 +4,7 @@ #ifndef ERROR_H_INCLUDED #define ERROR_H_INCLUDED -#include "export.h" /* EXPORT_DECL EXPORT_DEF */ +#include "export.h" /* EXPORT_DECL EXPORT_DEF */ #include "mutils.h" enum error { @@ -18,7 +18,7 @@ enum error { E_PARSE_UCS2, E_ENCODE_GSM7, E_PACK_GSM7, - E_DECODE_GSM7, + E_DECODE_GSM7, /* 10 */ E_SMSDB, E_QUEUE, E_BUILD_PDU, @@ -28,7 +28,7 @@ enum error { E_MALFORMED_HEXSTR, E_INVALID_SCA, E_INVALID_TPDU_TYPE, - E_PARSE_TPDU, + E_PARSE_TPDU, /* 20 */ E_INVALID_TIMESTAMP, E_INVALID_CHARSET, E_BUILD_SCA, @@ -39,12 +39,38 @@ enum error { 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" + "Unknown error", /* 0 */ + "Device disabled", + "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", /* 10 */ + "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", /* 20 */ + "Invalid timestamp", + "Invalid charset", + "Cannot build SCA", + "Cannot build phone number", + "Input too large" }; - return enum2str(err, errors, ITEMS_OF(errors)); + return enum2str(err, + errors, + ITEMS_OF(errors)); } EXPORT_DECL __thread int chan_dongle_err; From 503dba87d726854b74b49e70679e64e6e86d5812 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Fri, 4 Nov 2022 10:00:58 +0100 Subject: [PATCH 116/117] Revert "Updated interfaces for E171" This reverts commit f1517b427f7542f278ba6b8e504c5debb563bd9a. See discussion on: https://github.com/wdoekes/asterisk-chan-dongle/commit/f1517b427f7542f278ba6b8e504c5debb563bd9a There @Vald3m argues that the old values were correct. --- pdiscovery.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pdiscovery.c b/pdiscovery.c index c599c232..6dd3f9f1 100644 --- a/pdiscovery.c +++ b/pdiscovery.c @@ -65,9 +65,9 @@ static const struct pdiscovery_device device_ids[] = { { 0x12d1, 0x1001, { 2, 1, /* 0 */ } }, /* E1550 and generic */ // { 0x12d1, 0x1465, { 2, 1, /* 0 */ } }, /* K3520 */ { 0x12d1, 0x140c, { 3, 2, /* 0 */ } }, /* E17xx */ - { 0x12d1, 0x14ac, { 4, 3, /* 0 */ } }, /* E153Du-1 : thanks mghadam */ + { 0x12d1, 0x14ac, { 4, 3, /* 0 */ } }, /* E153Du-1 : thanks mghadam */ { 0x12d1, 0x1436, { 4, 3, /* 0 */ } }, /* E1750 */ - { 0x12d1, 0x1506, { 3, 2, /* 0 */ } }, /* E171 firmware 21.x : thanks Sergey Ivanov */ + { 0x12d1, 0x1506, { 1, 2, /* 0 */ } }, /* E171 firmware 21.x : thanks Sergey Ivanov */ { 0x2c7c, 0x0125, { 1, 4, /* 0 */ } }, /* Dongle EC25-A LTE modem */ }; From 13450af5f648ddf4bc279c08e53917f503253bdd Mon Sep 17 00:00:00 2001 From: micmac1 Date: Tue, 26 Dec 2023 17:51:27 +0100 Subject: [PATCH 117/117] Consider long long (lld) for time_t (#173) Some libc implementations are shifting toward using 64-bit time_t for all arches to solve the year 2038 problem. musl is one of them, see [1]. So when compiling for a 32-bit arch time_t will be long long. This commit adds this option. [1] https://musl.libc.org/time64.html Signed-off-by: Sebastian Kemper --- configure.ac | 2 ++ 1 file changed, 2 insertions(+) diff --git a/configure.ac b/configure.ac index e6e7ec97..1dc7ca7e 100644 --- a/configure.ac +++ b/configure.ac @@ -165,11 +165,13 @@ dnl AC_CHECK_TYPE(uint64_t, unsigned long long) AC_CHECK_SIZEOF(int) AC_CHECK_SIZEOF(long int) +AC_CHECK_SIZEOF(long long int) AC_CHECK_SIZEOF(time_t) case "$ac_cv_sizeof_time_t" in ''|0) AC_MSG_ERROR([Could not find time_t type]);; $ac_cv_sizeof_int) AC_DEFINE([PRI_time_t], ["d"], [printf format for time_t]);; $ac_cv_sizeof_long_int) AC_DEFINE([PRI_time_t], ["ld"], [printf format for time_t]);; +$ac_cv_sizeof_long_long_int) AC_DEFINE([PRI_time_t], ["lld"], [printf format for time_t]);; *) AC_MSG_ERROR([Could not find match size of time_t to printf format]) esac