tom_modem: release a new at tool

This commit is contained in:
fujr 2024-10-21 22:22:08 +08:00
parent 656c32260d
commit fb355ce04e
12 changed files with 1926 additions and 0 deletions

View File

@ -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)))

View File

@ -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

View File

@ -0,0 +1,201 @@
#include "modem_types.h"
#include "main.h"
#include "utils.h"
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <termios.h>
#include <signal.h>
#include <sys/select.h>
#include <errno.h>
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;
}

View File

@ -0,0 +1,43 @@
#ifndef _MAIN_H_
#define _MAIN_H_
#include "modem_types.h"
#include "main.h"
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <termios.h>
#include <signal.h>
#include <sys/select.h>
#include <errno.h>
#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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,421 @@
/*
* 2017 - 2021 Cezary Jackiewicz <cezary@eko.one.pl>
* 2014 lovewilliam <ztong@vt.edu>
*/
// 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 <string.h>
#include <time.h>
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_length; i++) {
if (buffer[i] < 128) {
if (buffer[i] == GSM_7BITS_ESCAPE) {
buffer[i] = gsm7bits_extend_to_latin1[buffer[i + 1]];
memmove(&buffer[i + 1], &buffer[i + 2], buffer_length - i - 1);
buffer_length--;
} else {
buffer[i] = gsm7bits_to_latin1[buffer[i]];
}
}
}
return buffer_length;
}
#define NPC '?'
static const int latin1_to_gsm7bits[256] = {
NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, 0x0a, NPC,-0x0a, 0x0d, NPC, NPC,
NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC,
0x20, 0x21, 0x22, 0x23, 0x02, 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,
0x00, 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,-0x3c,-0x2f,-0x3e,-0x14, 0x11,
NPC, 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,-0x28,-0x40,-0x29,-0x3d, NPC,
NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC,
NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC,
NPC, 0x40, NPC, 0x01, 0x24, 0x03, NPC, 0x5f, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC,
NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, NPC, 0x60,
NPC, NPC, NPC, NPC, 0x5b, 0x0e, 0x1c, 0x09, NPC, 0x1f, NPC, NPC, NPC, NPC, NPC, NPC,
NPC, 0x5d, NPC, NPC, NPC, NPC, 0x5c, NPC, 0x0b, NPC, NPC, NPC, 0x5e, NPC, NPC, 0x1e,
0x7f, NPC, NPC, NPC, 0x7b, 0x0f, 0x1d, NPC, 0x04, 0x05, NPC, NPC, 0x07, NPC, NPC, NPC,
NPC, 0x7d, 0x08, NPC, NPC, NPC, 0x7c, NPC, 0x0c, 0x06, NPC, NPC, 0x7e, NPC, NPC, NPC,
};
static int
AsciiToG7bit(const char* buffer, int buffer_length, unsigned char* output_buffer)
{
int i, j, val;
j=0;
for (i = 0; i < buffer_length; i++) {
val = latin1_to_gsm7bits[buffer[i] & 0xFF];
if (val < 0) {
output_buffer[j++] = GSM_7BITS_ESCAPE;
output_buffer[j++] = -1*val;
} else {
if (((buffer[i] & 0xFF) & 0xE0) == 0xC0) { /* test for two byte utf8 char */
val = NPC;
i++;
} else if (((buffer[i] & 0xFF) & 0xF0) == 0xE0) { /* test for three byte utf8 char */
val = NPC;
i++;
i++;
}
output_buffer[j++] = val;
}
}
return j;
}
// Encode a digit based phone number for SMS based format.
static int
EncodePhoneNumber(const char* phone_number, unsigned char* output_buffer, int buffer_size)
{
int output_buffer_length = 0;
const int phone_number_length = strlen(phone_number);
// Check if the output buffer is big enough.
if ((phone_number_length + 1) / 2 > 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;
}

View File

@ -0,0 +1,52 @@
/*
* 2017 - 2021 Cezary Jackiewicz <cezary@eko.one.pl>
* 2014 lovewilliam <ztong@vt.edu>
*/
// 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 <time.h>
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_

View File

@ -0,0 +1,111 @@
/*
* 2014 lovewilliam <ztong@vt.edu>
* SMS PDU Decoder
*/
#include "pdu.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <wchar.h>
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<sms_text_length;i++)
{
printf("%c", sms_text[i]);
}
break;
}
case 2:
{
// UCS2
for(int i = skip_bytes;i<sms_text_length;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++)
{
printf("%c",utf8_char[j]);
}
}
break;
}
default:
break;
}
printf("\n");
return 0;
}
int main()
{
return sms_decode();
}

View File

@ -0,0 +1,53 @@
/*
* 2014 lovewilliam <ztong@vt.edu>
* 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;
}

View File

@ -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> AT command");
err_msg(" -d, --tty_dev <TTY device> TTY device **REQUIRED**");
err_msg(" -b, --baud_rate <baud rate> Baud rate Default: 115200 Supported: 4800,9600,19200,38400,57600,115200");
err_msg(" -B, --data_bits <data bits> Data bits Default: 8 Supported: 5,6,7,8");
err_msg(" -t, --timeout <timeout> Timeout Default: 3");
err_msg(" -o, --operation <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> SMS PDU");
err_msg(" -i, --sms_index <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;
}

View File

@ -0,0 +1,59 @@
#ifndef _UTILS_H
#define _UTILS_H
#include <stdio.h>
#include <stdlib.h>
#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