agent: Added Username/Password request input logic
[platform/upstream/connman.git] / src / agent.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2010  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 wps = FALSE;
98         char *identity = NULL;
99         char *passphrase = NULL;
100         char *wpspin = NULL;
101         char *key;
102         DBusMessageIter iter, dict;
103         DBusMessage *reply = dbus_pending_call_steal_reply(call);
104
105         if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR)
106                 goto done;
107
108         dbus_message_iter_init(reply, &iter);
109         dbus_message_iter_recurse(&iter, &dict);
110         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
111                 DBusMessageIter entry, value;
112
113                 dbus_message_iter_recurse(&dict, &entry);
114                 if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
115                         break;
116
117                 dbus_message_iter_get_basic(&entry, &key);
118
119                 if (g_str_equal(key, "Identity")) {
120                         dbus_message_iter_next(&entry);
121                         if (dbus_message_iter_get_arg_type(&entry)
122                                                         != DBUS_TYPE_VARIANT)
123                                 break;
124                         dbus_message_iter_recurse(&entry, &value);
125                         dbus_message_iter_get_basic(&value, &identity);
126
127                 } else if (g_str_equal(key, "Passphrase")) {
128                         dbus_message_iter_next(&entry);
129                         if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
130                                 break;
131                         dbus_message_iter_recurse(&entry, &value);
132                         dbus_message_iter_get_basic(&value, &passphrase);
133
134                 } else if (g_str_equal(key, "WPS")) {
135                         wps = TRUE;
136
137                         dbus_message_iter_next(&entry);
138                         if (dbus_message_iter_get_arg_type(&entry)
139                                                         != DBUS_TYPE_VARIANT)
140                                 break;
141                         dbus_message_iter_recurse(&entry, &value);
142                         dbus_message_iter_get_basic(&value, &wpspin);
143                         break;
144                 }
145                 dbus_message_iter_next(&dict);
146         }
147
148         if (wps == TRUE) {
149                 struct connman_network *network;
150
151                 network = __connman_service_get_network(
152                                                 passphrase_reply->service);
153                 if (network == NULL)
154                         goto done;
155
156                 connman_network_set_bool(network, "WiFi.UseWPS", wps);
157
158                 if (wpspin != NULL && strlen(wpspin) > 0)
159                         connman_network_set_string(network,
160                                                 "WiFi.PinWPS", wpspin);
161                 else
162                         connman_network_set_string(network,
163                                                 "WiFi.PinWPS", NULL);
164         }
165
166 done:
167         passphrase_reply->callback(passphrase_reply->service, identity,
168                                 passphrase, passphrase_reply->user_data);
169         connman_service_unref(passphrase_reply->service);
170         dbus_message_unref(reply);
171         g_free(passphrase_reply);
172 }
173
174 static void request_input_append_alternates(DBusMessageIter *iter,
175                                                         void *user_data)
176 {
177         const char *str = user_data;
178         char **alternates, **alternative;
179
180         if (str == NULL)
181                 return;
182
183         alternates = g_strsplit(str, ",", 0);
184         if (alternates == NULL)
185                 return;
186
187         for (alternative = alternates; *alternative != NULL; alternative++)
188                 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
189                                                                 alternative);
190
191         g_strfreev(alternates);
192 }
193
194 static void request_input_append_identity(DBusMessageIter *iter,
195                                                         void *user_data)
196 {
197         char *str = "string";
198
199         connman_dbus_dict_append_basic(iter, "Type",
200                                 DBUS_TYPE_STRING, &str);
201         str = "Mandatory";
202         connman_dbus_dict_append_basic(iter, "Requirement",
203                                 DBUS_TYPE_STRING, &str);
204 }
205
206 static void request_input_append_passphrase(DBusMessageIter *iter,
207                                                         void *user_data)
208 {
209         struct connman_service *service = user_data;
210         char *value;
211         const char *phase2;
212
213         switch (__connman_service_get_security(service)) {
214         case CONNMAN_SERVICE_SECURITY_WEP:
215                 value = "wep";
216                 break;
217         case CONNMAN_SERVICE_SECURITY_PSK:
218                 value = "psk";
219                 break;
220         case CONNMAN_SERVICE_SECURITY_8021X:
221                 phase2 = __connman_service_get_phase2(service);
222
223                 if (phase2 != NULL && (
224                                 g_str_has_suffix(phase2, "GTC") == TRUE ||
225                                 g_str_has_suffix(phase2, "OTP") == TRUE))
226                         value = "response";
227                 else
228                         value = "passphrase";
229
230                 break;
231         default:
232                 value = "string";
233                 break;
234         }
235         connman_dbus_dict_append_basic(iter, "Type",
236                                 DBUS_TYPE_STRING, &value);
237         value = "Mandatory";
238         connman_dbus_dict_append_basic(iter, "Requirement",
239                                 DBUS_TYPE_STRING, &value);
240
241         if (__connman_service_wps_enabled(service) == TRUE) {
242                 connman_dbus_dict_append_array(iter, "Alternates",
243                                         DBUS_TYPE_STRING,
244                                         request_input_append_alternates,
245                                         "WPS");
246         }
247 }
248
249 static void request_input_append_wps(DBusMessageIter *iter, void *user_data)
250 {
251         const char *str = "wpspin";
252
253         connman_dbus_dict_append_basic(iter, "Type",
254                                 DBUS_TYPE_STRING, &str);
255         str = "alternate";
256         connman_dbus_dict_append_basic(iter, "Requirement",
257                                 DBUS_TYPE_STRING, &str);
258 }
259
260 static void request_input_append_password(DBusMessageIter *iter,
261                                                         void *user_data)
262 {
263         char *str = "passphrase";
264
265         connman_dbus_dict_append_basic(iter, "Type",
266                                 DBUS_TYPE_STRING, &str);
267         str = "Mandatory";
268         connman_dbus_dict_append_basic(iter, "Requirement",
269                                 DBUS_TYPE_STRING, &str);
270 }
271
272 static void request_input_login_reply(DBusPendingCall *call, void *user_data)
273 {
274         struct request_input_reply *username_password_reply = user_data;
275         char *username = NULL;
276         char *password = NULL;
277         char *key;
278         DBusMessageIter iter, dict;
279         DBusMessage *reply = dbus_pending_call_steal_reply(call);
280
281         if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR)
282                 goto done;
283
284         dbus_message_iter_init(reply, &iter);
285         dbus_message_iter_recurse(&iter, &dict);
286         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
287                 DBusMessageIter entry, value;
288
289                 dbus_message_iter_recurse(&dict, &entry);
290                 if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
291                         break;
292
293                 dbus_message_iter_get_basic(&entry, &key);
294
295                 if (g_str_equal(key, "Username")) {
296                         dbus_message_iter_next(&entry);
297                         if (dbus_message_iter_get_arg_type(&entry)
298                                                         != DBUS_TYPE_VARIANT)
299                                 break;
300                         dbus_message_iter_recurse(&entry, &value);
301                         dbus_message_iter_get_basic(&value, &username);
302
303                 } else if (g_str_equal(key, "Password")) {
304                         dbus_message_iter_next(&entry);
305                         if (dbus_message_iter_get_arg_type(&entry) !=
306                                                         DBUS_TYPE_VARIANT)
307                                 break;
308                         dbus_message_iter_recurse(&entry, &value);
309                         dbus_message_iter_get_basic(&value, &password);
310                 }
311
312                 dbus_message_iter_next(&dict);
313         }
314
315 done:
316         username_password_reply->callback(username_password_reply->service,
317                                         username, password,
318                                         username_password_reply->user_data);
319         connman_service_unref(username_password_reply->service);
320         dbus_message_unref(reply);
321         g_free(username_password_reply);
322 }
323
324 int __connman_agent_request_passphrase_input(struct connman_service *service,
325                                 authentication_cb_t callback, void *user_data)
326 {
327         DBusMessage *message;
328         const char *path;
329         DBusMessageIter iter;
330         DBusMessageIter dict;
331         DBusPendingCall *call;
332         struct request_input_reply *passphrase_reply;
333
334         if (service == NULL || agent_path == NULL || callback == NULL)
335                 return -ESRCH;
336
337         message = dbus_message_new_method_call(agent_sender, agent_path,
338                                         CONNMAN_AGENT_INTERFACE,
339                                         "RequestInput");
340         if (message == NULL)
341                 return -ENOMEM;
342
343         dbus_message_iter_init_append(message, &iter);
344
345         path = __connman_service_get_path(service);
346         dbus_message_iter_append_basic(&iter,
347                                 DBUS_TYPE_OBJECT_PATH, &path);
348
349         connman_dbus_dict_open(&iter, &dict);
350
351         if (__connman_service_get_security(service) ==
352                         CONNMAN_SERVICE_SECURITY_8021X) {
353                 connman_dbus_dict_append_dict(&dict, "Identity",
354                                         request_input_append_identity, service);
355         }
356
357         connman_dbus_dict_append_dict(&dict, "Passphrase",
358                                 request_input_append_passphrase, service);
359
360         if (__connman_service_wps_enabled(service) == TRUE) {
361             connman_dbus_dict_append_dict(&dict, "WPS",
362                                 request_input_append_wps, NULL);
363         }
364
365         connman_dbus_dict_close(&iter, &dict);
366
367         passphrase_reply = g_try_new0(struct request_input_reply, 1);
368         if (passphrase_reply == NULL) {
369                 dbus_message_unref(message);
370                 return -ENOMEM;
371         }
372
373         if (dbus_connection_send_with_reply(connection, message,
374                                                 &call, -1) == FALSE) {
375                 dbus_message_unref(message);
376                 g_free(passphrase_reply);
377                 return -ESRCH;
378         }
379
380         if (call == NULL) {
381                 dbus_message_unref(message);
382                 g_free(passphrase_reply);
383                 return -ESRCH;
384         }
385
386         passphrase_reply->service = connman_service_ref(service);
387         passphrase_reply->callback = callback;
388         passphrase_reply->user_data = user_data;
389
390         dbus_pending_call_set_notify(call, request_input_passphrase_reply,
391                                 passphrase_reply, NULL);
392
393         dbus_message_unref(message);
394
395         return -EIO;
396 }
397
398 int __connman_agent_request_login_input(struct connman_service *service,
399                                 authentication_cb_t callback, void *user_data)
400 {
401         DBusMessage *message;
402         const char *path;
403         DBusMessageIter iter;
404         DBusMessageIter dict;
405         DBusPendingCall *call;
406         struct request_input_reply *username_password_reply;
407
408         if (service == NULL || agent_path == NULL || callback == NULL)
409                 return -ESRCH;
410
411         message = dbus_message_new_method_call(agent_sender, agent_path,
412                                         CONNMAN_AGENT_INTERFACE,
413                                         "RequestInput");
414         if (message == NULL)
415                 return -ENOMEM;
416
417         dbus_message_iter_init_append(message, &iter);
418
419         path = __connman_service_get_path(service);
420         dbus_message_iter_append_basic(&iter,
421                                 DBUS_TYPE_OBJECT_PATH, &path);
422
423         connman_dbus_dict_open(&iter, &dict);
424
425         connman_dbus_dict_append_dict(&dict, "Username",
426                                 request_input_append_identity, service);
427
428         connman_dbus_dict_append_dict(&dict, "Password",
429                                 request_input_append_password, service);
430
431         connman_dbus_dict_close(&iter, &dict);
432
433         username_password_reply = g_try_new0(struct request_input_reply, 1);
434         if (username_password_reply == NULL) {
435                 dbus_message_unref(message);
436                 return -ENOMEM;
437         }
438
439         if (dbus_connection_send_with_reply(connection, message,
440                                                         &call, -1) == FALSE) {
441                 dbus_message_unref(message);
442                 g_free(username_password_reply);
443                 return -ESRCH;
444         }
445
446         if (call == NULL) {
447                 dbus_message_unref(message);
448                 g_free(username_password_reply);
449                 return -ESRCH;
450         }
451
452         username_password_reply->service = connman_service_ref(service);
453         username_password_reply->callback = callback;
454         username_password_reply->user_data = user_data;
455
456         dbus_pending_call_set_notify(call, request_input_login_reply,
457                                                 username_password_reply, NULL);
458
459         dbus_message_unref(message);
460
461         return -EIO;
462 }
463
464 struct report_error_data {
465         struct connman_service *service;
466         report_error_cb_t callback;
467         void *user_data;
468 };
469
470 static void report_error_reply(DBusPendingCall *call, void *user_data)
471 {
472         struct report_error_data *report_error = user_data;
473         DBusMessage *reply = dbus_pending_call_steal_reply(call);
474         gboolean retry = FALSE;
475         const char *dbus_err;
476
477         if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
478                 dbus_err = dbus_message_get_error_name(reply);
479                 if (dbus_err != NULL &&
480                         strcmp(dbus_err,
481                                 CONNMAN_AGENT_INTERFACE ".Error.Retry") == 0)
482                         retry = TRUE;
483         }
484
485         report_error->callback(report_error->service, retry,
486                         report_error->user_data);
487         connman_service_unref(report_error->service);
488         g_free(report_error);
489         dbus_message_unref(reply);
490 }
491
492 int __connman_agent_report_error(struct connman_service *service,
493                                 const char *error,
494                                 report_error_cb_t callback, void *user_data)
495 {
496         DBusMessage *message;
497         DBusMessageIter iter;
498         const char *path;
499         struct report_error_data *report_error;
500         DBusPendingCall *call;
501
502         if (service == NULL || agent_path == NULL || error == NULL ||
503                 callback == NULL)
504                 return -ESRCH;
505
506         message = dbus_message_new_method_call(agent_sender, agent_path,
507                                         CONNMAN_AGENT_INTERFACE,
508                                         "ReportError");
509         if (message == NULL)
510                 return -ENOMEM;
511
512         dbus_message_iter_init_append(message, &iter);
513
514         path = __connman_service_get_path(service);
515         dbus_message_iter_append_basic(&iter,
516                                 DBUS_TYPE_OBJECT_PATH, &path);
517         dbus_message_iter_append_basic(&iter,
518                                 DBUS_TYPE_STRING, &error);
519
520         report_error = g_try_new0(struct report_error_data, 1);
521         if (report_error == NULL) {
522                 dbus_message_unref(message);
523                 return -ENOMEM;
524         }
525
526         if (dbus_connection_send_with_reply(connection, message,
527                                                 &call, -1) == FALSE) {
528                 dbus_message_unref(message);
529                 g_free(report_error);
530                 return -ESRCH;
531         }
532
533         if (call == NULL) {
534                 dbus_message_unref(message);
535                 g_free(report_error);
536                 return -ESRCH;
537         }
538
539         report_error->service = connman_service_ref(service);
540         report_error->callback = callback;
541         report_error->user_data = user_data;
542         dbus_pending_call_set_notify(call, report_error_reply,
543                                 report_error, NULL);
544         dbus_message_unref(message);
545
546         return -EIO;
547 }
548
549 int __connman_agent_init(void)
550 {
551         DBG("");
552
553         connection = connman_dbus_get_connection();
554         if (connection == NULL)
555                 return -1;
556
557         return 0;
558 }
559
560 void __connman_agent_cleanup(void)
561 {
562         DBusMessage *message;
563
564         DBG("");
565
566         if (connection == NULL)
567                 return;
568
569         if (agent_watch > 0)
570                 g_dbus_remove_watch(connection, agent_watch);
571
572         if (agent_path == NULL)
573                 return;
574
575         message = dbus_message_new_method_call(agent_sender, agent_path,
576                                         CONNMAN_AGENT_INTERFACE, "Release");
577         if (message == NULL)
578                 return;
579
580         dbus_message_set_no_reply(message, TRUE);
581
582         g_dbus_send_message(connection, message);
583
584         agent_free();
585
586         dbus_connection_unref(connection);
587 }