From fb355ce04e3b9251aebc88540bb6fa9e052a50c9 Mon Sep 17 00:00:00 2001 From: fujr Date: Mon, 21 Oct 2024 22:22:08 +0800 Subject: [PATCH] tom_modem: release a new at tool --- application/tom_modem/Makefile | 35 + application/tom_modem/src/Makefile | 28 + application/tom_modem/src/main.c | 201 +++++ application/tom_modem/src/main.h | 43 + application/tom_modem/src/modem_types.h | 125 +++ application/tom_modem/src/pdu_lib/Makefile | 21 + application/tom_modem/src/pdu_lib/pdu.c | 421 ++++++++++ application/tom_modem/src/pdu_lib/pdu.h | 52 ++ .../tom_modem/src/pdu_lib/pdu_decoder.c | 111 +++ .../tom_modem/src/pdu_lib/ucs2_to_utf8.c | 53 ++ application/tom_modem/src/utils.c | 777 ++++++++++++++++++ application/tom_modem/src/utils.h | 59 ++ 12 files changed, 1926 insertions(+) create mode 100644 application/tom_modem/Makefile create mode 100644 application/tom_modem/src/Makefile create mode 100644 application/tom_modem/src/main.c create mode 100644 application/tom_modem/src/main.h create mode 100644 application/tom_modem/src/modem_types.h create mode 100644 application/tom_modem/src/pdu_lib/Makefile create mode 100644 application/tom_modem/src/pdu_lib/pdu.c create mode 100644 application/tom_modem/src/pdu_lib/pdu.h create mode 100644 application/tom_modem/src/pdu_lib/pdu_decoder.c create mode 100644 application/tom_modem/src/pdu_lib/ucs2_to_utf8.c create mode 100644 application/tom_modem/src/utils.c create mode 100644 application/tom_modem/src/utils.h diff --git a/application/tom_modem/Makefile b/application/tom_modem/Makefile new file mode 100644 index 0000000..ab45083 --- /dev/null +++ b/application/tom_modem/Makefile @@ -0,0 +1,35 @@ +include $(TOPDIR)/rules.mk +include $(INCLUDE_DIR)/kernel.mk + +PKG_NAME:= tom_modem +PKG_RELEASE:=beta +PKG_VERSION:=0.9 + +include $(INCLUDE_DIR)/package.mk + +define Package/$(PKG_NAME) + SECTION:=utils + CATEGORY:=Utilities + TITLE:=Fujr Modem Communite Tool +endef + +define Package/$(PKG_NAME)/description + Modem Communite Tool for 5G modem (By Fujr) +endef + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) + $(CP) src/* $(PKG_BUILD_DIR)/ +endef + +define Build/Compile + $(MAKE) -C $(PKG_BUILD_DIR) \ + $(TARGET_CONFIGURE_OPTS) CFLAGS="$(TARGET_CFLAGS)" +endef + +define Package/$(PKG_NAME)/install + $(INSTALL_DIR) $(1)/usr/bin + $(INSTALL_BIN) $(PKG_BUILD_DIR)/tom_modem $(1)/usr/bin/ +endef + +$(eval $(call BuildPackage,$(PKG_NAME))) diff --git a/application/tom_modem/src/Makefile b/application/tom_modem/src/Makefile new file mode 100644 index 0000000..85f4a61 --- /dev/null +++ b/application/tom_modem/src/Makefile @@ -0,0 +1,28 @@ +TARGET = tom_modem + +CFLAGS = -Wall -Ipdu_lib + +SRCS = main.c utils.c pdu_lib/pdu.c pdu_lib/ucs2_to_utf8.c + +OBJS = $(SRCS:.c=.o) + +$(TARGET): $(OBJS) + $(CC) $(LDFLAGS) -o $@ $^ + +clean: + rm -f $(OBJS) $(TARGET) + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + + +.PHONY:clean +clean: + rm -rf *.o *.*~ *~ *.swap $(all) +depend: + $(CC) $(CFLAGS) -MM $(SRCS) > .depend + +# 包含依赖文件 +ifneq "$(wildcard .depend)" "" +include .depend +endif diff --git a/application/tom_modem/src/main.c b/application/tom_modem/src/main.c new file mode 100644 index 0000000..c74d944 --- /dev/null +++ b/application/tom_modem/src/main.c @@ -0,0 +1,201 @@ +#include "modem_types.h" +#include "main.h" +#include "utils.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +void _timeout(int signo) +{ + err_msg("Exit with Signal %d", signo); + kill(getpid(), SIGINT); +} + +int parse_user_input(int argc, char *argv[], PROFILE_T *profile) +{ + int opt = 1; + int option; + profile->sms_index = -1; +#define has_more_argv() (opt < argc ? 1 : 0) + while (opt < argc) + { + option = match_option(argv[opt]); + if (option == -1) + { + usage(); + return -1; + } + opt++; + switch (option) + { + case AT_CMD: + if (!has_more_argv()) + { + usage(); + return -1; + } + profile->at_cmd = argv[opt++]; + break; + case TTY_DEV: + if (!has_more_argv()) + { + usage(); + return -1; + } + profile->tty_dev = argv[opt++]; + break; + case BAUD_RATE: + if (!has_more_argv()) + { + usage(); + return -1; + } + profile->baud_rate = atoi(argv[opt++]); + break; + case DATA_BITS: + if (!has_more_argv()) + { + usage(); + return -1; + } + profile->data_bits = atoi(argv[opt++]); + break; + case PARITY: + if (!has_more_argv()) + { + usage(); + return -1; + } + profile->parity = argv[opt++]; + break; + case STOP_BITS: + if (!has_more_argv()) + { + usage(); + return -1; + } + profile->stop_bits = atoi(argv[opt++]); + break; + case FLOW_CONTROL: + if (!has_more_argv()) + { + usage(); + return -1; + } + profile->flow_control = argv[opt++]; + break; + case TIMEOUT: + if (!has_more_argv()) + { + usage(); + return -1; + } + profile->timeout = atoi(argv[opt++]); + break; + case OPERATION: + if (!has_more_argv()) + { + usage(); + return -1; + } + profile->op = match_operation(argv[opt++]); + break; + case DEBUG: + profile->debug = 1; + break; + case SMS_PDU: + if (!has_more_argv()) + { + usage(); + return -1; + } + profile->sms_pdu = argv[opt++]; + break; + case SMS_INDEX: + if (!has_more_argv()) + { + usage(); + return -1; + } + profile->sms_index = atoi(argv[opt++]); + break; + default: + err_msg("Invalid option: %s", argv[opt]); + break; + } + } + // default settings: + if (profile->baud_rate == 0 ) + { + profile->baud_rate = 115200; + } + if (profile->data_bits == 0) + { + profile->data_bits = 8; + } + if (profile->timeout == 0) + { + profile->timeout = 3; + } + if (profile->op == 0 || profile->op == -1) + { + profile->op = AT_OP; + } + +} + +int run_op(PROFILE_T *profile) +{ + switch (profile->op) + { + case AT_OP: + at(profile); + break; + case SMS_READ_OP: + sms_read(profile); + break; + case SMS_SEND_OP: + sms_send(profile); + break; + case SMS_DELETE_OP: + sms_delete(profile); + break; + default: + err_msg("Invalid operation"); + break; + } +} + +int main(int argc, char *argv[]) +{ + int ret; + // init + self_name = argv[0]; + PROFILE_T *profile = &s_profile; + parse_user_input(argc, argv, profile); + dump_profile(); + signal(SIGALRM, _timeout); + + // try open tty devices + if (open_tty_device(profile)) + { + err_msg("Failed to open tty device"); + return -1; + } + if (run_op(profile)) + { + err_msg("Failed to run operation %d", profile->op); + return -1; + } + + dbg_msg("Exit"); + return 0; +} diff --git a/application/tom_modem/src/main.h b/application/tom_modem/src/main.h new file mode 100644 index 0000000..4182697 --- /dev/null +++ b/application/tom_modem/src/main.h @@ -0,0 +1,43 @@ +#ifndef _MAIN_H_ +#define _MAIN_H_ +#include "modem_types.h" +#include "main.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEFAULT_TIMEOUT 3 +// + +FILE *fdi; // file descriptor for input +FILE *fdo; // file descriptor for output +int tty_fd; // file descriptor for tty device + +PROFILE_T s_profile; // global profile +char *self_name; // program name + +extern int at(PROFILE_T *profile); + +extern int sms_read(PROFILE_T *profile); + +extern int sms_send(PROFILE_T *profile); + +extern int sms_delete(PROFILE_T *profile); + +extern void dump_profile(); + +extern int match_option(char *option_name); + +extern int match_operation(char *operation_name); + +extern int open_tty_device(PROFILE_T *profile); + +extern int usage(); + +#endif diff --git a/application/tom_modem/src/modem_types.h b/application/tom_modem/src/modem_types.h new file mode 100644 index 0000000..6c19849 --- /dev/null +++ b/application/tom_modem/src/modem_types.h @@ -0,0 +1,125 @@ + +#ifndef _MODEM_TYPES_H_ +#define _MODEM_TYPES_H_ + +//options +#define AT_CMD_S 'c' +#define TTY_DEV_S 'd' +#define BAUD_RATE_S 'b' +#define DATA_BITS_S 'B' +#define PARITY_S 'P' +#define STOP_BITS_S 'S' +#define FLOW_CONTROL_S 'F' +#define TIMEOUT_S 't' +#define OPERATION_S 'o' +#define DEBUG_S 'D' +#define SMS_PDU_S 'p' +#define SMS_INDEX_S 'i' + +#define AT_CMD_L "at_cmd" +#define TTY_DEV_L "tty_dev" +#define BAUD_RATE_L "baud_rate" +#define DATA_BITS_L "data_bits" +#define PARITY_L "parity" +#define STOP_BITS_L "stop_bits" +#define FLOW_CONTROL_L "flow_control" +#define TIMEOUT_L "timeout" +#define OPERATION_L "operation" +#define DEBUG_L "debug" +#define SMS_PDU_L "sms_pdu" +#define SMS_INDEX_L "sms_index" + +//operations +#define AT_OP_S 'a' +#define AT_OP_L "at" +#define SMS_READ_OP_S 'r' +#define SMS_READ_OP_L "sms_read" +#define SMS_SEND_OP_S 's' +#define SMS_SEND_OP_L "sms_send" +#define SMS_DELETE_OP_S 'd' +#define SMS_DELETE_OP_L "sms_delete" + +#define SET_READ_STORAGE "AT+CPMS=\"%s\"" +#define SET_PDU_FORMAT "AT+CMGF=0" +#define READ_ALL_SMS "AT+CMGL=4" +#define SEND_SMS "AT+CMGS=%d" +#define DELETE_SMS "AT+CMGD=%d" + +#define SMS_BUF_SIZE 16384 +#define LINE_BUF 1024 +#define SMS_LIST_SIZE 128 +#define COMMON_BUF_SIZE 1024 +#define PHONE_NUMBER_SIZE 64 +#define SMS_TEXT_SIZE 256 +#define SMS_PDU_STR_SIZE 512 +#define SMS_PDU_HEX_SIZE 512 + +// at_tool profile +typedef struct _PROFILE { + // AT command + // TTY device + // Baud rate + // Data bits + // Parity + // Stop bits + // Flow control + // Timeout + // operation + // debug mode + char *at_cmd; + char *tty_dev; + int baud_rate; + int data_bits; + char *parity; + int stop_bits; + char *flow_control; + int timeout; + int op; + int debug; + char *sms_pdu; + int sms_index; +} PROFILE_T; + +typedef struct _SMS { + int sms_index; + int sms_lenght; + int ref_number; + int segment_number; + int timestamp; + int total_segments; + int type; + char *sender; + char *sms_text; + char *sms_pdu; +} SMS_T; + +enum SMS_CHARSET { + SMS_CHARSET_7BIT, + SMS_CHARSET_UCS2 +}; + +enum OPTIONS { + AT_CMD, + TTY_DEV, + BAUD_RATE, + DATA_BITS, + PARITY, + STOP_BITS, + FLOW_CONTROL, + TIMEOUT, + OPERATION, + DEBUG, + SMS_PDU, + SMS_INDEX +}; + +enum OPERATIONS { + NULL_OP, + AT_OP, + SMS_READ_OP, + SMS_SEND_OP, + SMS_DELETE_OP +}; + + +#endif diff --git a/application/tom_modem/src/pdu_lib/Makefile b/application/tom_modem/src/pdu_lib/Makefile new file mode 100644 index 0000000..4985007 --- /dev/null +++ b/application/tom_modem/src/pdu_lib/Makefile @@ -0,0 +1,21 @@ +ALL: pdu_decoder + +#CROSS_COMPILE=mips-openwrt-linux- + +#CC = $(CROSS_COMPILE)gcc + +#CFLAGS = -O2 + +pdu.o: + $(CC) $(CFLAGS) -c pdu.c +pdu_decoder.o: + $(CC) $(CFLAGS) -c pdu_decoder.c +ucs2_to_utf8: + $(CC) $(CFLAGS) -c ucs2_to_utf8.c +pdu_decoder: pdu.o pdu_decoder.o ucs2_to_utf8 + $(CC) $(CFLAGS) ucs2_to_utf8.o pdu.o pdu_decoder.o -o pdu_decoder +clean: + rm -f *.o pdu_decoder +test: clean ALL + echo "0891683108501405F8240BA10156686616F60008414090912385235C6D4191CF6C4752A860015BC67801FF1A00350030003900360036FF0C4EB2FF0C8BB05F9762BD59566BCF592990FD8981676554E6FF0C611F89C9597D76848BDD63A883507ED960A87684670B53CBFF019884795D60A84E2D5956FF01"|./pdu_decoder + echo "0891683108501405F8640BA10156686616F6000841400100957423830608048A3002026B21767B5F556D4191CF6C475373900100356D4191CF5E01FF0853EF63620035004D6D4191CFFF09FF0C731B623394FE63A5FF1A0068007400740070003A002F002F007300680061006B0065002E00730064002E006300680069006E0061006D006F00620069006C0065002E0063006F006D30025C714E1C79FB52A8"|./pdu_decoder diff --git a/application/tom_modem/src/pdu_lib/pdu.c b/application/tom_modem/src/pdu_lib/pdu.c new file mode 100644 index 0000000..7e140fc --- /dev/null +++ b/application/tom_modem/src/pdu_lib/pdu.c @@ -0,0 +1,421 @@ +/* + * 2017 - 2021 Cezary Jackiewicz + * 2014 lovewilliam + */ +// Copyright 2011 The Avalon Project Authors. All rights reserved. +// Use of this source code is governed by the Apache License 2.0 +// that can be found in the LICENSE file. +// +// SMS encoding/decoding functions, which are based on examples from: +// http://www.dreamfabric.com/sms/ + +#include "pdu.h" + +#include +#include + +enum { + BITMASK_7BITS = 0x7F, + BITMASK_8BITS = 0xFF, + BITMASK_HIGH_4BITS = 0xF0, + BITMASK_LOW_4BITS = 0x0F, + + TYPE_OF_ADDRESS_UNKNOWN = 0x81, + TYPE_OF_ADDRESS_INTERNATIONAL_PHONE = 0x91, + TYPE_OF_ADDRESS_NATIONAL_SUBSCRIBER = 0xC8, + TYPE_OF_ADDRESS_ALPHANUMERIC = 0xD0, + + SMS_DELIVER_ONE_MESSAGE = 0x04, + SMS_SUBMIT = 0x11, + + SMS_MAX_7BIT_TEXT_LENGTH = 160, +}; + +// Swap decimal digits of a number (e.g. 12 -> 21). +static unsigned char +SwapDecimalNibble(const unsigned char x) +{ + return (x / 16) + ((x % 16) * 10); +} + +// Encode/Decode PDU: Translate ASCII 7bit characters to 8bit buffer. +// SMS encoding example from: http://www.dreamfabric.com/sms/. +// +// 7-bit ASCII: "hellohello" +// [0]:h [1]:e [2]:l [3]:l [4]:o [5]:h [6]:e [7]:l [8]:l [9]:o +// 1101000 1100101 1101100 1101100 1101111 1101000 1100101 1101100 1101100 1101111 +// | ||| ||||| | ||||||| |||||| +// /-------------/ ///-------/// /////-///// \------------\ ||||||| \\\\\\ . +// | ||| ||||| | ||||||| |||||| +// input buffer position +// 10000000 22111111 33322222 44443333 55555333 66666655 77777776 98888888 --999999 +// | ||| ||||| | ||||||| |||||| +// 8bit encoded buffer +// 11101000 00110010 10011011 11111101 01000110 10010111 11011001 11101100 00110111 +// E8 32 9B FD 46 97 D9 EC 37 + + +// Encode PDU message by merging 7 bit ASCII characters into 8 bit octets. +int +EncodePDUMessage(const char* sms_text, int sms_text_length, unsigned char* output_buffer, int buffer_size) +{ + // Check if output buffer is big enough. + if ((sms_text_length * 7 + 7) / 8 > buffer_size) + return -1; + + int output_buffer_length = 0; + int carry_on_bits = 1; + int i = 0; + + for (; i < sms_text_length - 1; ++i) { + output_buffer[output_buffer_length++] = + ((sms_text[i] & BITMASK_7BITS) >> (carry_on_bits - 1)) | + ((sms_text[i + 1] & BITMASK_7BITS) << (8 - carry_on_bits)); + carry_on_bits++; + if (carry_on_bits == 8) { + carry_on_bits = 1; + ++i; + } + } + + if (i <= sms_text_length) + output_buffer[output_buffer_length++] = (sms_text[i] & BITMASK_7BITS) >> (carry_on_bits - 1); + + return output_buffer_length; +} + +// Decode PDU message by splitting 8 bit encoded buffer into 7 bit ASCII +// characters. +int +DecodePDUMessage_GSM_7bit(const unsigned char* buffer, int buffer_length, char* output_sms_text, int sms_text_length) +{ + int output_text_length = 0; + if (buffer_length > 0) + output_sms_text[output_text_length++] = BITMASK_7BITS & buffer[0]; + + if (sms_text_length > 1) { + int carry_on_bits = 1; + int i = 1; + for (; i < buffer_length; ++i) { + + output_sms_text[output_text_length++] = BITMASK_7BITS & ((buffer[i] << carry_on_bits) | (buffer[i - 1] >> (8 - carry_on_bits))); + + if (output_text_length == sms_text_length) break; + + carry_on_bits++; + + if (carry_on_bits == 8) { + carry_on_bits = 1; + output_sms_text[output_text_length++] = buffer[i] & BITMASK_7BITS; + if (output_text_length == sms_text_length) break; + } + + } + if (output_text_length < sms_text_length) // Add last remainder. + output_sms_text[output_text_length++] = buffer[i - 1] >> (8 - carry_on_bits); + } + + return output_text_length; +} + +#define GSM_7BITS_ESCAPE 0x1b + +static const unsigned char gsm7bits_to_latin1[128] = { + '@', 0xa3, '$', 0xa5, 0xe8, 0xe9, 0xf9, 0xec, 0xf2, 0xc7, '\n', 0xd8, 0xf8, '\r', 0xc5, 0xe5, + 0, '_', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xc6, 0xe6, 0xdf, 0xc9, + ' ', '!', '"', '#', 0xa4, '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', + 0xa1, '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', 0xc4, 0xd6, 0xd1, 0xdc, 0xa7, + 0xbf, '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', 0xe4, 0xf6, 0xf1, 0xfc, 0xe0, +}; + +static const unsigned char gsm7bits_extend_to_latin1[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, 0, 0, 0, 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 int +G7bitToAscii(char* buffer, int buffer_length) +{ + int i; + + for (i = 0; i buffer_size) + return -1; + + int i = 0; + for (; i < phone_number_length; ++i) { + + if (phone_number[i] < '0' && phone_number[i] > '9') + return -1; + + if (i % 2 == 0) { + output_buffer[output_buffer_length++] = BITMASK_HIGH_4BITS | (phone_number[i] - '0'); + } else { + output_buffer[output_buffer_length - 1] = + (output_buffer[output_buffer_length - 1] & BITMASK_LOW_4BITS) | + ((phone_number[i] - '0') << 4); + } + } + + return output_buffer_length; +} + +// Decode a digit based phone number for SMS based format. +static int +DecodePhoneNumber(const unsigned char* buffer, int phone_number_length, char* output_phone_number) +{ + int i = 0; + for (; i < phone_number_length; ++i) { + if (i % 2 == 0) + output_phone_number[i] = (buffer[i / 2] & BITMASK_LOW_4BITS) + '0'; + else + output_phone_number[i] = ((buffer[i / 2] & BITMASK_HIGH_4BITS) >> 4) + '0'; + } + output_phone_number[phone_number_length] = '\0'; // Terminate C string. + return phone_number_length; +} + +// Encode a SMS message to PDU +int +pdu_encode(const char* service_center_number, const char* phone_number, const char* sms_text, + unsigned char* output_buffer, int buffer_size) +{ + if (buffer_size < 2) + return -1; + + int output_buffer_length = 0; + + // 1. Set SMS center number. + int length = 0; + if (service_center_number && strlen(service_center_number) > 0) { + output_buffer[1] = TYPE_OF_ADDRESS_INTERNATIONAL_PHONE; + length = EncodePhoneNumber(service_center_number, + output_buffer + 2, buffer_size - 2); + if (length < 0 && length >= 254) + return -1; + length++; // Add type of address. + } + output_buffer[0] = length; + output_buffer_length = length + 1; + if (output_buffer_length + 4 > buffer_size) + return -1; // Check if it has space for four more bytes. + + // 2. Set type of message. + output_buffer[output_buffer_length++] = SMS_SUBMIT; + output_buffer[output_buffer_length++] = 0x00; // Message reference. + + // 3. Set phone number. + output_buffer[output_buffer_length] = strlen(phone_number); + + if (strlen(phone_number) < 6) { + output_buffer[output_buffer_length + 1] = TYPE_OF_ADDRESS_UNKNOWN; + } else { + output_buffer[output_buffer_length + 1] = TYPE_OF_ADDRESS_INTERNATIONAL_PHONE; + } + + length = EncodePhoneNumber(phone_number, + output_buffer + output_buffer_length + 2, + buffer_size - output_buffer_length - 2); + output_buffer_length += length + 2; + if (output_buffer_length + 4 > buffer_size) + return -1; // Check if it has space for four more bytes. + + + // 4. Protocol identifiers. + output_buffer[output_buffer_length++] = 0x00; // TP-PID: Protocol identifier. + output_buffer[output_buffer_length++] = 0x00; // TP-DCS: Data coding scheme. + output_buffer[output_buffer_length++] = 0xB0; // TP-VP: Validity: 10 days + + // 5. SMS message. + int sms_text_length = strlen(sms_text); + char sms_text_7bit[2*SMS_MAX_7BIT_TEXT_LENGTH]; + sms_text_length = AsciiToG7bit(sms_text, sms_text_length, sms_text_7bit); + if (sms_text_length > SMS_MAX_7BIT_TEXT_LENGTH) + return -1; + output_buffer[output_buffer_length++] = sms_text_length; + length = EncodePDUMessage(sms_text_7bit, sms_text_length, + output_buffer + output_buffer_length, + buffer_size - output_buffer_length); + if (length < 0) + return -1; + output_buffer_length += length; + + return output_buffer_length; +} + +int pdu_decode(const unsigned char* buffer, int buffer_length, + time_t* output_sms_time, + char* output_sender_phone_number, int sender_phone_number_size, + char* output_sms_text, int sms_text_size, + int* tp_dcs, + int* ref_number, + int* total_parts, + int* part_number, + int* skip_bytes) +{ + + if (buffer_length <= 0) + return -1; + + const int sms_deliver_start = 1 + buffer[0]; + if (sms_deliver_start + 1 > buffer_length) + return -2; + + const int user_data_header_length = (buffer[sms_deliver_start]>>4); + + const int sender_number_length = buffer[sms_deliver_start + 1]; + if (sender_number_length + 1 > sender_phone_number_size) + return -3; // Buffer too small to hold decoded phone number. + + const int sender_type_of_address = buffer[sms_deliver_start + 2]; + if (sender_type_of_address == TYPE_OF_ADDRESS_ALPHANUMERIC) { + int sender_len1 = DecodePDUMessage_GSM_7bit(buffer + sms_deliver_start + 3, (sender_number_length + 1) / 2, output_sender_phone_number, sender_number_length); + int sender_len2 = G7bitToAscii(output_sender_phone_number, sender_len1 - 1); + output_sender_phone_number[sender_len2] = 0; + } else { + DecodePhoneNumber(buffer + sms_deliver_start + 3, sender_number_length, output_sender_phone_number); + } + + const int sms_pid_start = sms_deliver_start + 3 + (buffer[sms_deliver_start + 1] + 1) / 2; + + // Decode timestamp. + struct tm sms_broken_time; + sms_broken_time.tm_year = 100 + SwapDecimalNibble(buffer[sms_pid_start + 2]); + sms_broken_time.tm_mon = SwapDecimalNibble(buffer[sms_pid_start + 3]) - 1; + sms_broken_time.tm_mday = SwapDecimalNibble(buffer[sms_pid_start + 4]); + sms_broken_time.tm_hour = SwapDecimalNibble(buffer[sms_pid_start + 5]); + sms_broken_time.tm_min = SwapDecimalNibble(buffer[sms_pid_start + 6]); + sms_broken_time.tm_sec = SwapDecimalNibble(buffer[sms_pid_start + 7]); + (*output_sms_time) = timegm(&sms_broken_time); + + const int sms_start = sms_pid_start + 2 + 7; + if (sms_start + 1 > buffer_length) return -1; // Invalid input buffer. + + int tmp; + if((user_data_header_length&0x04)==0x04) { + tmp = buffer[sms_start + 1] + 1; + *skip_bytes = tmp; + *ref_number = 0x000000FF&buffer[sms_start + tmp - 2]; + *total_parts = 0x000000FF&buffer[sms_start + tmp - 1]; + *part_number = 0x000000FF&buffer[sms_start + tmp]; + } else { + tmp = 0; + *skip_bytes = tmp; + *ref_number = tmp; + *total_parts = tmp; + *part_number = tmp; + } + + int output_sms_text_length = buffer[sms_start]; + if (sms_text_size < output_sms_text_length) return -1; // Cannot hold decoded buffer. + + const int sms_tp_dcs_start = sms_pid_start + 1; + *tp_dcs = buffer[sms_tp_dcs_start]; + + switch((*tp_dcs / 4) % 4) + { + case 0: + { + // GSM 7 bit + int decoded_sms_text_size = DecodePDUMessage_GSM_7bit(buffer + sms_start + 1, buffer_length - (sms_start + 1), + output_sms_text, output_sms_text_length); + if (decoded_sms_text_size != output_sms_text_length) return -1; // Decoder length is not as expected. + output_sms_text_length = G7bitToAscii(output_sms_text, output_sms_text_length); + break; + } + case 2: + { + // UCS2 + memcpy(output_sms_text, buffer + sms_start + 1, output_sms_text_length); + break; + } + default: + break; + } + + // Add a C string end. + if (output_sms_text_length < sms_text_size) + output_sms_text[output_sms_text_length] = 0; + else + output_sms_text[sms_text_size-1] = 0; + + return output_sms_text_length; +} diff --git a/application/tom_modem/src/pdu_lib/pdu.h b/application/tom_modem/src/pdu_lib/pdu.h new file mode 100644 index 0000000..734a393 --- /dev/null +++ b/application/tom_modem/src/pdu_lib/pdu.h @@ -0,0 +1,52 @@ +/* + * 2017 - 2021 Cezary Jackiewicz + * 2014 lovewilliam + */ +// Copyright 2011 The Avalon Project Authors. All rights reserved. +// Use of this source code is governed by the Apache License 2.0 +// that can be found in the LICENSE file. +#ifndef SMS_PDU_H_ +#define SMS_PDU_H_ + +#include + +enum { SMS_MAX_PDU_LENGTH = 256 }; + +/* + * Encode an SMS message. Output the encoded message into output pdu buffer. + * Returns the length of the SMS encoded message in the output buffer or + * a negative number in case encoding failed (for example provided output buffer + * does not have enough space). + */ +int pdu_encode(const char* service_center_number, const char* phone_number, const char* text, + unsigned char* pdu, int pdu_size); + +/* + * Decode an SMS message. Output the decoded message into the sms text buffer. + * Returns the length of the SMS dencoded message or a negative number in + * case encoding failed (for example provided output buffer has not enough + * space). + */ +int pdu_decode(const unsigned char* pdu, int pdu_len, + time_t* sms_time, + char* phone_number, int phone_number_size, + char* text, int text_size, + int* tp_dcs, + int* ref_number, + int* total_parts, + int* part_number, + int* skip_bytes); + +int ucs2_to_utf8 (int ucs2, unsigned char * utf8); + +int DecodePDUMessage_GSM_7bit(const unsigned char* buffer, + int buffer_length, + char* output_sms_text, + int sms_text_length); + +int EncodePDUMessage(const char* sms_text, + int sms_text_length, + unsigned char* output_buffer, + int buffer_size); + +#endif // SMS_SMS_H_ diff --git a/application/tom_modem/src/pdu_lib/pdu_decoder.c b/application/tom_modem/src/pdu_lib/pdu_decoder.c new file mode 100644 index 0000000..c9183d6 --- /dev/null +++ b/application/tom_modem/src/pdu_lib/pdu_decoder.c @@ -0,0 +1,111 @@ +/* + * 2014 lovewilliam + * SMS PDU Decoder + */ +#include "pdu.h" + +#include +#include +#include +#include +#include + +int ucs2_to_utf8 (int ucs2, unsigned char * utf8); + +int sms_decode() +{ + char buffer[2*SMS_MAX_PDU_LENGTH+4]; + char *p = buffer; + char t[2]; + int d; + do + { + t[0] = getchar(); + if(t[0]=='\n') + { + break; + } + t[1] = getchar(); + if(t[1]=='\n') + { + break; + } + *p = strtol(t,NULL,16); + p++; + }while(1); + + time_t sms_time; + char sms_phone[40]; + char sms_text[161]; + int tp_dcs_type; + int ref_number; + int total_parts; + int part_number; + int skip_bytes; + + int sms_text_length = pdu_decode((const unsigned char*)buffer, + sizeof(buffer), + &sms_time, + sms_phone, sizeof(sms_phone), + sms_text, sizeof(sms_text), + &tp_dcs_type, + &ref_number, + &total_parts, + &part_number, + &skip_bytes); + + printf("From:%s\n",sms_phone); + printf("Textlen=%d\n",sms_text_length); + char time_data_str[64]; + strftime(time_data_str,64,"%D %T", localtime(&sms_time)); + printf("Date/Time:%s\n",time_data_str); + + if (total_parts > 0) { + printf("Reference number: %d\n", ref_number); + printf("SMS segment %d of %d\n", part_number, total_parts); + } + + switch((tp_dcs_type / 4) % 4) + { + case 0: + { + // GSM7 bit + int i = skip_bytes; + if(skip_bytes > 0) i = (skip_bytes*8+6)/7; + for(;i + * from http://www.lemoda.net/c/ucs2-to-utf8/ucs2-to-utf8.c + */ +/* Input: a Unicode code point, "ucs2". + + Output: UTF-8 characters in buffer "utf8". + + Return value: the number of bytes written into "utf8", or -1 if + there was an error. + + This adds a zero byte to the end of the string. It assumes that the + buffer "utf8" has at least four bytes of space to write to. */ + +#define UNICODE_SURROGATE_PAIR -2 +#define UNICODE_BAD_INPUT -1 + +int ucs2_to_utf8 (int ucs2, unsigned char * utf8) +{ + if (ucs2 < 0x80) { + utf8[0] = ucs2; + utf8[1] = '\0'; + return 1; + } + if (ucs2 >= 0x80 && ucs2 < 0x800) { + utf8[0] = (ucs2 >> 6) | 0xC0; + utf8[1] = (ucs2 & 0x3F) | 0x80; + utf8[2] = '\0'; + return 2; + } + if (ucs2 >= 0x800 && ucs2 < 0xFFFF) { + if (ucs2 >= 0xD800 && ucs2 <= 0xDFFF) { + /* Ill-formed. */ + return UNICODE_SURROGATE_PAIR; + } + utf8[0] = ((ucs2 >> 12) ) | 0xE0; + utf8[1] = ((ucs2 >> 6 ) & 0x3F) | 0x80; + utf8[2] = ((ucs2 ) & 0x3F) | 0x80; + utf8[3] = '\0'; + return 3; + } + if (ucs2 >= 0x10000 && ucs2 < 0x10FFFF) { + /* http://tidy.sourceforge.net/cgi-bin/lxr/source/src/utf8.c#L380 */ + utf8[0] = 0xF0 | (ucs2 >> 18); + utf8[1] = 0x80 | ((ucs2 >> 12) & 0x3F); + utf8[2] = 0x80 | ((ucs2 >> 6) & 0x3F); + utf8[3] = 0x80 | ((ucs2 & 0x3F)); + utf8[4] = '\0'; + return 4; + } + return UNICODE_BAD_INPUT; +} + diff --git a/application/tom_modem/src/utils.c b/application/tom_modem/src/utils.c new file mode 100644 index 0000000..9bf323e --- /dev/null +++ b/application/tom_modem/src/utils.c @@ -0,0 +1,777 @@ +#include "utils.h" +#include "pdu_lib/pdu.h" + +int match_option(char *option_name) +{ + char short_option; + char *long_option; + // if start with '-' then it is an single character option + if (option_name[0] == '-' && option_name[1] != '-') + { + + short_option = option_name[1]; + switch (short_option) + { + case AT_CMD_S: + return AT_CMD; + case TTY_DEV_S: + return TTY_DEV; + case BAUD_RATE_S: + return BAUD_RATE; + case DATA_BITS_S: + return DATA_BITS; + case PARITY_S: + return PARITY; + case STOP_BITS_S: + return STOP_BITS; + case FLOW_CONTROL_S: + return FLOW_CONTROL; + case TIMEOUT_S: + return TIMEOUT; + case OPERATION_S: + return OPERATION; + case DEBUG_S: + return DEBUG; + case SMS_PDU_S: + return SMS_PDU; + case SMS_INDEX_S: + return SMS_INDEX; + default: + return -1; + } + } + if (option_name[0] == '-' && option_name[1] == '-') + { + long_option = option_name + 2; + if (strcmp(long_option, AT_CMD_L) == 0) + { + return AT_CMD; + } + else if (strcmp(long_option, TTY_DEV_L) == 0) + { + return TTY_DEV; + } + else if (strcmp(long_option, BAUD_RATE_L) == 0) + { + return BAUD_RATE; + } + else if (strcmp(long_option, DATA_BITS_L) == 0) + { + return DATA_BITS; + } + else if (strcmp(long_option, PARITY_L) == 0) + { + return PARITY; + } + else if (strcmp(long_option, STOP_BITS_L) == 0) + { + return STOP_BITS; + } + else if (strcmp(long_option, FLOW_CONTROL_L) == 0) + { + return FLOW_CONTROL; + } + else if (strcmp(long_option, TIMEOUT_L) == 0) + { + return TIMEOUT; + } + else if (strcmp(long_option, OPERATION_L) == 0) + { + return OPERATION; + } + else if (strcmp(long_option, DEBUG_L) == 0) + { + return DEBUG; + } + else if (strcmp(long_option, SMS_PDU_L) == 0) + { + return SMS_PDU; + } + else if (strcmp(long_option, SMS_INDEX_L) == 0) + { + return SMS_INDEX; + } + else + { + return -1; + } + } + // if start with '--' then it is a long option + return -1; +} + +int match_operation(char *operation_name) +{ + + char short_op; + int opstr_len = strlen(operation_name); + if (opstr_len == 1) + { + short_op = operation_name[0]; + switch (short_op) + { + case AT_OP_S: + return AT_OP; + case SMS_READ_OP_S: + return SMS_READ_OP; + case SMS_SEND_OP_S: + return SMS_SEND_OP; + case SMS_DELETE_OP_S: + return SMS_DELETE_OP; + default: + return -1; + break; + } + } + else if (opstr_len > 1) + { + if (strcmp(operation_name, AT_OP_L) == 0) + { + return AT_OP; + } + else if (strcmp(operation_name, SMS_READ_OP_L) == 0) + { + return SMS_READ_OP; + } + else if (strcmp(operation_name, SMS_SEND_OP_L) == 0) + { + return SMS_SEND_OP; + } + else if (strcmp(operation_name, SMS_DELETE_OP_L) == 0) + { + return SMS_DELETE_OP; + } + else + { + return -1; + } + } +} + +int open_tty_device(PROFILE_T *profile) +{ + tty_fd = open(profile->tty_dev, O_RDWR | O_NOCTTY); + if (tty_fd < 0) + { + err_msg("Error opening tty device: %s", profile->tty_dev); + return -1; + } + + if (set_tty_device(profile) != 0) + { + err_msg("Error setting tty device"); + return -1; + } + tcflush(tty_fd, TCIOFLUSH); + atexit(clean_up); + close(tty_fd); + tty_fd = open(profile->tty_dev, O_RDWR | O_NOCTTY); + fdi = fdopen(tty_fd, "r"); + fdo = fdopen(tty_fd, "w"); + if (fdi == NULL || fdo == NULL) + { + err_msg("Error opening file descriptor"); + return -1; + } + + if (setvbuf(fdo, NULL, _IOFBF, 0)) + { + err_msg("Error setting buffer for fdi"); + return -1; + } + + if (setvbuf(fdi, NULL, _IOLBF, 0)) + { + err_msg("Error setting buffer for fdi"); + return -1; + } + + return 0; +} + +static int set_tty_device(PROFILE_T *profile) +{ + int baud_rate, data_bits, stop_bits; + char *flow_control; + struct termios tty; + baud_rate = profile->baud_rate; + data_bits = profile->data_bits; + stop_bits = profile->stop_bits; + flow_control = profile->flow_control; + if (tcgetattr(tty_fd, &tty) != 0) + { + err_msg("Error getting tty attributes"); + return -1; + } + memmove(&oldtio, &tty, sizeof(struct termios)); + cfmakeraw(&tty); + tty.c_cflag |= CLOCAL; // 忽略调制解调器控制线,允许本地连接 + tty.c_cflag |= CREAD; // 使能接收 + + // clear flow control ,stop bits parity + tty.c_cflag &= ~CRTSCTS; + tty.c_cflag &= ~CSTOPB; + tty.c_cflag &= ~PARENB; + tty.c_oflag &= ~OPOST; + tty.c_cc[VMIN] = 1; + tty.c_cc[VTIME] = 0; + + // set data bits 5,6,7,8 + tty.c_cflag &= ~CSIZE; // 清除数据位设置 + switch (data_bits) + { + case 5: + tty.c_cflag |= CS5; + break; + case 6: + tty.c_cflag |= CS6; + break; + case 7: + tty.c_cflag |= CS7; + break; + case 8: + tty.c_cflag |= CS8; + break; + default: + tty.c_cflag |= CS8; + break; + } + + // set baud rate + switch (baud_rate) + { + case 4800: + cfsetspeed(&tty, B4800); + break; + case 9600: + cfsetspeed(&tty, B9600); + break; + case 19200: + cfsetspeed(&tty, B19200); + break; + case 38400: + cfsetspeed(&tty, B38400); + break; + case 57600: + cfsetspeed(&tty, B57600); + break; + case 115200: + cfsetspeed(&tty, B115200); + break; + + default: + cfsetspeed(&tty, B115200); + break; + } + if (tcsetattr(tty_fd, TCSANOW, &tty) != 0) + { + err_msg("Error setting tty attributes"); + return -1; + } + return 0; +} + +int char_to_hex(char c) +{ + // convert char to hex + int is_digit, is_lower, is_upper; + is_digit = c - '0'; + is_lower = c - 'a' + 10; + is_upper = c - 'A' + 10; + if (is_digit >= 0 && is_digit <= 9) + { + return is_digit; + } + else if (is_lower >= 10 && is_lower <= 15) + { + return is_lower; + } + else if (is_upper >= 10 && is_upper <= 15) + { + return is_upper; + } + else + { + return -1; + } +} + +static void clean_up() +{ + if (tcsetattr(tty_fd, TCSANOW, &oldtio) != 0) + { + err_msg("Error restoring old tty attributes"); + return; + } + dbg_msg("Clean up success"); + tcflush(tty_fd, TCIOFLUSH); + close(tty_fd); +} + +static void escape_json(char *input, char *output) +{ + char *p = input; + char *q = output; + while (*p) + { + if (*p == '"') + { + *q++ = '\\'; + *q++ = '"'; + } + else if (*p == '\\') + { + *q++ = '\\'; + *q++ = '\\'; + } + else if (*p == '/') + { + *q++ = '\\'; + *q++ = '/'; + } + else if (*p == '\b') + { + *q++ = '\\'; + *q++ = 'b'; + } + else if (*p == '\f') + { + *q++ = '\\'; + *q++ = 'f'; + } + else if (*p == '\n') + { + *q++ = '\\'; + *q++ = 'n'; + } + else if (*p == '\r') + { + *q++ = '\\'; + *q++ = 'r'; + } + else if (*p == '\t') + { + *q++ = '\\'; + *q++ = 't'; + } + else + { + *q++ = *p; + } + p++; + } + *q = '\0'; +} + +int usage() +{ + err_msg("Usage: %s [options]", self_name); + err_msg("Options:"); + err_msg(" -c, --at_cmd AT command"); + err_msg(" -d, --tty_dev TTY device **REQUIRED**"); + err_msg(" -b, --baud_rate Baud rate Default: 115200 Supported: 4800,9600,19200,38400,57600,115200"); + err_msg(" -B, --data_bits Data bits Default: 8 Supported: 5,6,7,8"); + err_msg(" -t, --timeout Timeout Default: 3"); + err_msg(" -o, --operation Operation(at[a:defualt], sms_read[r], sms_send[s], sms_delete[d])"); + err_msg(" -D, --debug Debug mode Default: off"); + err_msg(" -p, --sms_pdu SMS PDU"); + err_msg(" -i, --sms_index SMS index"); + err_msg("Example:"); + err_msg(" %s -c ATI -d /dev/ttyUSB2 -b 115200 -B 8 -o at #advance at mode set bautrate and data bit", self_name); + err_msg(" %s -c ATI -d /dev/ttyUSB2 # normal at mode", self_name); + err_msg(" %s -d /dev/mhi_DUN -o r # read sms", self_name); + exit(-1); +} + +void dump_profile() +{ + dbg_msg("AT command: %s", s_profile.at_cmd); + dbg_msg("TTY device: %s", s_profile.tty_dev); + dbg_msg("Baud rate: %d", s_profile.baud_rate); + dbg_msg("Data bits: %d", s_profile.data_bits); + dbg_msg("Parity: %s", s_profile.parity); + dbg_msg("Stop bits: %d", s_profile.stop_bits); + dbg_msg("Flow control: %s", s_profile.flow_control); + dbg_msg("Timeout: %d", s_profile.timeout); + dbg_msg("Operation: %d", s_profile.op); + dbg_msg("Debug: %d", s_profile.debug); + dbg_msg("SMS PDU: %s", s_profile.sms_pdu); + dbg_msg("SMS index: %d", s_profile.sms_index); +} + +int tty_read(FILE *fdi, char *output, int len, int timeout) +{ + return tty_read_keyword(fdi, output, len, timeout, NULL); +} + +int tty_read_keyword(FILE *fdi, char *output, int len, int timeout, char *key_word) +{ + int ret, fd; + fd_set rfds; + struct timeval tv; + char tmp[LINE_BUF] = {0}; + int msg_len = 0; + int key_word_len = 0; + fd = fileno(fdi); + tv.tv_sec = 0; + tv.tv_usec = timeout; + + FD_ZERO(&rfds); + FD_SET(fd, &rfds); + if (key_word != NULL) + { + key_word_len = strlen(key_word); + } + while (1) + { + ret = select(fd + 1, &rfds, NULL, NULL, &tv); + if (ret == -1) + { + if (errno == EINTR) + { + err_msg("Interrupted by signal"); + return -1; + } + err_msg("Error in select"); + return -1; + } + else + { + fgets(tmp, LINE_BUF, fdi); + if (output != NULL) + msg_len += snprintf(output + msg_len, len - msg_len, "%s", tmp); + dbg_msg("%s", tmp); + } + if (key_word != NULL){ + if (strncmp(tmp, key_word, key_word_len) == 0) + { + dbg_msg("Received end sign: %s", tmp); + return 0; + } + } + if (strncmp(tmp, "OK", 2) == 0 || + strncmp(tmp, "ERROR", 5) == 0 || + strncmp(tmp, "+CMS ERROR:", 11) == 0 || + strncmp(tmp, "NO CARRIER", 10) == 0){ + dbg_msg("Received end sign: %s", tmp); + if (key_word == NULL){ + return 0; + } + break; + } + } + + return -1; +} + +int tty_write(FILE *fdo, char *input) +{ + int cmd_len, ret; + char *cmd_line; + cmd_len = strlen(input) + 3; + cmd_line = (char *)malloc(cmd_len); + if (cmd_line == NULL) + { + err_msg("Error allocating memory"); + return -1; + } + snprintf(cmd_line, cmd_len, "%s\r\n", input); + ret = fputs(cmd_line, fdo); + free(cmd_line); + fflush(fdo); + usleep(100); + if (ret < 0) + { + err_msg("Error writing to tty %d" , ret); + return -1; + } + return 0; +} + +int at(PROFILE_T *profile) +{ + char output[COMMON_BUF_SIZE] = {0}; + if (profile->at_cmd == NULL) + { + err_msg("AT command is empty"); + return -1; + } + alarm(profile->timeout); + + if (tty_write(fdo, profile->at_cmd)) + { + err_msg("Error writing to tty"); + return -1; + } + + if (tty_read(fdi, output, COMMON_BUF_SIZE, 1000)) + { + err_msg("Error reading from tty"); + return -1; + } + user_msg("%s", output); + return 0; +} + +int sms_read(PROFILE_T *profile) +{ + SMS_T *sms_list[SMS_LIST_SIZE]; + SMS_T *sms; + char sms_pdu[SMS_BUF_SIZE] = {0}; + tty_write(fdo, SET_PDU_FORMAT); + alarm(profile->timeout); + if (tty_read_keyword(fdi, NULL, COMMON_BUF_SIZE, 1000, "OK")) + { + err_msg("Error setting PDU format"); + return -1; + } + dbg_msg("Set PDU format success"); + tty_write(fdo, READ_ALL_SMS); + alarm(profile->timeout); + if (tty_read_keyword(fdi, sms_pdu, SMS_BUF_SIZE, 1000, "OK")) + { + err_msg("Error reading SMS"); + return -1; + } + alarm(0); + + + //遍历 sms_pdu 的每一行 + char *line = strtok(sms_pdu, "\n"); + int sms_count = 0; + while (line != NULL) + { + + if (strncmp(line, "+CMGL:", 6) == 0) + { + //解析 line +CMGL: 2,1,,102 获取短信索引 + sms = (SMS_T *)malloc(sizeof(SMS_T)); + memset(sms, 0, sizeof(SMS_T)); + char *pdu = strtok(NULL, "\n"); + sms->sms_pdu = (char *)malloc(strlen(pdu)); + sms->sender = (char *)malloc(PHONE_NUMBER_SIZE); + sms->sms_text = (char *)malloc(SMS_TEXT_SIZE); + sms->sms_index = get_sms_index(line); + memcpy(sms->sms_pdu, pdu, strlen(pdu)); + int sms_len = decode(sms); + if (sms_len > 0) + { + sms_list[sms_count] = sms; + sms_count++; + } + else + { + destroy_sms(sms); + } + } + line = strtok(NULL, "\n"); + } + + // for (int i = 1; i <= sms_count; i++) + // { + // dump_sms(sms_list[i]); + // //destroy_sms(sms_list[i]); + // } + display_sms_in_json(sms_list,sms_count); + dbg_msg("Read SMS success"); + dbg_msg("%s", sms_pdu); + return 0; +} + +int sms_send(PROFILE_T *profile) +{ + if (profile->sms_pdu == NULL) + { + err_msg("SMS PDU is empty"); + return -1; + } + + int pdu_len = strlen(profile->sms_pdu); + int pdu_expected_len = (pdu_len) / 2 - 1; + char *send_sms_cmd; + char *write_pdu_cmd; + tty_write(fdo, SET_PDU_FORMAT); + alarm(profile->timeout); + if (tty_read_keyword(fdi, NULL, COMMON_BUF_SIZE, 1000, "OK")) + { + err_msg("Error setting PDU format"); + return -1; + } + dbg_msg("Set PDU format success"); + send_sms_cmd = (char *)malloc(32); + write_pdu_cmd = (char *)malloc(256); + snprintf(send_sms_cmd, 32, SEND_SMS, pdu_expected_len); + dbg_msg("Send SMS command: %s", send_sms_cmd); + snprintf(write_pdu_cmd, 256, "%s%c", profile->sms_pdu, 0x1A); + dbg_msg("Write PDU command: %s", write_pdu_cmd); + free(send_sms_cmd); + free(write_pdu_cmd); + alarm(0); + tty_write(fdo, send_sms_cmd); + sleep(1); + tty_write(fdo, write_pdu_cmd); + alarm(profile->timeout); + if (tty_read_keyword(fdi, NULL, COMMON_BUF_SIZE, 1000, "+CMGS:")) + { + err_msg("Error sending SMS STEP 2"); + return -1; + } + + + + + return 0; +} + +int sms_delete(PROFILE_T *profile) +{ + if (profile->sms_index < 0) + { + err_msg("SMS index is empty"); + return -1; + } + char *delete_sms_cmd; + delete_sms_cmd = (char *)malloc(32); + snprintf(delete_sms_cmd, 32, DELETE_SMS, profile->sms_index); + tty_write(fdo, delete_sms_cmd); + alarm(profile->timeout); + if (tty_read_keyword(fdi, NULL, COMMON_BUF_SIZE, 1000, "OK")) + { + err_msg("Error deleting SMS"); + return -1; + } + return 0; +} + +int decode(SMS_T *sms) +{ + char sms_text[SMS_TEXT_SIZE] = {0}; + int tp_dcs; + int skip_bytes; + int pdu_str_len; + time_t sms_time; + unsigned char hex_pdu[SMS_PDU_HEX_SIZE] = {0}; + pdu_str_len = strlen(sms->sms_pdu); + for (int i = 0; i < pdu_str_len; i += 2) + { + hex_pdu[i / 2] = char_to_hex(sms->sms_pdu[i]) << 4; + hex_pdu[i / 2] |= char_to_hex(sms->sms_pdu[i + 1]); + } + int sms_len = pdu_decode(hex_pdu, pdu_str_len/2, + &sms->timestamp, + sms->sender, PHONE_NUMBER_SIZE, + sms_text, SMS_TEXT_SIZE, + &tp_dcs, + &sms->ref_number, + &sms->total_segments, + &sms->segment_number, + &skip_bytes); + if (sms_len <= 0) + { + err_msg("Error decoding pdu"); + return sms_len; + } + sms->sms_lenght = sms_len; + + switch ((tp_dcs / 4) % 4) + { + case 0: + { + // GSM 7 bit + sms->type = SMS_CHARSET_7BIT; + int i; + i = skip_bytes; + if (skip_bytes > 0) + i = (skip_bytes * 8 + 6) / 7; + for (; i < strlen(sms_text); i++) + { + sprintf(sms->sms_text + i, "%c", sms_text[i]); + } + i++; + sprintf(sms->sms_text + i, "%c", '\0'); + break; + } + case 2: + { + // UCS2 + sms->type = SMS_CHARSET_UCS2; + int offset = 0; + for (int i = skip_bytes; i < SMS_TEXT_SIZE; i += 2) + { + int ucs2_char = 0x000000FF & sms_text[i + 1]; + ucs2_char |= (0x0000FF00 & (sms_text[i] << 8)); + unsigned char utf8_char[5]; + int len = ucs2_to_utf8(ucs2_char, utf8_char); + int j; + for (j = 0; j < len; j++) + { + sprintf(sms->sms_text + offset, "%c", utf8_char[j]); + if (utf8_char[j] != '\0') + { + offset++; + } + + } + } + offset++; + sprintf(sms->sms_text + offset, "%c", '\0'); + break; + } + default: + break; + } + return sms_len; +} + +int display_sms_in_json(SMS_T **sms,int num) +{ + + char msg_json[SMS_BUF_SIZE]; + int offset; + offset = sprintf(msg_json, "{\"msg\":["); + for (int i = 0; i < num; i++) + { + char escaped_text[SMS_TEXT_SIZE]; + escape_json(sms[i]->sms_text, escaped_text); + if (sms[i]->ref_number) + offset += sprintf(msg_json + offset, "{\"index\":%d,\"sender\":\"%s\",\"timestamp\":%d,\"content\":\"%s\",\"reference\":%d,\"total\":%d,\"part\":%d},", + sms[i]->sms_index, sms[i]->sender, sms[i]->timestamp, escaped_text, sms[i]->ref_number, sms[i]->total_segments, sms[i]->segment_number); + else + offset += sprintf(msg_json + offset, "{\"index\":%d,\"sender\":\"%s\",\"timestamp\":%d,\"content\":\"%s\"},", + sms[i]->sms_index, sms[i]->sender, sms[i]->timestamp, escaped_text); + } + offset--; + offset += sprintf(msg_json + offset, "]}"); + user_msg("%s\n", msg_json); + return 0; + + +} + +int dump_sms(SMS_T *sms) +{ + dbg_msg("SMS Index: %d", sms->sms_index); + dbg_msg("SMS Text: %s", sms->sms_text); + dbg_msg("SMS Sender: %s", sms->sender); + dbg_msg("SMS Timestamp: %d", sms->timestamp); + dbg_msg("SMS Segment: %d/%d", sms->segment_number, sms->total_segments); + return 0; +} + +int destroy_sms(SMS_T *sms) +{ + if (sms->sms_pdu != NULL) + { + free(sms->sms_pdu); + } + if (sms->sender != NULL) + { + free(sms->sender); + } + if (sms->sms_text != NULL) + { + free(sms->sms_text); + } + free(sms); + return 0; +} diff --git a/application/tom_modem/src/utils.h b/application/tom_modem/src/utils.h new file mode 100644 index 0000000..d93a2c7 --- /dev/null +++ b/application/tom_modem/src/utils.h @@ -0,0 +1,59 @@ +#ifndef _UTILS_H +#define _UTILS_H +#include +#include +#include "modem_types.h" +#include "main.h" + +extern PROFILE_T s_profile; +extern FILE *fdi; // file descriptor for input +extern FILE *fdo; // file descriptor for output +extern int tty_fd; // file descriptor for tty device +struct termios oldtio; // old tty setting + + + +#define dbg_msg(fmt, args...) do { \ + if (s_profile.debug) { \ + fprintf(stderr, "[DBG]" fmt, ##args); \ + fprintf(stderr, "\n"); \ + fflush(stderr); \ + } \ +} while(0) + +#define err_msg(fmt, args...) do { \ + if (1) { \ + fprintf(stderr, "[ERR]" fmt , ##args); \ + fprintf(stderr, "\n"); \ + fflush(stderr); \ + } \ +} while(0) + +#define get_sms_index(line) \ + ({ \ + const char *index_str = (line) + 7; \ + const char *first_comma = strchr(index_str, ','); \ + int sms_index = -1; \ + if (first_comma) { \ + char temp[(size_t)(first_comma - index_str) + 1]; \ + memcpy(temp, index_str, first_comma - index_str); \ + temp[(size_t)(first_comma - index_str)] = '\0'; \ + sms_index = atoi(temp); \ + } \ + sms_index; \ + }) + +#define user_msg(fmt, args...) (fprintf(stdout, fmt , ##args)) + + + +int match_option(char *option_name); + +int match_operation(char *operation_name); + +int open_tty_device(PROFILE_T *profile); + +static int set_tty_device(PROFILE_T *profile); + +static void clean_up(); +#endif