#include #include #include #include #include "eos.h" #include "net.h" #include "unicode.h" #include "gsm.h" #include "at_cmd.h" #include "cell.h" #define CTRL_Z 0x1a static const char *TAG = "EOS SMS"; static char _pdu_in[2048]; static char _pdu_out[2048]; extern char *at_cmd_buf; static ssize_t sms_encode(unsigned char *buffer, uint16_t buf_len, char *pdu, size_t pdu_size) { ucp_t ch; size_t pdu_len; ssize_t rv; int i; char *addr; uint8_t addr_type; size_t addr_len; uint8_t txt[GSM_UD_SIZE]; size_t txt_len; uint16_t flags; if (buf_len < 2) return EOS_ERR_SIZE; flags = buffer[0] << 8; flags |= buffer[1]; buffer += 2; buf_len -= 2; if (buf_len < 2) return EOS_ERR_SIZE; switch (buffer[0]) { case EOS_CELL_SMS_ADDRTYPE_INTL: addr_type = GSM_EXT | GSM_TON_INTERNATIONAL | GSM_NPI_TELEPHONE; break; case EOS_CELL_SMS_ADDRTYPE_OTHER: addr_type = GSM_EXT | GSM_TON_UNKNOWN | GSM_NPI_TELEPHONE; break; default: return EOS_ERR; } addr_len = buffer[1]; addr = (char *)buffer + 2; if (buf_len < 2 + addr_len) return EOS_ERR_SIZE; buffer += 2 + addr_len; buf_len -= 2 + addr_len; i = 0; txt_len = 0; while (i < buf_len) { int _rv; if (buf_len - i < 4) { _rv = utf8_len_str(buffer + i); if (_rv < 0) return EOS_ERR; if (buf_len - i < _rv) return EOS_ERR_SIZE; } _rv = utf8_dec(buffer + i, &ch); if (_rv < 0) return EOS_ERR; if (ch >= 0xffff) return EOS_ERR; i += _rv; _rv = gsm_ucs2_to_7bit((char *)txt + txt_len, sizeof(txt) - txt_len, ch); if (_rv < 0) return EOS_ERR; txt_len += _rv; } if (pdu_size < 2) return EOS_ERR_SIZE; pdu_putc(pdu, 0); rv = gsm_sms_enc(pdu + 2, pdu_size - 2, addr, addr_len, addr_type, NULL, 0, txt, txt_len, GSM_ENC_7BIT, flags); if (rv < 0) return EOS_ERR; pdu_len = rv + 2; return pdu_len; } static ssize_t sms_decode(char *pdu, size_t pdu_len, unsigned char *buf, uint16_t buf_size) { uint16_t buf_len = 0; ssize_t rv; int i, j; char smsc_addr[GSM_ADDR_SIZE]; size_t smsc_addr_len; uint8_t smsc_info, smsc_info_len, smsc_addr_type; char orig_addr[GSM_ADDR_SIZE]; size_t orig_addr_len; uint8_t orig_addr_type; uint8_t udh[GSM_UDH_SIZE]; size_t udh_len; uint8_t txt[GSM_UD_SIZE]; size_t txt_len; uint8_t txt_enc; char smsc_timestamp[GSM_TS_SIZE]; uint16_t flags; if (pdu_len < 2) return EOS_ERR_SIZE; rv = pdu_getc(pdu, &smsc_info); if (rv < 0) return rv; smsc_info_len = 2 * (smsc_info + 1); if (pdu_len < smsc_info_len) return EOS_ERR_SIZE; if (smsc_info > 1) { /* SMSC address - currently not used */ pdu_putc(pdu, (smsc_info - 1) * 2); rv = gsm_addr_dec(pdu, pdu_len, smsc_addr, &smsc_addr_len, &smsc_addr_type); if (rv < 0) smsc_addr_len = 0; } rv = gsm_sms_dec(pdu + smsc_info_len, pdu_len - smsc_info_len, orig_addr, &orig_addr_len, &orig_addr_type, udh, &udh_len, txt, &txt_len, &txt_enc, smsc_timestamp, &flags); if ((rv == GSM_ERR_NOTSUPPORTED) || (txt_enc == GSM_ENC_8BIT)) { ESP_LOGE(TAG, "Message not supported: %s", pdu); return EOS_ERR_NOTSUPPORTED; } if (rv < 0) return EOS_ERR; if (buf_size < buf_len + 2) return EOS_ERR_SIZE; buf[0] = flags >> 8; buf[1] = flags; buf_len += 2; if (buf_size < buf_len + GSM_TS_SIZE) return EOS_ERR_SIZE; memcpy(buf + buf_len, smsc_timestamp, GSM_TS_SIZE); buf_len += GSM_TS_SIZE; if ((orig_addr_type & GSM_TON) == GSM_TON_ALPHANUMERIC) { if (buf_size < buf_len + 2) return EOS_ERR_SIZE; buf[buf_len] = EOS_CELL_SMS_ADDRTYPE_ALPHA; buf[buf_len + 1] = 0; buf_len += 2; i = 0; j = 0; while (i < orig_addr_len) { uint16_t ch; int _rv; _rv = gsm_7bit_to_ucs2((char *)orig_addr + i, orig_addr_len - i, &ch); if (_rv < 0) return EOS_ERR; i += _rv; if (buf_size - buf_len - j < 4) { _rv = utf8_len_ch(ch); if (_rv < 0) return EOS_ERR; if (buf_size - buf_len - j < _rv) return EOS_ERR_SIZE; } _rv = utf8_enc(ch, buf + buf_len + j); if (_rv < 0) return EOS_ERR; j += _rv; } buf[buf_len - 1] = j; buf_len += j; } else { if (buf_size < buf_len + 2) return EOS_ERR_SIZE; buf[buf_len] = ((orig_addr_type & GSM_TON) == GSM_TON_INTERNATIONAL) ? EOS_CELL_SMS_ADDRTYPE_INTL : EOS_CELL_SMS_ADDRTYPE_OTHER; buf[buf_len + 1] = orig_addr_len; buf_len += 2; if (buf_size < buf_len + orig_addr_len) return EOS_ERR_SIZE; memcpy(buf + buf_len, orig_addr, orig_addr_len); buf_len += orig_addr_len; } i = 0; j = 0; while (i < txt_len) { ucp_t ch; int _rv; if (txt_enc == GSM_ENC_7BIT) { uint16_t _ch; _rv = gsm_7bit_to_ucs2((char *)txt + i, txt_len - i, &_ch); if (_rv < 0) return EOS_ERR; ch = _ch; } else { if (txt_len - i < 2) return EOS_ERR_SIZE; if (txt_len - i < 4) { _rv = utf16_len_str(txt + i); if (_rv < 0) return EOS_ERR; if (txt_len - i < _rv) return EOS_ERR_SIZE; } _rv = utf16_dec(txt + i, &ch); if (_rv < 0) return EOS_ERR; } i += _rv; if (buf_size - buf_len - j < 4) { _rv = utf8_len_ch(ch); if (_rv < 0) return EOS_ERR; if (buf_size - buf_len - j < _rv) return EOS_ERR_SIZE; } _rv = utf8_enc(ch, buf + buf_len + j); if (_rv < 0) return EOS_ERR; j += _rv; } if (buf_size < buf_len + j + 1) return EOS_ERR_SIZE; buf[buf_len + j] = '\0'; buf_len += j + 1; return buf_len; } void eos_cell_sms_handler(unsigned char mtype, unsigned char *buffer, uint16_t buf_len) { int rv; buffer++; buf_len--; switch (mtype) { case EOS_CELL_MTYPE_SMS_LIST: { if (buf_len < 1) return; rv = snprintf(at_cmd_buf, AT_SIZE_CMD_BUF, "AT+CMGL=%d\r", buffer[0]); if ((rv < 0) || (rv >= AT_SIZE_CMD_BUF)) return; rv = eos_modem_take(1000); if (rv) return; at_cmd(at_cmd_buf); do { unsigned char *buf; uint16_t buf_len; char *pdu = _pdu_in; size_t pdu_size = sizeof(_pdu_in); size_t pdu_len; ssize_t _rv; rv = at_expect("^\\+CMGL: [0-9]+,[0-9],.*,[0-9]+$", "^OK", 1000); if (rv) break; rv = eos_modem_readln(pdu, pdu_size, 1000); if (rv) break; pdu_len = strlen(pdu); buf = eos_net_alloc(); buf[0] = EOS_CELL_MTYPE_SMS | EOS_CELL_MTYPE_SMS_LIST_ITEM; _rv = sms_decode(pdu, pdu_len, buf + 1, EOS_NET_SIZE_BUF - 1); if (_rv < 0) { eos_net_free(buf); continue; } buf_len = _rv; eos_net_send(EOS_NET_MTYPE_CELL, buf, buf_len + 1); } while (1); eos_modem_give(); break; } case EOS_CELL_MTYPE_SMS_MSG: { char b[4]; char *pdu = _pdu_in; size_t pdu_size = sizeof(_pdu_in); size_t pdu_len; ssize_t _rv; _rv = sms_encode(buffer, buf_len, pdu, pdu_size); if (_rv < 0) return; pdu_len = _rv; if (pdu_size < pdu_len + 2) return; pdu[pdu_len] = CTRL_Z; pdu[pdu_len + 1] = '\0'; rv = snprintf(at_cmd_buf, AT_SIZE_CMD_BUF, "AT+CMGS=%d\r", pdu_len / 2); if ((rv < 0) || (rv >= AT_SIZE_CMD_BUF)) return; rv = eos_modem_take(1000); if (rv) return; at_cmd(at_cmd_buf); // wait for: '> ' (0d 0a 3e 20) eos_modem_read(b, 4, 1000); at_cmd(pdu); rv = at_expect("^\\+CMGS: [0-9]+", "^ERROR", 5000); if (!rv) rv = at_expect("^OK", "^ERROR", 1000); eos_modem_give(); break; } } } static void sms_received_handler(char *urc, regmatch_t m[]) { char cmd[32]; unsigned char *buf; uint16_t buf_len; int ref, rv; char *pdu = _pdu_out; size_t pdu_size = sizeof(_pdu_out); size_t pdu_len; ssize_t _rv; sscanf(urc + m[1].rm_so, "%d", &ref); rv = snprintf(cmd, sizeof(cmd), "AT+CMGR=%d\r", ref); if ((rv < 0) || (rv >= sizeof(cmd))) return; at_cmd(cmd); rv = at_expect("^\\+CMGR: [0-9],.*,[0-9]+$", "^ERROR", 1000); if (rv) return; rv = eos_modem_readln(pdu, pdu_size, 1000); if (rv) return; pdu_len = strlen(pdu); rv = at_expect("^OK", NULL, 1000); buf = eos_net_alloc(); buf[0] = EOS_CELL_MTYPE_SMS | EOS_CELL_MTYPE_SMS_MSG; _rv = sms_decode(pdu, pdu_len, buf + 1, EOS_NET_SIZE_BUF - 1); if (_rv < 0) { eos_net_free(buf); return; } buf_len = _rv; eos_net_send(EOS_NET_MTYPE_CELL, buf, buf_len + 1); } void eos_cell_sms_init(void) { at_urc_insert("^\\+CMTI: .*,([0-9]+)$", sms_received_handler, REG_EXTENDED); }