Fix TC-2372 Dialer crashes when BT phone is offline
[profile/ivi/lemolo.git] / utils / i18n.c
1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif
4 #include <Elementary.h>
5 #include <Eldbus.h>
6
7 #include "i18n.h"
8 #include "log.h"
9
10 typedef struct _Locale_Manager Locale_Manager;
11 typedef struct _Locale_Bus_Object Locale_Bus_Object;
12
13 static const char bus_name[] = "org.freedesktop.locale1";
14
15 static Eldbus_Connection *bus_conn = NULL;
16 static char *bus_id = NULL;
17 static Locale_Manager *locale_manager = NULL;
18 static Eldbus_Signal_Handler *sig_locale_properties_changed = NULL;
19
20 struct _Locale_Callback_List_Node
21 {
22         EINA_INLIST;
23         void (*cb)(void *data);
24         const void *cb_data;
25 };
26
27 static Eina_Inlist *cbs_locale_connected = NULL;
28 static Eina_Inlist *cbs_locale_disconnected = NULL;
29 static Eina_Inlist *cbs_locale_properties_changed = NULL;
30
31 #define LOCALE_IFACE                            "org.freedesktop.locale1"
32 #define LOCALE_BUS_PATH                         "/org/freedesktop/locale1"
33 #define LOCALE_PROPERTIES_IFACE                 "org.freedesktop.DBus.Properties"
34 #define PROP_LOCALE_STRING                      "Locale"
35
36 struct _Locale_Bus_Object
37 {
38         const char *path;
39         Eina_List *dbus_signals; /* of Eldbus_Signal_Handler */
40 };
41
42 static void _bus_object_free(Locale_Bus_Object *o)
43 {
44         Eldbus_Signal_Handler *sh;
45
46         eina_stringshare_del(o->path);
47
48         EINA_LIST_FREE(o->dbus_signals, sh)
49                 eldbus_signal_handler_del(sh);
50 }
51
52 struct _Locale_Manager
53 {
54         Locale_Bus_Object base;
55         const char *name;
56         unsigned int interfaces;
57         const char *lang;
58 };
59
60 static Locale_Manager *_locale_get(void)
61 {
62         return locale_manager;
63 }
64
65 static void _notify_locale_callbacks_list(Eina_Inlist *list)
66 {
67         Locale_Callback_List_Node *node;
68
69         EINA_INLIST_FOREACH(list, node)
70                 node->cb((void *) node->cb_data);
71 }
72
73 static void _locale_get_property_reply(void *data, Eldbus_Message *msg,
74                                                 Eldbus_Pending *pending __UNUSED__)
75 {
76         Eldbus_Message_Iter *variant, *array;
77         const char *type, *locale, *err_name, *err_message;
78
79         EINA_SAFETY_ON_NULL_RETURN(data);
80
81         if (!msg) {
82                 ERR("No message");
83                 return;
84         }
85
86         if (eldbus_message_error_get(msg, &err_name, &err_message)) {
87                 ERR("Failed to get property: %s: %s", err_name, err_message);
88                 return;
89         }
90
91         if (!eldbus_message_arguments_get(msg, "v", &variant)) {
92                 ERR("Could not get arguments");
93                 return;
94         }
95
96         if (!eldbus_message_iter_arguments_get(variant, "as", &array)) {
97                 ERR("Could not get arguments");
98                 return;
99         }
100
101         if (!eldbus_message_iter_get_and_next(array, 's', &locale)) {
102                 ERR("Could not get locale");
103                 return;
104         }
105
106         char *property_name = (char*) data;
107         Locale_Manager *m = _locale_get();
108
109         if (strcmp(property_name, PROP_LOCALE_STRING) == 0) {
110                 if (m->lang)
111                         eina_stringshare_replace(&m->lang, locale);
112                 else
113                         m->lang = eina_stringshare_add(locale);
114
115                 DBG("Locale is set to: %s", m->lang);
116         } else {
117                 return;
118         }
119
120         _notify_locale_callbacks_list(cbs_locale_properties_changed);
121 }
122
123 static void _locale_property_get(char *property, char *path, Eldbus_Message_Cb cb, void *data)
124 {
125         Eldbus_Message *msg;
126         char *interface_name;
127
128         EINA_SAFETY_ON_NULL_RETURN(property);
129         EINA_SAFETY_ON_NULL_RETURN(path);
130
131         if (strcmp(property, PROP_LOCALE_STRING) == 0) {
132                 interface_name = LOCALE_IFACE;
133         } else {
134                 return;
135         }
136
137         msg = eldbus_message_method_call_new(
138                 bus_id, LOCALE_BUS_PATH, LOCALE_PROPERTIES_IFACE, "Get");
139
140         eldbus_message_arguments_append(msg, "ss", interface_name, property);
141         eldbus_connection_send(bus_conn, msg, cb, data, -1);
142 }
143
144 static Locale_Manager *_locale_new(const char *path)
145 {
146         Locale_Manager *m = calloc(1, sizeof(Locale_Manager));
147         EINA_SAFETY_ON_NULL_RETURN_VAL(m, NULL);
148
149         m->base.path = eina_stringshare_add(path);
150         EINA_SAFETY_ON_NULL_GOTO(m->base.path, error_path);
151
152         return m;
153
154 error_path:
155         free(m);
156         return NULL;
157 }
158
159 static void _locale_free(Locale_Manager *m)
160 {
161         if (m->lang)
162                 eina_stringshare_del(m->lang);
163
164         eina_stringshare_del(m->base.path);
165
166         _bus_object_free(&m->base);
167         free(m);
168 }
169
170 static void _locale_properties_changed(void *data __UNUSED__, Eldbus_Message *msg)
171 {
172         Eldbus_Message_Iter *array1, *array2;
173         const char *interface, *err_name, *err_message;
174
175         if (eldbus_message_error_get(msg, &err_name, &err_message)) {
176                 ERR("Failed to get signal: %s: %s", err_name, err_message);
177                 return;
178         }
179
180         if (!eldbus_message_arguments_get(msg, "sa{sv}as", &interface, &array1, &array2)) {
181                 ERR("Could not get PropertiesChanged arguments");
182                 return;
183         }
184
185         DBG("Locale property changed at %s", interface);
186         if (strcmp(interface, LOCALE_IFACE) == 0) {
187                 _locale_property_get(PROP_LOCALE_STRING, LOCALE_BUS_PATH, _locale_get_property_reply, PROP_LOCALE_STRING);
188         }
189 }
190
191 static void _locale_load(void)
192 {
193         locale_manager = _locale_new("/");
194         EINA_SAFETY_ON_NULL_RETURN(locale_manager);
195 }
196
197 static void _locale_connected(const char *id)
198 {
199         free(bus_id);
200         bus_id = strdup(id);
201
202         sig_locale_properties_changed = eldbus_signal_handler_add(
203                 bus_conn, bus_id, NULL,
204                 LOCALE_PROPERTIES_IFACE,
205                 "PropertiesChanged",
206                 _locale_properties_changed, NULL);
207
208         _locale_load();
209
210         _notify_locale_callbacks_list(cbs_locale_connected);
211 }
212
213 static void _locale_disconnected(void)
214 {
215         if (sig_locale_properties_changed) {
216                 eldbus_signal_handler_del(sig_locale_properties_changed);
217                 sig_locale_properties_changed = NULL;
218         }
219
220         if (bus_id) {
221                 _notify_locale_callbacks_list(cbs_locale_disconnected);
222                 free(bus_id);
223                 bus_id = NULL;
224         }
225 }
226
227 static void _name_owner_changed(void *data __UNUSED__, Eldbus_Message *msg)
228 {
229         const char *name, *from, *to;
230
231         if (!eldbus_message_arguments_get(msg, "sss", &name, &from, &to)) {
232                 ERR("Could not get NameOwnerChanged arguments");
233                 return;
234         }
235
236         if (strcmp(name, bus_name) != 0)
237                 return;
238
239         DBG("NameOwnerChanged %s from=%s to=%s", name, from, to);
240
241         if (from[0] == '\0' && to[0] != '\0') {
242                 INF("localed appeared as %s", to);
243                 _locale_connected(to);
244         } else if (from[0] != '\0' && to[0] == '\0') {
245                 INF("localed disappeared from %s", from);
246                 _locale_disconnected();
247         }
248 }
249
250 static void _locale_get_name_owner(void *data __UNUSED__, Eldbus_Message *msg,
251                                                 Eldbus_Pending *pending __UNUSED__)
252 {
253         const char *id;
254         const char *err_name, *err_message;
255
256         if (!msg) {
257                 ERR("No message");
258                 return;
259         }
260
261         if (eldbus_message_error_get(msg, &err_name, &err_message)) {
262                 ERR("Failed to get name owner: %s: %s", err_name, err_message);
263                 return;
264         }
265
266         if (!eldbus_message_arguments_get(msg, "s", &id)) {
267                 ERR("Could not get arguments");
268                 return;
269         }
270
271         if (!id || id[0] == '\0') {
272                 ERR("No name owner fo %s!", bus_name);
273                 return;
274         }
275
276         INF("localed bus id: %s", id);
277         _locale_connected(id);
278 }
279
280 const char* locale_lang_get(void)
281 {
282         Locale_Manager *m = _locale_get();
283         EINA_SAFETY_ON_NULL_RETURN_VAL(m, EINA_FALSE);
284
285         return m->lang;
286 }
287
288 Eina_Bool locale_init(void)
289 {
290         if (!elm_need_eldbus()) {
291                 CRITICAL("Elementary does not support DBus.");
292                 return EINA_FALSE;
293         }
294
295         bus_conn = eldbus_connection_get(ELDBUS_CONNECTION_TYPE_SYSTEM);
296         if (!bus_conn) {
297                 CRITICAL("Could not get DBus System Bus");
298                 return EINA_FALSE;
299         }
300
301         eldbus_signal_handler_add(bus_conn, ELDBUS_FDO_BUS, ELDBUS_FDO_PATH,
302                                         ELDBUS_FDO_INTERFACE,
303                                         "NameOwnerChanged",
304                                         _name_owner_changed, NULL);
305
306         eldbus_name_owner_get(bus_conn, bus_name, _locale_get_name_owner, NULL);
307
308         return EINA_TRUE;
309 }
310
311 void locale_shutdown(void)
312 {
313         if (locale_manager) {
314                 _locale_free(locale_manager);
315                 locale_manager = NULL;
316         }       
317         _locale_disconnected();
318 }
319
320 static Locale_Callback_List_Node * _locale_callback_list_node_create(
321         void (*cb)(void *data),const void *data)
322 {
323         Locale_Callback_List_Node  *node_new;
324
325         node_new = calloc(1, sizeof(Locale_Callback_List_Node));
326         EINA_SAFETY_ON_NULL_RETURN_VAL(node_new, NULL);
327
328         node_new->cb_data = data;
329         node_new->cb = cb;
330
331         return node_new;
332 }
333
334 static void _locale_callback_list_delete(Eina_Inlist **list,
335                                         Locale_Callback_List_Node *node)
336 {
337         EINA_SAFETY_ON_NULL_RETURN(*list);
338         *list = eina_inlist_remove(*list, EINA_INLIST_GET(node));
339         free(node);
340 }
341
342 Locale_Callback_List_Node *
343 locale_properties_changed_cb_add(void (*cb)(void *data), const void *data)
344 {
345         Locale_Callback_List_Node *node;
346
347         EINA_SAFETY_ON_NULL_RETURN_VAL(cb, NULL);
348         node = _locale_callback_list_node_create(cb, data);
349         EINA_SAFETY_ON_NULL_RETURN_VAL(node, NULL);
350
351         cbs_locale_properties_changed = eina_inlist_append(cbs_locale_properties_changed,
352                                                 EINA_INLIST_GET(node));
353
354         return node;
355 }
356
357 void locale_properties_changed_cb_del(Locale_Callback_List_Node *node)
358 {
359         EINA_SAFETY_ON_NULL_RETURN(node);
360         _locale_callback_list_delete(&cbs_locale_properties_changed, node);
361 }