agent: WPS input request logic
[framework/connectivity/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         passphrase_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 *passphrase = NULL;
99         char *wpspin = NULL;
100         char *key;
101         DBusMessageIter iter, dict;
102         DBusMessage *reply = dbus_pending_call_steal_reply(call);
103
104         if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR)
105                 goto done;
106
107         dbus_message_iter_init(reply, &iter);
108         dbus_message_iter_recurse(&iter, &dict);
109         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
110                 DBusMessageIter entry, value;
111
112                 dbus_message_iter_recurse(&dict, &entry);
113                 if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
114                         break;
115
116                 dbus_message_iter_get_basic(&entry, &key);
117                 if (g_str_equal(key, "Passphrase")) {
118                         dbus_message_iter_next(&entry);
119                         if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
120                                 break;
121                         dbus_message_iter_recurse(&entry, &value);
122                         dbus_message_iter_get_basic(&value, &passphrase);
123                         break;
124                 } else if (g_str_equal(key, "WPS")) {
125                         wps = TRUE;
126
127                         dbus_message_iter_next(&entry);
128                         if (dbus_message_iter_get_arg_type(&entry)
129                                                         != DBUS_TYPE_VARIANT)
130                                 break;
131                         dbus_message_iter_recurse(&entry, &value);
132                         dbus_message_iter_get_basic(&value, &wpspin);
133                         break;
134                 }
135                 dbus_message_iter_next(&dict);
136         }
137
138         if (wps == TRUE) {
139                 struct connman_network *network;
140
141                 network = __connman_service_get_network(
142                                                 passphrase_reply->service);
143                 if (network == NULL)
144                         goto done;
145
146                 connman_network_set_bool(network, "WiFi.UseWPS", wps);
147
148                 if (wpspin != NULL && strlen(wpspin) > 0)
149                         connman_network_set_string(network,
150                                                 "WiFi.PinWPS", wpspin);
151                 else
152                         connman_network_set_string(network,
153                                                 "WiFi.PinWPS", NULL);
154         }
155
156 done:
157         passphrase_reply->callback(passphrase_reply->service,
158                                 passphrase, passphrase_reply->user_data);
159         connman_service_unref(passphrase_reply->service);
160         dbus_message_unref(reply);
161         g_free(passphrase_reply);
162 }
163
164 static void request_input_append_alternates(DBusMessageIter *iter,
165                                                         void *user_data)
166 {
167         const char *str = user_data;
168         char **alternates, **alternative;
169
170         if (str == NULL)
171                 return;
172
173         alternates = g_strsplit(str, ",", 0);
174         if (alternates == NULL)
175                 return;
176
177         for (alternative = alternates; *alternative != NULL; alternative++)
178                 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
179                                                                 alternative);
180
181         g_strfreev(alternates);
182 }
183
184 static void request_input_append_passphrase(DBusMessageIter *iter,
185                                                         void *user_data)
186 {
187         struct connman_service *service = user_data;
188         char *value;
189
190         switch (__connman_service_get_security(service)) {
191         case CONNMAN_SERVICE_SECURITY_WEP:
192                 value = "wep";
193                 break;
194         case CONNMAN_SERVICE_SECURITY_PSK:
195                 value = "psk";
196                 break;
197         default:
198                 value = "string";
199                 break;
200         }
201         connman_dbus_dict_append_basic(iter, "Type",
202                                 DBUS_TYPE_STRING, &value);
203         value = "Mandatory";
204         connman_dbus_dict_append_basic(iter, "Requirement",
205                                 DBUS_TYPE_STRING, &value);
206
207         if (__connman_service_wps_enabled(service) == TRUE) {
208                 connman_dbus_dict_append_array(iter, "Alternates",
209                                         DBUS_TYPE_STRING,
210                                         request_input_append_alternates,
211                                         "WPS");
212         }
213 }
214
215 static void request_input_append_wps(DBusMessageIter *iter, void *user_data)
216 {
217         const char *str = "wpspin";
218
219         connman_dbus_dict_append_basic(iter, "Type",
220                                 DBUS_TYPE_STRING, &str);
221         str = "alternate";
222         connman_dbus_dict_append_basic(iter, "Requirement",
223                                 DBUS_TYPE_STRING, &str);
224 }
225
226 int __connman_agent_request_input(struct connman_service *service,
227                                 passphrase_cb_t callback, void *user_data)
228 {
229         DBusMessage *message;
230         const char *path;
231         DBusMessageIter iter;
232         DBusMessageIter dict;
233         DBusPendingCall *call;
234         struct request_input_reply *passphrase_reply;
235
236         if (service == NULL || agent_path == NULL || callback == NULL)
237                 return -ESRCH;
238
239         message = dbus_message_new_method_call(agent_sender, agent_path,
240                                         CONNMAN_AGENT_INTERFACE,
241                                         "RequestInput");
242         if (message == NULL)
243                 return -ENOMEM;
244
245         dbus_message_iter_init_append(message, &iter);
246
247         path = __connman_service_get_path(service);
248         dbus_message_iter_append_basic(&iter,
249                                 DBUS_TYPE_OBJECT_PATH, &path);
250
251         connman_dbus_dict_open(&iter, &dict);
252         connman_dbus_dict_append_dict(&dict, "Passphrase",
253                                 request_input_append_passphrase, service);
254
255         if (__connman_service_wps_enabled(service) == TRUE) {
256             connman_dbus_dict_append_dict(&dict, "WPS",
257                                 request_input_append_wps, NULL);
258         }
259
260         connman_dbus_dict_close(&iter, &dict);
261
262         passphrase_reply = g_try_new0(struct request_input_reply, 1);
263         if (passphrase_reply == NULL) {
264                 dbus_message_unref(message);
265                 return -ENOMEM;
266         }
267
268         if (dbus_connection_send_with_reply(connection, message,
269                                                 &call, -1) == FALSE) {
270                 dbus_message_unref(message);
271                 g_free(passphrase_reply);
272                 return -ESRCH;
273         }
274
275         if (call == NULL) {
276                 dbus_message_unref(message);
277                 g_free(passphrase_reply);
278                 return -ESRCH;
279         }
280
281         passphrase_reply->service = connman_service_ref(service);
282         passphrase_reply->callback = callback;
283         passphrase_reply->user_data = user_data;
284
285         dbus_pending_call_set_notify(call, request_input_passphrase_reply,
286                                 passphrase_reply, NULL);
287
288         dbus_message_unref(message);
289
290         return -EIO;
291 }
292
293 struct report_error_data {
294         struct connman_service *service;
295         report_error_cb_t callback;
296         void *user_data;
297 };
298
299 static void report_error_reply(DBusPendingCall *call, void *user_data)
300 {
301         struct report_error_data *report_error = user_data;
302         DBusMessage *reply = dbus_pending_call_steal_reply(call);
303         gboolean retry = FALSE;
304         const char *dbus_err;
305
306         if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
307                 dbus_err = dbus_message_get_error_name(reply);
308                 if (dbus_err != NULL &&
309                         strcmp(dbus_err,
310                                 CONNMAN_AGENT_INTERFACE ".Error.Retry") == 0)
311                         retry = TRUE;
312         }
313
314         report_error->callback(report_error->service, retry,
315                         report_error->user_data);
316         connman_service_unref(report_error->service);
317         g_free(report_error);
318         dbus_message_unref(reply);
319 }
320
321 int __connman_agent_report_error(struct connman_service *service,
322                                 const char *error,
323                                 report_error_cb_t callback, void *user_data)
324 {
325         DBusMessage *message;
326         DBusMessageIter iter;
327         const char *path;
328         struct report_error_data *report_error;
329         DBusPendingCall *call;
330
331         if (service == NULL || agent_path == NULL || error == NULL ||
332                 callback == NULL)
333                 return -ESRCH;
334
335         message = dbus_message_new_method_call(agent_sender, agent_path,
336                                         CONNMAN_AGENT_INTERFACE,
337                                         "ReportError");
338         if (message == NULL)
339                 return -ENOMEM;
340
341         dbus_message_iter_init_append(message, &iter);
342
343         path = __connman_service_get_path(service);
344         dbus_message_iter_append_basic(&iter,
345                                 DBUS_TYPE_OBJECT_PATH, &path);
346         dbus_message_iter_append_basic(&iter,
347                                 DBUS_TYPE_STRING, &error);
348
349         report_error = g_try_new0(struct report_error_data, 1);
350         if (report_error == NULL) {
351                 dbus_message_unref(message);
352                 return -ENOMEM;
353         }
354
355         if (dbus_connection_send_with_reply(connection, message,
356                                                 &call, -1) == FALSE) {
357                 dbus_message_unref(message);
358                 g_free(report_error);
359                 return -ESRCH;
360         }
361
362         if (call == NULL) {
363                 dbus_message_unref(message);
364                 g_free(report_error);
365                 return -ESRCH;
366         }
367
368         report_error->service = connman_service_ref(service);
369         report_error->callback = callback;
370         report_error->user_data = user_data;
371         dbus_pending_call_set_notify(call, report_error_reply,
372                                 report_error, NULL);
373         dbus_message_unref(message);
374
375         return -EIO;
376 }
377
378 int __connman_agent_init(void)
379 {
380         DBG("");
381
382         connection = connman_dbus_get_connection();
383         if (connection == NULL)
384                 return -1;
385
386         return 0;
387 }
388
389 void __connman_agent_cleanup(void)
390 {
391         DBusMessage *message;
392
393         DBG("");
394
395         if (connection == NULL)
396                 return;
397
398         if (agent_watch > 0)
399                 g_dbus_remove_watch(connection, agent_watch);
400
401         if (agent_path == NULL)
402                 return;
403
404         message = dbus_message_new_method_call(agent_sender, agent_path,
405                                         CONNMAN_AGENT_INTERFACE, "Release");
406         if (message == NULL)
407                 return;
408
409         dbus_message_set_no_reply(message, TRUE);
410
411         g_dbus_send_message(connection, message);
412
413         agent_free();
414
415         dbus_connection_unref(connection);
416 }