Tizen 2.1 release
[platform/core/uifw/e17.git] / src / modules / connman / agent.c
1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif
4
5 #include <stdbool.h>
6 #include <stdlib.h>
7 #include <string.h>
8
9 #include "e.h"
10 #include "agent.h"
11 #include "E_Connman.h"
12 #include "e_mod_main.h"
13 #define AGENT_IFACE "net.connman.Agent"
14
15 typedef struct _E_Connman_Agent_Input E_Connman_Agent_Input;
16
17 struct Connman_Field
18 {
19    const char *name;
20
21    const char *type;
22    const char *requirement;
23    const char *value;
24    Eina_Array *alternates;
25 };
26
27 struct _E_Connman_Agent_Input
28 {
29    char *key;
30    char *value;
31    int show_password;
32 };
33
34 struct _E_Connman_Agent
35 {
36    E_Dialog *dialog;
37    E_DBus_Object *obj;
38    DBusMessage *msg;
39    E_DBus_Connection *conn;
40    Eina_Bool canceled:1;
41 };
42
43 static void
44 _dict_append_basic(DBusMessageIter *dict, const char *key, void *val)
45 {
46    DBusMessageIter entry, value;
47
48    dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
49                                     NULL, &entry);
50
51    dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
52    dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
53                                     DBUS_TYPE_STRING_AS_STRING, &value);
54    dbus_message_iter_append_basic(&value, DBUS_TYPE_STRING, &val);
55    dbus_message_iter_close_container(&entry, &value);
56
57    dbus_message_iter_close_container(dict, &entry);
58 }
59
60 static void
61 _dialog_ok_cb(void *data, E_Dialog *dialog)
62 {
63    E_Connman_Agent *agent = data;
64    E_Connman_Agent_Input *input;
65    Evas_Object *toolbook, *list;
66    DBusMessageIter iter, dict;
67    Eina_List *input_list, *l;
68    DBusMessage *reply;
69
70    toolbook = agent->dialog->content_object;
71
72    /* fugly - no toolbook page get */
73    list = evas_object_data_get(toolbook, "mandatory");
74    if ((!list) || (!evas_object_visible_get(list)))
75      {
76         list = evas_object_data_get(toolbook, "alternate");
77         if ((!list) || (!evas_object_visible_get(list)))
78           {
79              ERR("Couldn't get user input.");
80              e_object_del(E_OBJECT(dialog));
81              return;
82           }
83      }
84
85    agent->canceled = EINA_FALSE;
86    input_list = evas_object_data_get(list, "input_list");
87
88    reply = dbus_message_new_method_return(agent->msg);
89    dbus_message_iter_init_append(reply, &iter);
90
91    dbus_message_iter_open_container(
92       &iter, DBUS_TYPE_ARRAY,
93       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
94       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
95       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
96
97    EINA_LIST_FOREACH(input_list, l, input)
98       _dict_append_basic(&dict, input->key, input->value);
99
100    dbus_message_iter_close_container(&iter, &dict);
101
102    dbus_message_set_no_reply(reply, EINA_TRUE);
103    e_dbus_message_send(agent->conn, reply, NULL, -1, NULL);
104
105    e_object_del(E_OBJECT(dialog));
106 }
107
108 static void
109 _dialog_cancel_cb(void *data, E_Dialog *dialog)
110 {
111    E_Connman_Agent *agent = data;
112    agent->canceled = EINA_TRUE;
113    e_object_del(E_OBJECT(dialog));
114 }
115
116 static void
117 _dialog_key_down_cb(void *data, Evas *e __UNUSED__, Evas_Object *o __UNUSED__,
118                     void *event)
119 {
120    Evas_Event_Key_Down *ev = event;
121    E_Connman_Agent *agent = data;
122
123    if (!strcmp(ev->keyname, "Return"))
124      _dialog_ok_cb(agent, agent->dialog);
125    else if (strcmp(ev->keyname, "Escape") == 0)
126      _dialog_cancel_cb(agent, agent->dialog);
127 }
128
129 static void
130 _dialog_cancel(E_Connman_Agent *agent)
131 {
132    DBusMessage *reply;
133
134    reply = dbus_message_new_error(agent->msg,
135                                   "net.connman.Agent.Error.Canceled",
136                                   "User canceled dialog");
137    dbus_message_set_no_reply(reply, EINA_TRUE);
138    e_dbus_message_send(agent->conn, reply, NULL, -1, NULL);
139 }
140
141 static void
142 _dialog_del_cb(void *data)
143 {
144    E_Dialog *dialog = data;
145    E_Connman_Agent *agent = e_object_data_get(E_OBJECT(dialog));
146
147    if (agent->canceled)
148      _dialog_cancel(agent);
149
150    // FIXME need to mark cs->pending_connect = NULL;
151    dbus_message_unref(agent->msg);
152    agent->dialog = NULL;
153 }
154
155 static void
156 _page_del(void *data __UNUSED__, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
157 {
158    E_Connman_Agent_Input *input;
159    Eina_List *input_list;
160
161    input_list = evas_object_data_get(obj, "input_list");
162    EINA_LIST_FREE(input_list, input)
163      {
164         free(input->key);
165         free(input);
166      }
167 }
168
169 static void
170 _show_password_cb(void *data, Evas_Object *obj, void *event  __UNUSED__)
171 {
172    Evas_Object *entry = data;
173    int hidden;
174
175    hidden = !e_widget_check_checked_get(obj);
176    e_widget_entry_password_set(entry, hidden);
177 }
178
179 static void
180 _dialog_field_add(E_Connman_Agent *agent, struct Connman_Field *field)
181 {
182    Evas_Object *toolbook, *list, *framelist, *entry, *check;
183    E_Connman_Agent_Input *input;
184    Eina_List *input_list;
185    Eina_Bool mandatory;
186    char header[64];
187    Evas *evas;
188
189    evas = agent->dialog->win->evas;
190    toolbook = agent->dialog->content_object;
191    mandatory = !strcmp(field->requirement, "mandatory");
192
193    if ((!mandatory) && (strcmp(field->requirement, "alternate")))
194      {
195         WRN("Field not handled: %s %s", field->name, field->type);
196         return;
197      }
198
199    input = E_NEW(E_Connman_Agent_Input, 1);
200    input->key = strdup(field->name);
201    entry = e_widget_entry_add(evas, &(input->value), NULL, NULL, NULL);
202    evas_object_show(entry);
203
204    list = evas_object_data_get(toolbook, field->requirement);
205    if (!list)
206      {
207         list = e_widget_list_add(evas, 0, 0);
208         e_widget_toolbook_page_append(toolbook, NULL, field->name,
209                                       list, 1, 1, 1, 1, 0.5, 0.0);
210         evas_object_data_set(toolbook, field->requirement, list);
211
212         e_widget_toolbook_page_show(toolbook, 0);
213         evas_object_event_callback_add(list, EVAS_CALLBACK_DEL,
214                                        _page_del, NULL);
215
216         if (mandatory)
217           e_widget_focus_set(entry, 1);
218      }
219
220    input_list = evas_object_data_get(list, "input_list");
221    input_list = eina_list_append(input_list, input);
222    evas_object_data_set(list, "input_list", input_list);
223
224    snprintf(header, sizeof(header), "%s required to access network:",
225             field->name);
226    framelist = e_widget_framelist_add(evas, header, 0);
227    evas_object_show(framelist);
228    e_widget_list_object_append(list, framelist, 1, 1, 0.5);
229
230    e_widget_framelist_object_append(framelist, entry);
231
232    if ((!strcmp(field->name, "Passphrase")) ||
233        (!strcmp(field->name, "Password")))
234      {
235         e_widget_entry_password_set(entry, 1);
236
237         check = e_widget_check_add(evas, _("Show password"),
238                                    &(input->show_password));
239         evas_object_show(check);
240         e_widget_framelist_object_append(framelist, check);
241
242         evas_object_smart_callback_add(check, "changed", _show_password_cb,
243                                        entry);
244      }
245 }
246
247 static E_Dialog *
248 _dialog_new(E_Connman_Agent *agent)
249 {
250    Evas_Object *toolbook;
251    E_Dialog *dialog;
252    Evas *evas;
253    int mw, mh;
254
255    dialog = e_dialog_new(NULL, "E", "connman_request_input");
256    if (!dialog)
257      return NULL;
258
259    e_dialog_title_set(dialog, _("Input requested"));
260    e_dialog_border_icon_set(dialog, "dialog-ask");
261
262    e_dialog_button_add(dialog, _("Ok"), NULL, _dialog_ok_cb, agent);
263    e_dialog_button_add(dialog, _("Cancel"), NULL, _dialog_cancel_cb, agent);
264    agent->canceled = EINA_TRUE; /* if win is closed it works like cancel */
265
266    evas = dialog->win->evas;
267
268    toolbook = e_widget_toolbook_add(evas, 48 * e_scale, 48 * e_scale);
269    evas_object_show(toolbook);
270
271    e_widget_size_min_get(toolbook, &mw, &mh);
272    /* is it a hack ? */
273    if (mw < 260)
274      mw = 260;
275    if (mh < 130)
276      mh = 130;
277    e_dialog_content_set(dialog, toolbook, mw, mh);
278    e_dialog_show(dialog);
279    e_dialog_resizable_set(dialog, 1);
280
281    evas_object_event_callback_add(dialog->bg_object, EVAS_CALLBACK_KEY_DOWN,
282                                   _dialog_key_down_cb, agent);
283
284    e_object_del_attach_func_set(E_OBJECT(dialog), _dialog_del_cb);
285    e_object_data_set(E_OBJECT(dialog), agent);
286
287    e_dialog_button_focus_num(dialog, 0);
288    e_win_centered_set(dialog->win, 1);
289
290    return dialog;
291 }
292
293 static DBusMessage *
294 _agent_release(E_DBus_Object *obj, DBusMessage *msg)
295 {
296    E_Connman_Agent *agent;
297    DBusMessage *reply;
298
299    DBG("Agent released");
300
301    reply = dbus_message_new_method_return(msg);
302
303    agent = e_dbus_object_data_get(obj);
304    EINA_SAFETY_ON_FALSE_RETURN_VAL(agent, reply);
305
306    if (agent->dialog)
307      e_object_del(E_OBJECT(agent->dialog));
308
309    return reply;
310 }
311
312 static DBusMessage *
313 _agent_report_error(E_DBus_Object *obj, DBusMessage *msg)
314 {
315    return NULL;
316 }
317
318 static DBusMessage *
319 _agent_request_browser(E_DBus_Object *obj, DBusMessage *msg)
320 {
321    return NULL;
322 }
323
324 static Eina_Bool
325 _parse_field_value(struct Connman_Field *field, const char *key,
326                    DBusMessageIter *value)
327 {
328    if (!strcmp(key, "Type"))
329      {
330         EINA_SAFETY_ON_FALSE_RETURN_VAL(
331            dbus_message_iter_get_arg_type(value) == DBUS_TYPE_STRING,
332            EINA_FALSE);
333         dbus_message_iter_get_basic(value, &field->type);
334         return EINA_TRUE;
335      }
336
337    if (!strcmp(key, "Requirement"))
338      {
339         EINA_SAFETY_ON_FALSE_RETURN_VAL(
340            dbus_message_iter_get_arg_type(value) == DBUS_TYPE_STRING,
341            EINA_FALSE);
342         dbus_message_iter_get_basic(value, &field->requirement);
343         return EINA_TRUE;
344      }
345
346    if (!strcmp(key, "Alternates"))
347      {
348         EINA_SAFETY_ON_FALSE_RETURN_VAL(
349            dbus_message_iter_get_arg_type(value) == DBUS_TYPE_ARRAY,
350            EINA_FALSE);
351          /* ignore alternates */
352         return EINA_TRUE;
353      }
354
355    if (!strcmp(key, "Value"))
356      {
357         EINA_SAFETY_ON_FALSE_RETURN_VAL(
358            dbus_message_iter_get_arg_type(value) == DBUS_TYPE_STRING,
359            EINA_FALSE);
360         dbus_message_iter_get_basic(value, &field->value);
361         return EINA_TRUE;
362      }
363
364    DBG("Ignored unknown argument: %s", key);
365    return EINA_FALSE;
366 }
367
368 static Eina_Bool
369 _parse_field(struct Connman_Field *field, DBusMessageIter *value)
370 {
371    DBusMessageIter dict;
372
373    EINA_SAFETY_ON_FALSE_RETURN_VAL(
374       dbus_message_iter_get_arg_type(value) == DBUS_TYPE_ARRAY, EINA_FALSE);
375    dbus_message_iter_recurse(value, &dict);
376
377    for (; dbus_message_iter_get_arg_type(&dict) != DBUS_TYPE_INVALID;
378         dbus_message_iter_next(&dict))
379      {
380         DBusMessageIter entry, var;
381         const char *key;
382
383         dbus_message_iter_recurse(&dict, &entry);
384         EINA_SAFETY_ON_FALSE_RETURN_VAL(
385            dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING,
386            EINA_FALSE);
387         dbus_message_iter_get_basic(&entry, &key);
388
389         dbus_message_iter_next(&entry);
390         EINA_SAFETY_ON_FALSE_RETURN_VAL(
391            dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_VARIANT,
392            EINA_FALSE);
393         dbus_message_iter_recurse(&entry, &var);
394
395         if (!_parse_field_value(field, key, &var))
396           return EINA_FALSE;
397      }
398
399    return EINA_TRUE;
400 }
401
402 static DBusMessage *
403 _agent_request_input(E_DBus_Object *obj, DBusMessage *msg)
404 {
405    E_Connman_Module_Context *ctxt = connman_mod->data;
406    const Eina_List *l;
407    E_Connman_Instance *inst;
408    DBusMessageIter iter, dict;
409    E_Connman_Agent *agent;
410    DBusMessage *reply;
411    const char *path;
412
413    agent = e_dbus_object_data_get(obj);
414
415    /* Discard previous requests */
416    if (agent->msg)
417      dbus_message_unref(agent->msg);
418    agent->msg = dbus_message_ref(msg);
419
420    EINA_LIST_FOREACH(ctxt->instances, l, inst)
421      econnman_popup_del(inst);
422
423    if (agent->dialog)
424      e_object_del(E_OBJECT(agent->dialog));
425    agent->dialog = _dialog_new(agent);
426    EINA_SAFETY_ON_NULL_GOTO(agent->dialog, err);
427
428    dbus_message_iter_init(msg, &iter);
429    if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_OBJECT_PATH)
430      goto err;
431    dbus_message_iter_get_basic(&iter, &path);
432
433    dbus_message_iter_next(&iter);
434    if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
435      goto err;
436    dbus_message_iter_recurse(&iter, &dict);
437
438    for (; dbus_message_iter_get_arg_type(&dict) != DBUS_TYPE_INVALID;
439         dbus_message_iter_next(&dict))
440      {
441         struct Connman_Field field = { NULL, NULL, NULL, NULL, NULL };
442         DBusMessageIter entry, var;
443
444         dbus_message_iter_recurse(&dict, &entry);
445         if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
446           goto err;
447         dbus_message_iter_get_basic(&entry, &field.name);
448
449         dbus_message_iter_next(&entry);
450         if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
451           goto err;
452         dbus_message_iter_recurse(&entry, &var);
453
454         if (!_parse_field(&field, &var))
455           return NULL;
456
457         DBG("AGENT Got field:\n"
458             "\tName: %s\n"
459             "\tType: %s\n"
460             "\tRequirement: %s\n"
461             "\tAlternates: (omit array)\n"
462             "\tValue: %s",
463             field.name, field.type, field.requirement, field.value);
464
465         _dialog_field_add(agent, &field);
466      }
467
468    return NULL;
469
470 err:
471    dbus_message_unref(msg);
472    agent->msg = NULL;
473    WRN("Failed to parse msg");
474    reply = dbus_message_new_method_return(msg);
475    return reply;
476 }
477
478 static DBusMessage *
479 _agent_cancel(E_DBus_Object *obj, DBusMessage *msg)
480 {
481    E_Connman_Agent *agent;
482    DBusMessage *reply;
483
484    DBG("Agent canceled");
485
486    reply = dbus_message_new_method_return(msg);
487
488    agent = e_dbus_object_data_get(obj);
489    EINA_SAFETY_ON_FALSE_RETURN_VAL(agent, reply);
490
491    if (agent->dialog)
492      e_object_del(E_OBJECT(agent->dialog));
493
494    return reply;
495 }
496
497 E_Connman_Agent *
498 econnman_agent_new(E_DBus_Connection *edbus_conn)
499 {
500    E_DBus_Object *agent_obj;
501    E_DBus_Interface *iface;
502    E_Connman_Agent *agent;
503
504    agent = E_NEW(E_Connman_Agent, 1);
505    EINA_SAFETY_ON_NULL_RETURN_VAL(agent, NULL);
506
507    iface = e_dbus_interface_new(AGENT_IFACE);
508    if (!iface)
509      {
510         ERR("Failed to create e_dbus interface");
511         free(agent);
512         return NULL;
513      }
514
515    e_dbus_interface_method_add(iface, "Release", "", "", _agent_release);
516    e_dbus_interface_method_add(iface, "ReportError", "os", "",
517                                _agent_report_error);
518    e_dbus_interface_method_add(iface, "RequestBrowser", "os", "",
519                                _agent_request_browser);
520    e_dbus_interface_method_add(iface, "RequestInput", "oa{sv}", "a{sv}",
521                                _agent_request_input);
522    e_dbus_interface_method_add(iface, "Cancel", "", "", _agent_cancel);
523
524    agent_obj = e_dbus_object_add(edbus_conn, AGENT_PATH, agent);
525    if (!agent_obj)
526      {
527         ERR("Failed to create e_dbus object");
528         e_dbus_interface_unref(iface);
529         free(agent);
530         return NULL;
531      }
532
533    agent->obj = agent_obj;
534    agent->conn = edbus_conn;
535    e_dbus_object_interface_attach(agent_obj, iface);
536
537    e_dbus_interface_unref(iface);
538
539    return agent;
540 }
541
542 void
543 econnman_agent_del(E_Connman_Agent *agent)
544 {
545    EINA_SAFETY_ON_NULL_RETURN(agent);
546    e_dbus_object_free(agent->obj);
547    free(agent);
548 }