diff --git a/Makefile.am b/Makefile.am index c15f9c55..f259015d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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) diff --git a/src/common.c b/src/common.c index ff94fc0e..63884a3a 100644 --- a/src/common.c +++ b/src/common.c @@ -26,6 +26,7 @@ #define _GNU_SOURCE #include #include +#include #include diff --git a/src/sms.c b/src/sms.c index fe765808..149ecd0a 100644 --- a/src/sms.c +++ b/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); } diff --git a/src/smsutil.c b/src/smsutil.c index 79d99669..966a016c 100644 --- a/src/smsutil.c +++ b/src/smsutil.c @@ -23,16 +23,38 @@ #include #endif +#define _GNU_SOURCE #include #include #include +#include +#include +#include +#include + #include +#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); diff --git a/src/smsutil.h b/src/smsutil.h index 93cdf973..daccaf71 100644 --- a/src/smsutil.h +++ b/src/smsutil.h @@ -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, diff --git a/unit/test-sms.c b/unit/test-sms.c index b5617b59..85af3b89 100644 --- a/unit/test-sms.c +++ b/unit/test-sms.c @@ -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;