Write state of SMS assembly to disk and restore on startup.
authorAndrzej Zaborowski <andrew.zaborowski@intel.com>
Sat, 5 Sep 2009 01:16:02 +0000 (03:16 +0200)
committerDenis Kenzior <denkenz@gmail.com>
Tue, 8 Sep 2009 19:44:39 +0000 (14:44 -0500)
This way we can continue receiving segmented messages over a reset or
crash.

Makefile.am
src/common.c
src/sms.c
src/smsutil.c
src/smsutil.h
unit/test-sms.c

index c15f9c5..f259015 100644 (file)
@@ -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)
 
index ff94fc0..63884a3 100644 (file)
@@ -26,6 +26,7 @@
 #define _GNU_SOURCE
 #include <string.h>
 #include <ctype.h>
+#include <errno.h>
 
 #include <glib.h>
 
index fe76580..149ecd0 100644 (file)
--- 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);
 }
 
index 79d9966..966a016 100644 (file)
 #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)
+{
+       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)
 {
-       return g_new0(struct sms_assembly, 1);
+       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)
@@ -2199,6 +2389,16 @@ GSList *sms_assembly_add_fragment(struct sms_assembly *assembly,
                                        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);
        GSList *l;
@@ -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);
index 93cdf97..daccaf7 100644 (file)
@@ -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,
index b5617b5..85af3b8 100644 (file)
@@ -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;