mirror of git://git.sysmocom.de/ofono
Write state of SMS assembly to disk and restore on startup.
This way we can continue receiving segmented messages over a reset or crash.
This commit is contained in:
parent
110dfb9e2e
commit
06ea6137a3
|
@ -222,12 +222,13 @@ unit_test_util_SOURCES = unit/test-util.c src/util.c
|
|||
unit_test_util_LDADD = @GLIB_LIBS@
|
||||
unit_objects += $(unit_test_utils_OBJECTS)
|
||||
|
||||
unit_test_sms_SOURCES = unit/test-sms.c src/util.c src/smsutil.c
|
||||
unit_test_sms_SOURCES = unit/test-sms.c src/util.c src/smsutil.c src/storage.c
|
||||
unit_test_sms_LDADD = @GLIB_LIBS@
|
||||
unit_objects += $(unit_test_sms_OBJECTS)
|
||||
|
||||
unit_test_simutil_SOURCES = unit/test-simutil.c src/util.c \
|
||||
src/simutil.c src/smsutil.c
|
||||
src/simutil.c src/smsutil.c \
|
||||
src/storage.c
|
||||
unit_test_simutil_LDADD = @GLIB_LIBS@
|
||||
unit_objects += $(unit_test_simutil_OBJECTS)
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#define _GNU_SOURCE
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
|
|
58
src/sms.c
58
src/sms.c
|
@ -57,6 +57,9 @@ struct ofono_sms {
|
|||
gint tx_source;
|
||||
struct ofono_message_waiting *mw;
|
||||
unsigned int mw_watch;
|
||||
struct ofono_sim *sim;
|
||||
unsigned int sim_watch;
|
||||
unsigned int imsi_watch;
|
||||
const struct ofono_sms_driver *driver;
|
||||
void *driver_data;
|
||||
struct ofono_atom *atom;
|
||||
|
@ -785,6 +788,17 @@ static void sms_unregister(struct ofono_atom *atom)
|
|||
__ofono_modem_remove_atom_watch(modem, sms->mw_watch);
|
||||
sms->mw_watch = 0;
|
||||
}
|
||||
|
||||
if (sms->sim_watch) {
|
||||
if (sms->imsi_watch) {
|
||||
ofono_sim_remove_ready_watch(sms->sim,
|
||||
sms->imsi_watch);
|
||||
sms->imsi_watch = 0;
|
||||
}
|
||||
|
||||
__ofono_modem_remove_atom_watch(modem, sms->sim_watch);
|
||||
sms->sim_watch = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void sms_remove(struct ofono_atom *atom)
|
||||
|
@ -836,7 +850,6 @@ struct ofono_sms *ofono_sms_create(struct ofono_modem *modem,
|
|||
|
||||
sms->sca.type = 129;
|
||||
sms->ref = 1;
|
||||
sms->assembly = sms_assembly_new();
|
||||
sms->txq = g_queue_new();
|
||||
sms->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_SMS,
|
||||
sms_remove, sms);
|
||||
|
@ -870,12 +883,45 @@ static void mw_watch(struct ofono_atom *atom,
|
|||
sms->mw = __ofono_atom_get_data(atom);
|
||||
}
|
||||
|
||||
static void sms_got_imsi(void *data)
|
||||
{
|
||||
struct ofono_sms *sms = data;
|
||||
const char *imsi = ofono_sim_get_imsi(sms->sim);
|
||||
|
||||
sms->assembly = sms_assembly_new(imsi);
|
||||
}
|
||||
|
||||
static void sim_watch(struct ofono_atom *atom,
|
||||
enum ofono_atom_watch_condition cond, void *data)
|
||||
{
|
||||
struct ofono_sms *sms = data;
|
||||
|
||||
if (cond == OFONO_ATOM_WATCH_CONDITION_UNREGISTERED) {
|
||||
sms->imsi_watch = 0;
|
||||
|
||||
if (sms->assembly) {
|
||||
sms_assembly_free(sms->assembly);
|
||||
sms->assembly = NULL;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
sms->sim = __ofono_atom_get_data(atom);
|
||||
sms->imsi_watch = ofono_sim_add_ready_watch(sms->sim, sms_got_imsi,
|
||||
sms, NULL);
|
||||
|
||||
if (ofono_sim_get_ready(sms->sim))
|
||||
sms_got_imsi(sms);
|
||||
}
|
||||
|
||||
void ofono_sms_register(struct ofono_sms *sms)
|
||||
{
|
||||
DBusConnection *conn = ofono_dbus_get_connection();
|
||||
struct ofono_modem *modem = __ofono_atom_get_modem(sms->atom);
|
||||
const char *path = __ofono_atom_get_path(sms->atom);
|
||||
struct ofono_atom *mw_atom;
|
||||
struct ofono_atom *sim_atom;
|
||||
|
||||
if (!g_dbus_register_interface(conn, path,
|
||||
SMS_MANAGER_INTERFACE,
|
||||
|
@ -899,6 +945,16 @@ void ofono_sms_register(struct ofono_sms *sms)
|
|||
if (mw_atom && __ofono_atom_get_registered(mw_atom))
|
||||
mw_watch(mw_atom, OFONO_ATOM_WATCH_CONDITION_REGISTERED, sms);
|
||||
|
||||
sms->sim_watch = __ofono_modem_add_atom_watch(modem,
|
||||
OFONO_ATOM_TYPE_SIM,
|
||||
sim_watch, sms, NULL);
|
||||
|
||||
sim_atom = __ofono_modem_find_atom(modem, OFONO_ATOM_TYPE_SIM);
|
||||
|
||||
if (sim_atom && __ofono_atom_get_registered(sim_atom))
|
||||
sim_watch(sim_atom,
|
||||
OFONO_ATOM_WATCH_CONDITION_REGISTERED, sms);
|
||||
|
||||
__ofono_atom_register(sms->atom, sms_unregister);
|
||||
}
|
||||
|
||||
|
|
214
src/smsutil.c
214
src/smsutil.c
|
@ -23,16 +23,38 @@
|
|||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include "types.h"
|
||||
#include "common.h"
|
||||
#include "util.h"
|
||||
#include "storage.h"
|
||||
#include "smsutil.h"
|
||||
|
||||
#define uninitialized_var(x) x = x
|
||||
|
||||
#define SMS_BACKUP_MODE 0600
|
||||
#define SMS_BACKUP_PATH STORAGEDIR "/%s/sms"
|
||||
#define SMS_BACKUP_PATH_DIR SMS_BACKUP_PATH "/%s-%i-%i"
|
||||
#define SMS_BACKUP_PATH_FILE SMS_BACKUP_PATH_DIR "/%03i"
|
||||
|
||||
#define SMS_ADDR_FMT "%21[0-9+*#]"
|
||||
|
||||
static GSList *sms_assembly_add_fragment_backup(struct sms_assembly *assembly,
|
||||
const struct sms *sms, time_t ts,
|
||||
const struct sms_address *addr,
|
||||
guint16 ref, guint8 max, guint8 seq,
|
||||
gboolean backup);
|
||||
|
||||
void extract_bcd_number(const unsigned char *buf, int len, char *out)
|
||||
{
|
||||
static const char digit_lut[] = "0123456789*#abc\0";
|
||||
|
@ -2173,9 +2195,177 @@ char *sms_decode_text(GSList *sms_list)
|
|||
return utf8;
|
||||
}
|
||||
|
||||
struct sms_assembly *sms_assembly_new()
|
||||
static int sms_serialize(unsigned char *buf, const struct sms *sms)
|
||||
{
|
||||
return g_new0(struct sms_assembly, 1);
|
||||
int len, tpdu_len;
|
||||
|
||||
sms_encode(sms, &len, &tpdu_len, buf + 1);
|
||||
buf[0] = tpdu_len;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static gboolean sms_deserialize(const unsigned char *buf,
|
||||
struct sms *sms, int len)
|
||||
{
|
||||
if (len < 1)
|
||||
return FALSE;
|
||||
|
||||
return sms_decode(buf + 1, len - 1, FALSE, buf[0], sms);
|
||||
}
|
||||
|
||||
static void sms_assembly_load(struct sms_assembly *assembly,
|
||||
const struct dirent *dir)
|
||||
{
|
||||
struct sms_address addr;
|
||||
char straddr[sizeof(addr.address) + 1];
|
||||
guint16 ref;
|
||||
guint8 max;
|
||||
guint8 seq;
|
||||
char *path;
|
||||
int len;
|
||||
struct stat segment_stat;
|
||||
struct dirent **segments;
|
||||
char *endp;
|
||||
int r;
|
||||
int i;
|
||||
unsigned char buf[177];
|
||||
struct sms segment;
|
||||
|
||||
if (dir->d_type != DT_DIR)
|
||||
return;
|
||||
|
||||
if (sscanf(dir->d_name, SMS_ADDR_FMT "-%hi-%hhi",
|
||||
straddr, &ref, &max) < 3)
|
||||
return;
|
||||
sms_address_from_string(&addr, straddr);
|
||||
|
||||
path = g_strdup_printf(SMS_BACKUP_PATH "/%s",
|
||||
assembly->imsi, dir->d_name);
|
||||
len = scandir(path, &segments, NULL, versionsort);
|
||||
g_free(path);
|
||||
|
||||
if (len < 0)
|
||||
return;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (segments[i]->d_type != DT_REG)
|
||||
continue;
|
||||
|
||||
seq = strtol(segments[i]->d_name, &endp, 10);
|
||||
if (*endp != '\0')
|
||||
continue;
|
||||
|
||||
r = read_file(buf, sizeof(buf), SMS_BACKUP_PATH "/%s/%s",
|
||||
assembly->imsi,
|
||||
dir->d_name, segments[i]->d_name);
|
||||
if (r < 0)
|
||||
continue;
|
||||
|
||||
if (!sms_deserialize(buf, &segment, r))
|
||||
continue;
|
||||
|
||||
path = g_strdup_printf(SMS_BACKUP_PATH "/%s/%s",
|
||||
assembly->imsi,
|
||||
dir->d_name, segments[i]->d_name);
|
||||
r = stat(path, &segment_stat);
|
||||
g_free(path);
|
||||
|
||||
if (r != 0)
|
||||
continue;
|
||||
|
||||
if (sms_assembly_add_fragment_backup(assembly, &segment,
|
||||
segment_stat.st_mtime,
|
||||
&addr, ref, max, seq, FALSE)) {
|
||||
/* This should not happen */
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
free(segments[i]);
|
||||
|
||||
free(segments);
|
||||
}
|
||||
|
||||
static gboolean sms_assembly_store(struct sms_assembly *assembly,
|
||||
struct sms_assembly_node *node,
|
||||
const struct sms *sms, guint8 seq)
|
||||
{
|
||||
unsigned char buf[177];
|
||||
int len;
|
||||
|
||||
if (!assembly->imsi)
|
||||
return FALSE;
|
||||
|
||||
len = sms_serialize(buf, sms);
|
||||
|
||||
if (write_file(buf, len, SMS_BACKUP_MODE,
|
||||
SMS_BACKUP_PATH_FILE, assembly->imsi,
|
||||
sms_address_to_string(&node->addr),
|
||||
node->ref, node->max_fragments, seq) != len)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void sms_assembly_backup_free(struct sms_assembly *assembly,
|
||||
struct sms_assembly_node *node)
|
||||
{
|
||||
char *path;
|
||||
int seq;
|
||||
|
||||
if (!assembly->imsi)
|
||||
return;
|
||||
|
||||
for (seq = 0; seq < node->max_fragments; seq++) {
|
||||
int offset = seq / 32;
|
||||
int bit = 1 << (seq % 32);
|
||||
|
||||
if (node->bitmap[offset] & bit) {
|
||||
path = g_strdup_printf(SMS_BACKUP_PATH_FILE,
|
||||
assembly->imsi,
|
||||
sms_address_to_string(&node->addr),
|
||||
node->ref, node->max_fragments, seq);
|
||||
unlink(path);
|
||||
g_free(path);
|
||||
}
|
||||
}
|
||||
|
||||
path = g_strdup_printf(SMS_BACKUP_PATH_DIR, assembly->imsi,
|
||||
sms_address_to_string(&node->addr),
|
||||
node->ref, node->max_fragments);
|
||||
rmdir(path);
|
||||
g_free(path);
|
||||
}
|
||||
|
||||
struct sms_assembly *sms_assembly_new(const char *imsi)
|
||||
{
|
||||
struct sms_assembly *ret = g_new0(struct sms_assembly, 1);
|
||||
char *path;
|
||||
struct dirent **entries;
|
||||
int len;
|
||||
|
||||
if (imsi) {
|
||||
ret->imsi = imsi;
|
||||
|
||||
/* Restore state from backup */
|
||||
|
||||
path = g_strdup_printf(SMS_BACKUP_PATH, imsi);
|
||||
len = scandir(path, &entries, NULL, alphasort);
|
||||
g_free(path);
|
||||
|
||||
if (len < 0)
|
||||
return ret;
|
||||
|
||||
while (len--) {
|
||||
sms_assembly_load(ret, entries[len]);
|
||||
free(entries[len]);
|
||||
}
|
||||
|
||||
free(entries);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void sms_assembly_free(struct sms_assembly *assembly)
|
||||
|
@ -2198,6 +2388,16 @@ GSList *sms_assembly_add_fragment(struct sms_assembly *assembly,
|
|||
const struct sms *sms, time_t ts,
|
||||
const struct sms_address *addr,
|
||||
guint16 ref, guint8 max, guint8 seq)
|
||||
{
|
||||
return sms_assembly_add_fragment_backup(assembly, sms,
|
||||
ts, addr, ref, max, seq, TRUE);
|
||||
}
|
||||
|
||||
static GSList *sms_assembly_add_fragment_backup(struct sms_assembly *assembly,
|
||||
const struct sms *sms, time_t ts,
|
||||
const struct sms_address *addr,
|
||||
guint16 ref, guint8 max, guint8 seq,
|
||||
gboolean backup)
|
||||
{
|
||||
int offset = seq / 32;
|
||||
int bit = 1 << (seq % 32);
|
||||
|
@ -2272,11 +2472,17 @@ out:
|
|||
node->bitmap[offset] |= bit;
|
||||
node->num_fragments += 1;
|
||||
|
||||
if (node->num_fragments < node->max_fragments)
|
||||
if (node->num_fragments < node->max_fragments) {
|
||||
if (backup)
|
||||
sms_assembly_store(assembly, node, sms, seq);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
completed = node->fragment_list;
|
||||
|
||||
sms_assembly_backup_free(assembly, node);
|
||||
|
||||
if (prev)
|
||||
prev->next = l->next;
|
||||
else
|
||||
|
@ -2310,6 +2516,8 @@ void sms_assembly_expire(struct sms_assembly *assembly, time_t before)
|
|||
continue;
|
||||
}
|
||||
|
||||
sms_assembly_backup_free(assembly, node);
|
||||
|
||||
g_slist_foreach(node->fragment_list, (GFunc)g_free, 0);
|
||||
g_slist_free(node->fragment_list);
|
||||
g_free(node);
|
||||
|
|
|
@ -360,6 +360,7 @@ struct sms_assembly_node {
|
|||
};
|
||||
|
||||
struct sms_assembly {
|
||||
const char *imsi;
|
||||
GSList *assembly_list;
|
||||
};
|
||||
|
||||
|
@ -456,7 +457,7 @@ gboolean sms_extract_language_variant(const struct sms *sms, guint8 *locking,
|
|||
unsigned char *sms_decode_datagram(GSList *sms_list, long *out_len);
|
||||
char *sms_decode_text(GSList *sms_list);
|
||||
|
||||
struct sms_assembly *sms_assembly_new();
|
||||
struct sms_assembly *sms_assembly_new(const char *imsi);
|
||||
void sms_assembly_free(struct sms_assembly *assembly);
|
||||
GSList *sms_assembly_add_fragment(struct sms_assembly *assembly,
|
||||
const struct sms *sms, time_t ts,
|
||||
|
|
|
@ -623,7 +623,7 @@ static void test_assembly()
|
|||
unsigned char pdu[164];
|
||||
long pdu_len;
|
||||
struct sms sms;
|
||||
struct sms_assembly *assembly = sms_assembly_new();
|
||||
struct sms_assembly *assembly = sms_assembly_new(NULL);
|
||||
guint16 ref;
|
||||
guint8 max;
|
||||
guint8 seq;
|
||||
|
@ -780,7 +780,7 @@ static void test_prepare_concat()
|
|||
struct sms *sms;
|
||||
struct sms decoded;
|
||||
int pdu_len, tpdu_len;
|
||||
struct sms_assembly *assembly = sms_assembly_new();
|
||||
struct sms_assembly *assembly = sms_assembly_new(NULL);
|
||||
guint16 ref;
|
||||
guint8 max;
|
||||
guint8 seq;
|
||||
|
|
Loading…
Reference in New Issue