core: Update copyright information
[framework/connectivity/connman.git] / src / agent.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <errno.h>
27 #include <stdlib.h>
28 #include <string.h>
29
30 #include <gdbus.h>
31
32 #include "connman.h"
33
34 static DBusConnection *connection = NULL;
35 static guint agent_watch = 0;
36 static gchar *agent_path = NULL;
37 static gchar *agent_sender = NULL;
38
39 static void agent_free(void)
40 {
41         agent_watch = 0;
42
43         g_free(agent_sender);
44         agent_sender = NULL;
45
46         g_free(agent_path);
47         agent_path = NULL;
48 }
49
50 static void agent_disconnect(DBusConnection *connection, void *data)
51 {
52         DBG("data %p", data);
53
54         agent_free();
55 }
56
57 int __connman_agent_register(const char *sender, const char *path)
58 {
59         DBG("sender %s path %s", sender, path);
60
61         if (agent_path != NULL)
62                 return -EEXIST;
63
64         agent_sender = g_strdup(sender);
65         agent_path = g_strdup(path);
66
67         agent_watch = g_dbus_add_disconnect_watch(connection, sender,
68                                                 agent_disconnect, NULL, NULL);
69
70         return 0;
71 }
72
73 int __connman_agent_unregister(const char *sender, const char *path)
74 {
75         DBG("sender %s path %s", sender, path);
76
77         if (agent_path == NULL)
78                 return -ESRCH;
79
80         if (agent_watch > 0)
81                 g_dbus_remove_watch(connection, agent_watch);
82
83         agent_free();
84
85         return 0;
86 }
87
88 struct request_input_reply {
89         struct connman_service *service;
90         authentication_cb_t callback;
91         void *user_data;
92 };
93
94 static void request_input_passphrase_reply(DBusPendingCall *call, void *user_data)
95 {
96         struct request_input_reply *passphrase_reply = user_data;
97         connman_bool_t values_received = FALSE;
98         connman_bool_t wps = FALSE;
99         char *identity = NULL;
100         char *passphrase = NULL;
101         char *wpspin = NULL;
102         char *key;
103         char *name = NULL;
104         int name_len = 0;
105         DBusMessageIter iter, dict;
106         DBusMessage *reply = dbus_pending_call_steal_reply(call);
107
108         if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR)
109                 goto done;
110
111         values_received = TRUE;
112
113         dbus_message_iter_init(reply, &iter);
114         dbus_message_iter_recurse(&iter, &dict);
115         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
116                 DBusMessageIter entry, value;
117
118                 dbus_message_iter_recurse(&dict, &entry);
119                 if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
120                         break;
121
122                 dbus_message_iter_get_basic(&entry, &key);
123
124                 if (g_str_equal(key, "Identity")) {
125                         dbus_message_iter_next(&entry);
126                         if (dbus_message_iter_get_arg_type(&entry)
127                                                         != DBUS_TYPE_VARIANT)
128                                 break;
129                         dbus_message_iter_recurse(&entry, &value);
130                         dbus_message_iter_get_basic(&value, &identity);
131
132                 } else if (g_str_equal(key, "Passphrase")) {
133                         dbus_message_iter_next(&entry);
134                         if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
135                                 break;
136                         dbus_message_iter_recurse(&entry, &value);
137                         dbus_message_iter_get_basic(&value, &passphrase);
138
139                 } else if (g_str_equal(key, "WPS")) {
140                         wps = TRUE;
141
142                         dbus_message_iter_next(&entry);
143                         if (dbus_message_iter_get_arg_type(&entry)
144                                                         != DBUS_TYPE_VARIANT)
145                                 break;
146                         dbus_message_iter_recurse(&entry, &value);
147                         dbus_message_iter_get_basic(&value, &wpspin);
148                         break;
149                 } else if (g_str_equal(key, "Name")) {
150                         dbus_message_iter_next(&entry);
151                         if (dbus_message_iter_get_arg_type(&entry)
152                                                         != DBUS_TYPE_VARIANT)
153                                 break;
154                         dbus_message_iter_recurse(&entry, &value);
155                         dbus_message_iter_get_basic(&value, &name);
156                         name_len = strlen(name);
157                 } else if (g_str_equal(key, "SSID")) {
158                         dbus_message_iter_next(&entry);
159                         if (dbus_message_iter_get_arg_type(&entry)
160                                                         != DBUS_TYPE_VARIANT)
161                                 break;
162                         dbus_message_iter_recurse(&entry, &value);
163                         if (dbus_message_iter_get_arg_type(&value)
164                                                         != DBUS_TYPE_VARIANT)
165                                 break;
166                         if (dbus_message_iter_get_element_type(&value)
167                                                         != DBUS_TYPE_VARIANT)
168                                 break;
169                         dbus_message_iter_get_fixed_array(&value, &name,
170                                                         &name_len);
171                 }
172                 dbus_message_iter_next(&dict);
173         }
174
175 done:
176         passphrase_reply->callback(passphrase_reply->service, values_received,
177                                 name, name_len,
178                                 identity, passphrase,
179                                 wps, wpspin,
180                                 passphrase_reply->user_data);
181         connman_service_unref(passphrase_reply->service);
182         dbus_message_unref(reply);
183         dbus_pending_call_unref(call);
184         g_free(passphrase_reply);
185 }
186
187 static void request_input_append_alternates(DBusMessageIter *iter,
188                                                         void *user_data)
189 {
190         const char *str = user_data;
191         char **alternates, **alternative;
192
193         if (str == NULL)
194                 return;
195
196         alternates = g_strsplit(str, ",", 0);
197         if (alternates == NULL)
198                 return;
199
200         for (alternative = alternates; *alternative != NULL; alternative++)
201                 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
202                                                                 alternative);
203
204         g_strfreev(alternates);
205 }
206
207 static void request_input_append_identity(DBusMessageIter *iter,
208                                                         void *user_data)
209 {
210         char *str = "string";
211
212         connman_dbus_dict_append_basic(iter, "Type",
213                                 DBUS_TYPE_STRING, &str);
214         str = "mandatory";
215         connman_dbus_dict_append_basic(iter, "Requirement",
216                                 DBUS_TYPE_STRING, &str);
217 }
218
219 static void request_input_append_passphrase(DBusMessageIter *iter,
220                                                         void *user_data)
221 {
222         struct connman_service *service = user_data;
223         char *value;
224         const char *phase2;
225
226         switch (__connman_service_get_security(service)) {
227         case CONNMAN_SERVICE_SECURITY_WEP:
228                 value = "wep";
229                 break;
230         case CONNMAN_SERVICE_SECURITY_PSK:
231                 value = "psk";
232                 break;
233         case CONNMAN_SERVICE_SECURITY_8021X:
234                 phase2 = __connman_service_get_phase2(service);
235
236                 if (phase2 != NULL && (
237                                 g_str_has_suffix(phase2, "GTC") == TRUE ||
238                                 g_str_has_suffix(phase2, "OTP") == TRUE))
239                         value = "response";
240                 else
241                         value = "passphrase";
242
243                 break;
244         default:
245                 value = "string";
246                 break;
247         }
248         connman_dbus_dict_append_basic(iter, "Type",
249                                 DBUS_TYPE_STRING, &value);
250         value = "mandatory";
251         connman_dbus_dict_append_basic(iter, "Requirement",
252                                 DBUS_TYPE_STRING, &value);
253
254         if (__connman_service_wps_enabled(service) == TRUE) {
255                 connman_dbus_dict_append_array(iter, "Alternates",
256                                         DBUS_TYPE_STRING,
257                                         request_input_append_alternates,
258                                         "WPS");
259         }
260 }
261
262 static void request_input_append_wps(DBusMessageIter *iter, void *user_data)
263 {
264         const char *str = "wpspin";
265
266         connman_dbus_dict_append_basic(iter, "Type",
267                                 DBUS_TYPE_STRING, &str);
268         str = "alternate";
269         connman_dbus_dict_append_basic(iter, "Requirement",
270                                 DBUS_TYPE_STRING, &str);
271 }
272
273 static void request_input_append_name(DBusMessageIter *iter, void *user_data)
274 {
275         const char *str = "string";
276
277         connman_dbus_dict_append_basic(iter, "Type",
278                                 DBUS_TYPE_STRING, &str);
279         str = "mandatory";
280         connman_dbus_dict_append_basic(iter, "Requirement",
281                                 DBUS_TYPE_STRING, &str);
282         connman_dbus_dict_append_array(iter, "Alternates",
283                                 DBUS_TYPE_STRING,
284                                 request_input_append_alternates,
285                                 "SSID");
286 }
287
288 static void request_input_append_ssid(DBusMessageIter *iter, void *user_data)
289 {
290         const char *str = "ssid";
291
292         connman_dbus_dict_append_basic(iter, "Type",
293                                 DBUS_TYPE_STRING, &str);
294         str = "alternate";
295         connman_dbus_dict_append_basic(iter, "Requirement",
296                                 DBUS_TYPE_STRING, &str);
297 }
298
299 static void request_input_append_password(DBusMessageIter *iter,
300                                                         void *user_data)
301 {
302         char *str = "passphrase";
303
304         connman_dbus_dict_append_basic(iter, "Type",
305                                 DBUS_TYPE_STRING, &str);
306         str = "mandatory";
307         connman_dbus_dict_append_basic(iter, "Requirement",
308                                 DBUS_TYPE_STRING, &str);
309 }
310
311 static void request_input_login_reply(DBusPendingCall *call, void *user_data)
312 {
313         struct request_input_reply *username_password_reply = user_data;
314         char *username = NULL;
315         char *password = NULL;
316         char *key;
317         DBusMessageIter iter, dict;
318         DBusMessage *reply = dbus_pending_call_steal_reply(call);
319
320         if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR)
321                 goto done;
322
323         dbus_message_iter_init(reply, &iter);
324         dbus_message_iter_recurse(&iter, &dict);
325         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
326                 DBusMessageIter entry, value;
327
328                 dbus_message_iter_recurse(&dict, &entry);
329                 if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
330                         break;
331
332                 dbus_message_iter_get_basic(&entry, &key);
333
334                 if (g_str_equal(key, "Username")) {
335                         dbus_message_iter_next(&entry);
336                         if (dbus_message_iter_get_arg_type(&entry)
337                                                         != DBUS_TYPE_VARIANT)
338                                 break;
339                         dbus_message_iter_recurse(&entry, &value);
340                         dbus_message_iter_get_basic(&value, &username);
341
342                 } else if (g_str_equal(key, "Password")) {
343                         dbus_message_iter_next(&entry);
344                         if (dbus_message_iter_get_arg_type(&entry) !=
345                                                         DBUS_TYPE_VARIANT)
346                                 break;
347                         dbus_message_iter_recurse(&entry, &value);
348                         dbus_message_iter_get_basic(&value, &password);
349                 }
350
351                 dbus_message_iter_next(&dict);
352         }
353
354 done:
355         username_password_reply->callback(username_password_reply->service,
356                                         TRUE, NULL, 0,
357                                         username, password,
358                                         FALSE, NULL,
359                                         username_password_reply->user_data);
360         connman_service_unref(username_password_reply->service);
361         dbus_message_unref(reply);
362         g_free(username_password_reply);
363 }
364
365 int __connman_agent_request_passphrase_input(struct connman_service *service,
366                                 authentication_cb_t callback, void *user_data)
367 {
368         DBusMessage *message;
369         const char *path;
370         DBusMessageIter iter;
371         DBusMessageIter dict;
372         DBusPendingCall *call;
373         struct request_input_reply *passphrase_reply;
374
375         if (service == NULL || agent_path == NULL || callback == NULL)
376                 return -ESRCH;
377
378         message = dbus_message_new_method_call(agent_sender, agent_path,
379                                         CONNMAN_AGENT_INTERFACE,
380                                         "RequestInput");
381         if (message == NULL)
382                 return -ENOMEM;
383
384         dbus_message_iter_init_append(message, &iter);
385
386         path = __connman_service_get_path(service);
387         dbus_message_iter_append_basic(&iter,
388                                 DBUS_TYPE_OBJECT_PATH, &path);
389
390         connman_dbus_dict_open(&iter, &dict);
391
392         if (__connman_service_is_hidden(service)) {
393                 connman_dbus_dict_append_dict(&dict, "Name",
394                                         request_input_append_name, NULL);
395                 connman_dbus_dict_append_dict(&dict, "SSID",
396                                         request_input_append_ssid, NULL);
397         }
398
399         if (__connman_service_get_security(service) ==
400                         CONNMAN_SERVICE_SECURITY_8021X) {
401                 connman_dbus_dict_append_dict(&dict, "Identity",
402                                         request_input_append_identity, service);
403         }
404
405         if (__connman_service_get_security(service) !=
406                         CONNMAN_SERVICE_SECURITY_NONE) {
407                 connman_dbus_dict_append_dict(&dict, "Passphrase",
408                                         request_input_append_passphrase, service);
409         }
410
411         if (__connman_service_wps_enabled(service) == TRUE) {
412             connman_dbus_dict_append_dict(&dict, "WPS",
413                                 request_input_append_wps, NULL);
414         }
415
416         connman_dbus_dict_close(&iter, &dict);
417
418         passphrase_reply = g_try_new0(struct request_input_reply, 1);
419         if (passphrase_reply == NULL) {
420                 dbus_message_unref(message);
421                 return -ENOMEM;
422         }
423
424         if (dbus_connection_send_with_reply(connection, message,
425                                                 &call, -1) == FALSE) {
426                 dbus_message_unref(message);
427                 g_free(passphrase_reply);
428                 return -ESRCH;
429         }
430
431         if (call == NULL) {
432                 dbus_message_unref(message);
433                 g_free(passphrase_reply);
434                 return -ESRCH;
435         }
436
437         passphrase_reply->service = connman_service_ref(service);
438         passphrase_reply->callback = callback;
439         passphrase_reply->user_data = user_data;
440
441         dbus_pending_call_set_notify(call, request_input_passphrase_reply,
442                                 passphrase_reply, NULL);
443
444         dbus_message_unref(message);
445
446         return -EIO;
447 }
448
449 int __connman_agent_request_login_input(struct connman_service *service,
450                                 authentication_cb_t callback, void *user_data)
451 {
452         DBusMessage *message;
453         const char *path;
454         DBusMessageIter iter;
455         DBusMessageIter dict;
456         DBusPendingCall *call;
457         struct request_input_reply *username_password_reply;
458
459         if (service == NULL || agent_path == NULL || callback == NULL)
460                 return -ESRCH;
461
462         message = dbus_message_new_method_call(agent_sender, agent_path,
463                                         CONNMAN_AGENT_INTERFACE,
464                                         "RequestInput");
465         if (message == NULL)
466                 return -ENOMEM;
467
468         dbus_message_iter_init_append(message, &iter);
469
470         path = __connman_service_get_path(service);
471         dbus_message_iter_append_basic(&iter,
472                                 DBUS_TYPE_OBJECT_PATH, &path);
473
474         connman_dbus_dict_open(&iter, &dict);
475
476         connman_dbus_dict_append_dict(&dict, "Username",
477                                 request_input_append_identity, service);
478
479         connman_dbus_dict_append_dict(&dict, "Password",
480                                 request_input_append_password, service);
481
482         connman_dbus_dict_close(&iter, &dict);
483
484         username_password_reply = g_try_new0(struct request_input_reply, 1);
485         if (username_password_reply == NULL) {
486                 dbus_message_unref(message);
487                 return -ENOMEM;
488         }
489
490         if (dbus_connection_send_with_reply(connection, message,
491                                                         &call, -1) == FALSE) {
492                 dbus_message_unref(message);
493                 g_free(username_password_reply);
494                 return -ESRCH;
495         }
496
497         if (call == NULL) {
498                 dbus_message_unref(message);
499                 g_free(username_password_reply);
500                 return -ESRCH;
501         }
502
503         username_password_reply->service = connman_service_ref(service);
504         username_password_reply->callback = callback;
505         username_password_reply->user_data = user_data;
506
507         dbus_pending_call_set_notify(call, request_input_login_reply,
508                                                 username_password_reply, NULL);
509
510         dbus_message_unref(message);
511
512         return -EIO;
513 }
514
515 struct request_browser_reply_data {
516         struct connman_service *service;
517         browser_authentication_cb_t callback;
518         void *user_data;
519 };
520
521 static void request_browser_reply(DBusPendingCall *call, void *user_data)
522 {
523         struct request_browser_reply_data *browser_reply_data = user_data;
524         DBusMessage *reply = dbus_pending_call_steal_reply(call);
525         connman_bool_t result = FALSE;
526
527         if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR)
528                 goto done;
529
530         result = TRUE;
531
532 done:
533         browser_reply_data->callback(browser_reply_data->service,
534                                         result, browser_reply_data->user_data);
535         connman_service_unref(browser_reply_data->service);
536         dbus_message_unref(reply);
537         g_free(browser_reply_data);
538 }
539
540 int __connman_agent_request_browser(struct connman_service *service,
541                                 browser_authentication_cb_t callback,
542                                 const char *url, void *user_data)
543 {
544         struct request_browser_reply_data *browser_reply_data;
545         DBusPendingCall *call;
546         DBusMessage *message;
547         DBusMessageIter iter;
548         const char *path;
549
550         if (service == NULL || agent_path == NULL || callback == NULL)
551                 return -ESRCH;
552
553         if (url == NULL)
554                 url = "";
555
556         message = dbus_message_new_method_call(agent_sender, agent_path,
557                                         CONNMAN_AGENT_INTERFACE,
558                                         "RequestBrowser");
559         if (message == NULL)
560                 return -ENOMEM;
561
562         dbus_message_iter_init_append(message, &iter);
563
564         path = __connman_service_get_path(service);
565         dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path);
566
567         dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &url);
568
569         browser_reply_data = g_try_new0(struct request_browser_reply_data, 1);
570         if (browser_reply_data == NULL) {
571                 dbus_message_unref(message);
572                 return -ENOMEM;
573         }
574
575         if (dbus_connection_send_with_reply(connection, message,
576                                                         &call, -1) == FALSE) {
577                 dbus_message_unref(message);
578                 g_free(browser_reply_data);
579                 return -ESRCH;
580         }
581
582         if (call == NULL) {
583                 dbus_message_unref(message);
584                 g_free(browser_reply_data);
585                 return -ESRCH;
586         }
587
588         browser_reply_data->service = connman_service_ref(service);
589         browser_reply_data->callback = callback;
590         browser_reply_data->user_data = user_data;
591
592         dbus_pending_call_set_notify(call, request_browser_reply,
593                                                 browser_reply_data, NULL);
594
595         dbus_message_unref(message);
596
597         return -EIO;
598 }
599
600 struct report_error_data {
601         struct connman_service *service;
602         report_error_cb_t callback;
603         void *user_data;
604 };
605
606 static void report_error_reply(DBusPendingCall *call, void *user_data)
607 {
608         struct report_error_data *report_error = user_data;
609         DBusMessage *reply = dbus_pending_call_steal_reply(call);
610         gboolean retry = FALSE;
611         const char *dbus_err;
612
613         if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
614                 dbus_err = dbus_message_get_error_name(reply);
615                 if (dbus_err != NULL &&
616                         strcmp(dbus_err,
617                                 CONNMAN_AGENT_INTERFACE ".Error.Retry") == 0)
618                         retry = TRUE;
619         }
620
621         report_error->callback(report_error->service, retry,
622                         report_error->user_data);
623         connman_service_unref(report_error->service);
624         g_free(report_error);
625         dbus_message_unref(reply);
626 }
627
628 int __connman_agent_report_error(struct connman_service *service,
629                                 const char *error,
630                                 report_error_cb_t callback, void *user_data)
631 {
632         DBusMessage *message;
633         DBusMessageIter iter;
634         const char *path;
635         struct report_error_data *report_error;
636         DBusPendingCall *call;
637
638         if (service == NULL || agent_path == NULL || error == NULL ||
639                 callback == NULL)
640                 return -ESRCH;
641
642         message = dbus_message_new_method_call(agent_sender, agent_path,
643                                         CONNMAN_AGENT_INTERFACE,
644                                         "ReportError");
645         if (message == NULL)
646                 return -ENOMEM;
647
648         dbus_message_iter_init_append(message, &iter);
649
650         path = __connman_service_get_path(service);
651         dbus_message_iter_append_basic(&iter,
652                                 DBUS_TYPE_OBJECT_PATH, &path);
653         dbus_message_iter_append_basic(&iter,
654                                 DBUS_TYPE_STRING, &error);
655
656         report_error = g_try_new0(struct report_error_data, 1);
657         if (report_error == NULL) {
658                 dbus_message_unref(message);
659                 return -ENOMEM;
660         }
661
662         if (dbus_connection_send_with_reply(connection, message,
663                                                 &call, -1) == FALSE) {
664                 dbus_message_unref(message);
665                 g_free(report_error);
666                 return -ESRCH;
667         }
668
669         if (call == NULL) {
670                 dbus_message_unref(message);
671                 g_free(report_error);
672                 return -ESRCH;
673         }
674
675         report_error->service = connman_service_ref(service);
676         report_error->callback = callback;
677         report_error->user_data = user_data;
678         dbus_pending_call_set_notify(call, report_error_reply,
679                                 report_error, NULL);
680         dbus_message_unref(message);
681
682         return -EIO;
683 }
684
685 int __connman_agent_init(void)
686 {
687         DBG("");
688
689         connection = connman_dbus_get_connection();
690         if (connection == NULL)
691                 return -1;
692
693         return 0;
694 }
695
696 void __connman_agent_cleanup(void)
697 {
698         DBusMessage *message;
699
700         DBG("");
701
702         if (connection == NULL)
703                 return;
704
705         if (agent_watch > 0)
706                 g_dbus_remove_watch(connection, agent_watch);
707
708         if (agent_path == NULL)
709                 return;
710
711         message = dbus_message_new_method_call(agent_sender, agent_path,
712                                         CONNMAN_AGENT_INTERFACE, "Release");
713         if (message == NULL)
714                 return;
715
716         dbus_message_set_no_reply(message, TRUE);
717
718         g_dbus_send_message(connection, message);
719
720         agent_free();
721
722         dbus_connection_unref(connection);
723 }