Fix double free issue
[platform/core/appfw/app-core.git] / src / base / appcore_base.c
index 7c1b826..305c065 100644 (file)
@@ -22,6 +22,7 @@
 #include <string.h>
 #include <stdlib.h>
 #include <sys/types.h>
+#include <sys/stat.h>
 #include <unistd.h>
 #include <malloc.h>
 #include <locale.h>
@@ -50,6 +51,7 @@ typedef struct _appcore_base_context {
        unsigned int tid;
        bool suspended_state;
        bool allowed_bg;
+       bool dirty;
 } appcore_base_context;
 
 typedef struct _appcore_base_event_node {
@@ -58,10 +60,16 @@ typedef struct _appcore_base_event_node {
        void *data;
 } appcore_base_event_node;
 
+struct lang_info_s {
+       char *parent;
+       GList *list;
+};
+
 static appcore_base_context __context;
 static GList *__events;
 static GDBusConnection *__bus;
 static guint __suspend_dbus_handler_initialized;
+static char *__locale_dir;
 
 static void __invoke_callback(void *event, int type)
 {
@@ -114,16 +122,293 @@ static void __on_low_battery(keynode_t *key, void *data)
                __invoke_callback(key, APPCORE_BASE_EVENT_LOW_BATTERY);
 }
 
+static void __destroy_lang_info(gpointer data)
+{
+       struct lang_info_s *info = (struct lang_info_s *)data;
+
+       if (info == NULL)
+               return;
+
+       if (info->list)
+               g_list_free_full(info->list, free);
+       if (info->parent)
+               free(info->parent);
+       free(info);
+}
+
+static struct lang_info_s *__create_lang_info(const char *lang)
+{
+       struct lang_info_s *info;
+
+       info = calloc(1, sizeof(struct lang_info_s));
+       if (info == NULL) {
+               _ERR("Out of memory");
+               return NULL;
+       }
+
+       info->parent = strdup(lang);
+       if (info->parent == NULL) {
+               _ERR("Out of memory");
+               free(info);
+               return NULL;
+       }
+
+       return info;
+}
+
+static gint __compare_langs(gconstpointer a, gconstpointer b)
+{
+       if (!a || !b)
+               return -1;
+
+       return strcmp(a, b);
+}
+
+static char *__get_string_before(const char *str, const char *delim)
+{
+       char *new_str;
+       char *dup_str;
+       char *token;
+
+       dup_str = strdup(str);
+       if (dup_str == NULL)
+               return NULL;
+
+       token = strtok(dup_str, delim);
+       if (token == NULL) {
+               free(dup_str);
+               return NULL;
+       }
+
+       new_str = strdup(token);
+       free(dup_str);
+
+       return new_str;
+}
+
+static GHashTable *__get_lang_table(void)
+{
+       GHashTable *table;
+       DIR *dp;
+       struct dirent *dentry;
+       char buf[PATH_MAX];
+       struct stat stat_buf;
+       int ret;
+       char *parent_lang;
+       struct lang_info_s *info;
+
+       if (__locale_dir == NULL || __locale_dir[0] == '\0')
+               return NULL;
+
+       table = g_hash_table_new_full(g_str_hash, g_str_equal,
+                       NULL, __destroy_lang_info);
+       if (table == NULL) {
+               _ERR("Out of memory");
+               return NULL;
+       }
+
+       dp = opendir(__locale_dir);
+       if (dp == NULL) {
+               g_hash_table_destroy(table);
+               return NULL;
+       }
+
+       while ((dentry = readdir(dp)) != NULL) {
+               if (!strcmp(dentry->d_name, ".") ||
+                               !strcmp(dentry->d_name, ".."))
+                       continue;
+
+               snprintf(buf, sizeof(buf), "%s/%s",
+                               __locale_dir, dentry->d_name);
+               ret = stat(buf, &stat_buf);
+               if (ret != 0 || !S_ISDIR(stat_buf.st_mode))
+                       continue;
+
+               parent_lang = __get_string_before(dentry->d_name, "_");
+               if (parent_lang == NULL) {
+                       _ERR("Out of memory");
+                       break;
+               }
+
+               info = g_hash_table_lookup(table, parent_lang);
+               if (info == NULL) {
+                       info = __create_lang_info(parent_lang);
+                       if (info == NULL) {
+                               free(parent_lang);
+                               break;
+                       }
+                       g_hash_table_insert(table, info->parent, info);
+               }
+               info->list = g_list_append(info->list, strdup(dentry->d_name));
+               free(parent_lang);
+       }
+       closedir(dp);
+
+       return table;
+}
+
+static GList *__append_langs(const char *lang, GList *list, GHashTable *table)
+{
+       struct lang_info_s *info;
+       GList *found;
+       char *parent_lang = NULL;
+       char *extract_lang;
+
+       if (lang == NULL)
+               return list;
+
+       extract_lang = __get_string_before(lang, ".");
+       if (extract_lang == NULL)
+               return list;
+
+       found = g_list_find_custom(list, extract_lang, __compare_langs);
+       if (found) {
+               list = g_list_remove_link(list, found);
+               list = g_list_concat(list, found);
+               goto end;
+       }
+
+       parent_lang = __get_string_before(extract_lang, "_");
+       if (parent_lang == NULL)
+               goto end;
+
+       info = g_hash_table_lookup(table, parent_lang);
+       if (info == NULL)
+               goto end;
+
+       found = g_list_find_custom(info->list, extract_lang, __compare_langs);
+       if (found) {
+               info->list = g_list_remove_link(info->list, found);
+               list = g_list_concat(list, found);
+               goto end;
+       }
+
+       found = g_list_find_custom(info->list, parent_lang, __compare_langs);
+       if (found) {
+               info->list = g_list_remove_link(info->list, found);
+               list = g_list_concat(list, found);
+               goto end;
+       }
+
+       found = g_list_first(info->list);
+       if (found) {
+               info->list = g_list_remove_link(info->list, found);
+               list = g_list_concat(list, found);
+       }
+
+end:
+       if (extract_lang)
+               free(extract_lang);
+       if (parent_lang)
+               free(parent_lang);
+
+       return list;
+}
+
+static GList *__split_language(const char *lang)
+{
+       GList *list = NULL;
+       char *dup_lang;
+       char *token;
+
+       dup_lang = strdup(lang);
+       if (dup_lang == NULL) {
+               _ERR("Out of memory");
+               return NULL;
+       }
+
+       token = strtok(dup_lang, ":");
+       while (token != NULL) {
+               list = g_list_append(list, strdup(token));
+               token = strtok(NULL, ":");
+       }
+       free(dup_lang);
+
+       return list;
+}
+
+static GList *__append_default_langs(GList *list)
+{
+       const char *langs[] = {"en_US", "en_GB", "en"};
+       unsigned int i;
+       GList *found;
+
+       for (i = 0; i < (sizeof(langs) / sizeof(langs[0])); i++) {
+               found = g_list_find_custom(list, langs[i], __compare_langs);
+               if (found == NULL)
+                       list = g_list_append(list, strdup(langs[i]));
+       }
+
+       return list;
+}
+
+static char *__get_language(const char *lang)
+{
+       GHashTable *table;
+       GList *list;
+       GList *lang_list = NULL;
+       GList *iter;
+       char *language;
+       char buf[LINE_MAX] = {'\0'};
+       size_t n;
+
+       list = __split_language(lang);
+       if (list == NULL)
+               return NULL;
+
+       table = __get_lang_table();
+       if (table == NULL) {
+               g_list_free_full(list, free);
+               return NULL;
+       }
+
+       iter = g_list_first(list);
+       while (iter) {
+               language = (char *)iter->data;
+               lang_list = __append_langs(language, lang_list, table);
+               iter = g_list_next(iter);
+       }
+       g_list_free_full(list, free);
+       g_hash_table_destroy(table);
+
+       lang_list = __append_default_langs(lang_list);
+       iter = g_list_first(lang_list);
+       while (iter) {
+               language = (char *)iter->data;
+               if (language) {
+                       if (buf[0] == '\0') {
+                               snprintf(buf, sizeof(buf), "%s", language);
+                       } else {
+                               n = sizeof(buf) - strlen(buf) - 1;
+                               strncat(buf, ":", n);
+                               n = sizeof(buf) - strlen(buf) - 1;
+                               strncat(buf, language, n);
+                       }
+               }
+               iter = g_list_next(iter);
+       }
+       g_list_free_full(lang_list, free);
+
+       return strdup(buf);
+}
+
 static void __update_lang(void)
 {
-       char language[32];
+       char *language;
        char *lang;
        char *r;
 
        lang = vconf_get_str(VCONFKEY_LANGSET);
        if (lang) {
-               snprintf(language, sizeof(language), "%s:en_US:en_GB:en", lang);
-               setenv("LANGUAGE", language, 1);
+               /* TODO: Use VCONFKEY_SETAPPL_LANGUAGES key */
+               language = __get_language(lang);
+               if (language) {
+                       _DBG("*****language(%s)", language);
+                       setenv("LANGUAGE", language, 1);
+                       free(language);
+               } else {
+                       setenv("LANGUAGE", lang, 1);
+               }
                setenv("LANG", lang, 1);
                setenv("LC_MESSAGES", lang, 1);
                r = setlocale(LC_ALL, "");
@@ -340,6 +625,12 @@ static int __set_i18n(const char *domain, const char *dir)
                return -1;
        }
 
+       if (dir) {
+               if (__locale_dir)
+                       free(__locale_dir);
+               __locale_dir = strdup(dir);
+       }
+
        __update_lang();
        __update_region();
 
@@ -401,6 +692,8 @@ EXPORT_API int appcore_base_on_set_i18n(void)
 
 EXPORT_API int appcore_base_init(appcore_base_ops ops, int argc, char **argv, void *data)
 {
+       int i;
+
        __context.ops = ops;
        __context.argc = argc;
        __context.argv = argv;
@@ -414,6 +707,17 @@ EXPORT_API int appcore_base_init(appcore_base_ops ops, int argc, char **argv, vo
 
        __init_suspend_dbus_handler();
 
+       if (!__context.dirty) {
+               __context.dirty = true;
+
+               for (i = APPCORE_BASE_EVENT_START + 1; i < APPCORE_BASE_EVENT_MAX; i++) {
+                       if (__exist_callback(i)) {
+                               if (__context.ops.set_event)
+                                       __context.ops.set_event(i, __context.data);
+                       }
+               }
+       }
+
        if (__context.ops.create && __context.ops.create(__context.data) < 0) {
                aul_status_update(STATUS_DYING);
                return 0;
@@ -429,6 +733,10 @@ EXPORT_API void appcore_base_fini(void)
 {
        int i;
 
+       aul_status_update(STATUS_DYING);
+       if (__context.ops.terminate)
+               __context.ops.terminate(__context.data);
+
        for (i = APPCORE_BASE_EVENT_START + 1; i < APPCORE_BASE_EVENT_MAX; i++) {
                if (__exist_callback(i)) {
                        if (__context.ops.unset_event)
@@ -440,10 +748,12 @@ EXPORT_API void appcore_base_fini(void)
        __events = NULL;
        __fini_suspend_dbus_handler();
 
-       aul_status_update(STATUS_DYING);
-       if (__context.ops.terminate)
-               __context.ops.terminate(__context.data);
+       if (__locale_dir) {
+               free(__locale_dir);
+               __locale_dir = NULL;
+       }
 
+       __context.dirty = false;
 }
 
 EXPORT_API int appcore_base_flush_memory(void)
@@ -631,7 +941,7 @@ EXPORT_API appcore_base_event_h appcore_base_add_event(enum appcore_base_event e
 {
        appcore_base_event_node *node;
 
-       if (!__exist_callback(event)) {
+       if (__context.dirty && !__exist_callback(event)) {
                if (__context.ops.set_event)
                        __context.ops.set_event(event, __context.data);
        }
@@ -657,7 +967,7 @@ EXPORT_API int appcore_base_remove_event(appcore_base_event_h handle)
        event = node->type;
        __events = g_list_remove(__events, node);
        free(node);
-       if (!__exist_callback(event)) {
+       if (__context.dirty && !__exist_callback(event)) {
                if (__context.ops.unset_event)
                        __context.ops.unset_event(event, __context.data);
        }