This way we can continue receiving segmented messages over a reset or crash. --- src/common.c | 29 +++++++ src/common.h | 10 +++ src/sim.c | 34 -------- src/sms.c | 14 +++- src/smsutil.c | 237 +++++++++++++++++++++++++++++++++++++++++++++++++++++- src/smsutil.h | 3 +- unit/Makefile.am | 6 +- unit/test-sms.c | 4 +- 8 files changed, 293 insertions(+), 44 deletions(-)
diff --git a/src/common.c b/src/common.c index 14bf7f9..e1d560e 100644 --- a/src/common.c +++ b/src/common.c @@ -26,6 +26,7 @@ #define _GNU_SOURCE #include <string.h> #include <ctype.h> +#include <errno.h> #include <glib.h> @@ -593,3 +594,31 @@ gboolean is_valid_pin(const char *pin) return TRUE; } + +int create_dirs(const char *filename, const mode_t mode) +{ + struct stat st; + char *dir; + const char *prev, *next; + int err; + + err = stat(filename, &st); + if (!err && S_ISREG(st.st_mode)) + return 0; + + dir = g_malloc(strlen(filename) + 1); + strcpy(dir, "/"); + + for (prev = filename; (next = strchr(prev + 1, '/')); prev = next) + if (next > prev + 1) { + strncat(dir, prev + 1, next - prev); + + if (mkdir(dir, mode) && errno != EEXIST) { + g_free(dir); + return -1; + } + } + + g_free(dir); + return 0; +} diff --git a/src/common.h b/src/common.h index 3805e21..18fb0c5 100644 --- a/src/common.h +++ b/src/common.h @@ -135,3 +135,13 @@ const char *ss_control_type_to_string(enum ss_control_type type); const char *bearer_class_to_string(enum bearer_class cls); gboolean is_valid_pin(const char *pin); + +#ifdef TEMP_FAILURE_RETRY +#define TFR TEMP_FAILURE_RETRY +#else +#define TFR +#endif + +#include <fcntl.h> + +int create_dirs(const char *filename, const mode_t mode); diff --git a/src/sim.c b/src/sim.c index d4387c9..cd608f7 100644 --- a/src/sim.c +++ b/src/sim.c @@ -44,12 +44,6 @@ #include "sim.h" #include "simutil.h" -#ifdef TEMP_FAILURE_RETRY -#define TFR TEMP_FAILURE_RETRY -#else -#define TFR -#endif - #define SIM_MANAGER_INTERFACE "org.ofono.SimManager" #define SIM_CACHE_MODE 0600 @@ -515,34 +509,6 @@ static void sim_retrieve_imsi(struct ofono_modem *modem) sim->ops->read_imsi(modem, sim_imsi_cb, modem); } -static int create_dirs(const char *filename, const mode_t mode) -{ - struct stat st; - char *dir; - const char *prev, *next; - int err; - - err = stat(filename, &st); - if (!err && S_ISREG(st.st_mode)) - return 0; - - dir = g_malloc(strlen(filename) + 1); - strcpy(dir, "/"); - - for (prev = filename; (next = strchr(prev + 1, '/')); prev = next) - if (next > prev + 1) { - strncat(dir, prev + 1, next - prev); - - if (mkdir(dir, mode) && errno != EEXIST) { - g_free(dir); - return -1; - } - } - - g_free(dir); - return 0; -} - static void sim_op_error(struct ofono_modem *modem) { struct sim_manager_data *sim = modem->sim_manager; diff --git a/src/sms.c b/src/sms.c index c7d83fa..0781f86 100644 --- a/src/sms.c +++ b/src/sms.c @@ -72,7 +72,6 @@ static struct sms_manager_data *sms_manager_create() sms->sca.type = 129; sms->ref = 1; - sms->assembly = sms_assembly_new(); sms->txq = g_queue_new(); return sms; @@ -792,6 +791,14 @@ void ofono_sms_status_notify(struct ofono_modem *modem, unsigned char *pdu, ofono_error("SMS Status-Report not yet handled"); } +static void sms_got_imsi(struct ofono_modem *modem) +{ + const char *imsi = ofono_sim_get_imsi(modem); + struct sms_manager_data *sms = modem->sms_manager; + + sms->assembly = sms_assembly_new(imsi); +} + int ofono_sms_manager_register(struct ofono_modem *modem, struct ofono_sms_ops *ops) { @@ -830,6 +837,11 @@ int ofono_sms_manager_register(struct ofono_modem *modem, ofono_modem_add_interface(modem, SMS_MANAGER_INTERFACE); + if (ofono_sim_get_ready(modem)) + sms_got_imsi(modem); + else + ofono_sim_ready_notify_register(modem, sms_got_imsi); + return 0; } diff --git a/src/smsutil.c b/src/smsutil.c index fcff9aa..2480d71 100644 --- a/src/smsutil.c +++ b/src/smsutil.c @@ -23,11 +23,20 @@ #include <config.h> #endif +#define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <string.h> #include <glib.h> - +#include <dirent.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> + +#include "driver.h" +#include "common.h" #include "util.h" #include "smsutil.h" @@ -2106,9 +2115,211 @@ 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) +{ + 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 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); + +#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 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 fd; + int r; + 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 (; len--; free(segments[len])) { + if (segments[len]->d_type != DT_REG) + continue; + + seq = strtol(segments[len]->d_name, &endp, 10); + if (*endp != '\0') + continue; + + path = g_strdup_printf(SMS_BACKUP_PATH "/%s/%s", + assembly->imsi, + dir->d_name, segments[len]->d_name); + fd = TFR(open(path, O_RDONLY)); + g_free(path); + + if (fd == -1) + continue; + + if (fstat(fd, &segment_stat) != 0) { + TFR(close(fd)); + continue; + } + + r = TFR(read(fd, buf, sizeof(buf))); + + if (r > 0 && sms_deserialize(buf, &segment, r)) { + if (sms_assembly_add_fragment_backup(assembly, + &segment, + segment_stat.st_mtime, + &addr, ref, max, seq, FALSE)) { + /* This can't happen */ + } + } + + TFR(close(fd)); + } + + 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]; + char *path; + int fd; + int len; + + if (!assembly->imsi) + return; + + len = sms_serialize(buf, sms); + + path = g_strdup_printf(SMS_BACKUP_PATH_FILE, assembly->imsi, + sms_address_to_string(&node->addr), + node->ref, node->max_fragments, seq); + + if (create_dirs(path, SMS_BACKUP_MODE | S_IXUSR)) { + g_free(path); + return FALSE; + } + + fd = TFR(open(path, O_WRONLY | O_CREAT, SMS_BACKUP_MODE)); + if (fd == -1) { + g_free(path); + return FALSE; + } + + if (TFR(write(fd, buf, len)) < len) { + TFR(close(fd)); + unlink(path); + g_free(path); + return FALSE; + } + + g_free(path); + TFR(close(fd)); + + return TRUE; +} + +static void sms_assembly_backup_free(struct sms_assembly *assembly, + struct sms_assembly_node *node) { - return g_new0(struct sms_assembly, 1); + 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); + unlink(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) @@ -2132,6 +2343,16 @@ GSList *sms_assembly_add_fragment(struct sms_assembly *assembly, const struct sms_address *addr, guint16 ref, guint8 max, guint8 seq) { + 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); GSList *l; @@ -2205,11 +2426,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 @@ -2243,6 +2470,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 95d0c78..f6541b6 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; }; @@ -454,7 +455,7 @@ gboolean sms_extract_concatenation(const struct sms *sms, guint16 *ref_num, 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/Makefile.am b/unit/Makefile.am index 8a27267..74d0f40 100644 --- a/unit/Makefile.am +++ b/unit/Makefile.am @@ -6,11 +6,13 @@ test_common_SOURCES = test-common.c $(top_srcdir)/src/common.c test_util_SOURCES = test-util.c $(top_srcdir)/src/util.c test_sms_SOURCES = test-sms.c $(top_srcdir)/src/util.c \ - $(top_srcdir)/src/smsutil.c + $(top_srcdir)/src/smsutil.c \ + $(top_srcdir)/src/common.c test_simutil_SOURCES = test-simutil.c $(top_srcdir)/src/util.c \ $(top_srcdir)/src/simutil.c \ - $(top_srcdir)/src/smsutil.c + $(top_srcdir)/src/smsutil.c \ + $(top_srcdir)/src/common.c LDADD = @GLIB_LIBS@ @GTHREAD_LIBS@ diff --git a/unit/test-sms.c b/unit/test-sms.c index 52d6859..a94cd4b 100644 --- a/unit/test-sms.c +++ b/unit/test-sms.c @@ -619,7 +619,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; @@ -776,7 +776,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; -- 1.6.1 _______________________________________________ ofono mailing list ofono@ofono.org http://lists.ofono.org/listinfo/ofono