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