Merge "[net-config] Refactoring code to register agent." into tizen
[platform/core/connectivity/net-config.git] / src / wifi-agent.c
1 /*
2  * Network Configuration Module
3  *
4  * Copyright (c) 2000 - 2012 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  */
19
20 #include <app.h>
21 #include <stdio.h>
22 #include <vconf.h>
23 #include <stdlib.h>
24 #include <unistd.h>
25 #include <vconf-keys.h>
26
27 #include "log.h"
28 #include "util.h"
29 #include "wifi.h"
30 #include "netdbus.h"
31 #include "wifi-agent.h"
32 #include "wifi-state.h"
33 #include "wifi-eap-config.h"
34 #include "network-state.h"
35 #include "network-accessibility.h"
36
37 #define NETCONFIG_AGENT_FIELD_NAME                              "Name"
38 #define NETCONFIG_AGENT_FIELD_SSID                              "SSID"
39 #define NETCONFIG_AGENT_FIELD_IDENTITY                  "Identity"
40 #define NETCONFIG_AGENT_FIELD_PASSPHRASE                "Passphrase"
41 #define NETCONFIG_AGENT_FIELD_WPS                               "WPS"
42 #define NETCONFIG_AGENT_FIELD_WPS_PBC                   "WPS_PBC"
43 #define NETCONFIG_AGENT_FIELD_WPS_PIN                   "WPS_PIN"
44
45 #define NETCONFIG_AGENT_ERR_CONNECT_FAILED              "connect-failed"
46
47 struct netconfig_wifi_agent {
48         GByteArray *ssid;
49         char *name;
50         char *identity;
51         char *passphrase;
52         char *wps_pin;
53         gboolean wps_pbc;
54 };
55
56 static struct netconfig_wifi_agent agent;
57
58 static void __netconfig_agent_clear_fields(void)
59 {
60         g_byte_array_free(agent.ssid, TRUE);
61         g_free(agent.name);
62         g_free(agent.identity);
63         g_free(agent.passphrase);
64         g_free(agent.wps_pin);
65
66         agent.ssid = NULL;
67         agent.name = NULL;
68         agent.identity = NULL;
69         agent.passphrase = NULL;
70         agent.wps_pin = NULL;
71         agent.wps_pbc = FALSE;
72 }
73
74 int connman_register_agent(void)
75 {
76         GVariant *reply = NULL;
77         GVariant *params = NULL;
78
79         params = g_variant_new("(o)", NETCONFIG_WIFI_PATH);
80         reply = netconfig_invoke_dbus_method(CONNMAN_SERVICE,
81                         CONNMAN_MANAGER_PATH, CONNMAN_MANAGER_INTERFACE,
82                         "RegisterAgent", params);
83
84         if (reply == NULL) {
85                         ERR("Fail to register agent");
86                         return FALSE;
87         } else
88                 g_variant_unref(reply);
89
90         INFO("Registered to connman agent successfully");
91
92         return TRUE;
93 }
94
95 int connman_unregister_agent(void)
96 {
97         gboolean reply = FALSE;
98         GVariant *param = NULL;
99         const char *path = NETCONFIG_WIFI_PATH;
100
101         param = g_variant_new("(o)", path);
102
103         DBG("ConnMan agent unregister");
104
105         reply = netconfig_invoke_dbus_method_nonblock(CONNMAN_SERVICE,
106                         CONNMAN_MANAGER_PATH, CONNMAN_MANAGER_INTERFACE,
107                         "UnregisterAgent", param, NULL);
108
109         if (reply != TRUE)
110                 ERR("Fail to unregister agent");
111
112         /* Clearing the agent fields */
113         __netconfig_agent_clear_fields();
114
115         return reply;
116 }
117
118 gboolean netconfig_wifi_set_agent_field_for_eap_network(
119                 const char *name, const char *identity, const char *passphrase)
120 {
121         int name_len;
122
123         if (name == NULL)
124                 return FALSE;
125
126         __netconfig_agent_clear_fields();
127
128         name_len = strlen(name);
129         agent.ssid = g_byte_array_sized_new(name_len);
130         agent.ssid->len = name_len;
131         memcpy(agent.ssid->data, name, name_len);
132
133         if (identity)
134                 agent.identity = g_strdup(identity);
135
136         if (passphrase)
137                 agent.passphrase = g_strdup(passphrase);
138
139         DBG("Successfully configured for EAP network");
140
141         return TRUE;
142 }
143
144 gboolean handle_set_field(NetConnmanAgent *connman_agent,
145                 GDBusMethodInvocation *context, const gchar *service, GVariant *fields)
146 {
147         GError *error = NULL;
148         GVariantIter *iter;
149         gpointer field;
150         GVariant *value;
151         gboolean updated = FALSE;
152         gboolean reply = FALSE;
153
154         g_return_val_if_fail(connman_agent != NULL, FALSE);
155
156         DBG("Set agent fields for %s", service);
157
158         if (netconfig_is_wifi_profile(service) != TRUE) {
159                 error = g_error_new(G_DBUS_ERROR,
160                                 G_DBUS_ERROR_AUTH_FAILED,
161                                 CONNMAN_ERROR_INTERFACE ".InvalidService");
162
163                 g_dbus_method_invocation_return_gerror(context, error);
164                 g_clear_error(&error);
165
166                 return reply;
167         }
168
169         __netconfig_agent_clear_fields();
170         g_variant_get(fields, "a{sv}", &iter);
171         while (g_variant_iter_loop(iter, "{sv}", &field, &value)) {
172                 if (g_strcmp0(field, NETCONFIG_AGENT_FIELD_PASSPHRASE) == 0) {
173                         if (g_variant_is_of_type(value, G_VARIANT_TYPE_STRING)) {
174                                 agent.passphrase = g_strdup(g_variant_get_string(value, NULL));
175                                 updated = TRUE;
176
177                                 DBG("Field [%s] - []", field);
178                         }
179                 } else if (g_strcmp0(field, NETCONFIG_AGENT_FIELD_WPS_PBC) == 0) {
180                         if (g_variant_is_of_type(value, G_VARIANT_TYPE_STRING) &&
181                                         g_strcmp0(g_variant_get_string(value, NULL), "enable") == 0) {
182                                 agent.wps_pbc = TRUE;
183                                 updated = TRUE;
184
185                                 DBG("Field [%s] - [%d]", field, agent.wps_pbc);
186                         }
187                 } else if (g_strcmp0(field, NETCONFIG_AGENT_FIELD_WPS_PIN) == 0) {
188                         if (g_variant_is_of_type(value, G_VARIANT_TYPE_STRING)) {
189                                 agent.wps_pin = g_strdup(g_variant_get_string(value, NULL));
190                                 updated = TRUE;
191
192                                 DBG("Field [%s] - []", field);
193                         }
194                 } else if (g_strcmp0(field, NETCONFIG_AGENT_FIELD_NAME) == 0) {
195                         if (g_variant_is_of_type(value, G_VARIANT_TYPE_STRING)) {
196                                 agent.name = g_strdup(g_variant_get_string(value, NULL));
197                                 updated = TRUE;
198
199                                 DBG("Field [%s] - []", field);
200                         }
201                 } else if (g_strcmp0(field, NETCONFIG_AGENT_FIELD_SSID) == 0) {
202                         if (agent.ssid != NULL) {
203                                 g_byte_array_free(agent.ssid, TRUE);
204                                 agent.ssid = NULL;
205                         }
206
207                         if (g_variant_is_of_type(value, G_VARIANT_TYPE_BYTESTRING)) {
208                                 guint8 char_value;
209                                 GVariantIter *iter1;
210                                 GByteArray *array = g_byte_array_new();
211
212                                 g_variant_get(value, "ay", &iter1);
213                                 while (g_variant_iter_loop(iter1, "y", &char_value))
214                                         g_byte_array_append(array, &char_value, 1);
215                                 g_variant_iter_free(iter1);
216                                 if (array != NULL && (array->len > 0)) {
217                                         agent.ssid = g_byte_array_sized_new(array->len);
218                                         agent.ssid->len = array->len;
219                                         memcpy(agent.ssid->data, array->data, array->len);
220                                         updated = TRUE;
221
222                                         DBG("Field [%s] - []", field);
223                                 }
224                         }
225                 } else if (g_strcmp0(field, NETCONFIG_AGENT_FIELD_IDENTITY) == 0) {
226                         if (g_variant_is_of_type(value, G_VARIANT_TYPE_STRING)) {
227                                 agent.identity = g_strdup(g_variant_get_string(value, NULL));
228                                 updated = TRUE;
229
230                                 DBG("Field [%s] - []", field);
231                         }
232                 }
233         }
234
235         if (updated == TRUE) {
236                 reply = netconfig_invoke_dbus_method_nonblock(CONNMAN_SERVICE,
237                                 service, CONNMAN_SERVICE_INTERFACE, "Connect",
238                                 NULL, __netconfig_wifi_connect_reply);
239                 if (reply == TRUE) {
240                         g_dbus_method_invocation_return_value(context, NULL);
241                 } else {
242                         error = g_error_new(G_DBUS_ERROR,
243                                         G_DBUS_ERROR_AUTH_FAILED,
244                                         CONNMAN_ERROR_INTERFACE ".InvalidArguments");
245
246                         g_dbus_method_invocation_return_gerror(context, error);
247                         g_clear_error(&error);
248                 }
249         } else {
250                 error = g_error_new(G_DBUS_ERROR,
251                                 G_DBUS_ERROR_AUTH_FAILED,
252                                 CONNMAN_ERROR_INTERFACE ".InvalidArguments");
253
254                 g_dbus_method_invocation_return_gerror(context, error);
255                 g_clear_error(&error);
256         }
257
258         if (reply != TRUE) {
259                 ERR("Fail to connect Wi-Fi");
260
261                 __netconfig_agent_clear_fields();
262         }
263         g_variant_iter_free(iter);
264
265         net_connman_agent_complete_set_field(connman_agent, context);
266         return reply;
267 }
268
269 gboolean handle_request_input(NetConnmanAgent *connman_agent,
270                 GDBusMethodInvocation *context, const gchar *service, GVariant *fields)
271 {
272         GVariantIter *iter;
273         gchar *field = NULL;
274         GVariant *r_value = NULL;
275         GVariant *out_table = NULL;
276         gboolean updated = FALSE;
277         GVariantBuilder *builder = NULL;
278
279         g_return_val_if_fail(connman_agent != NULL, FALSE);
280
281         if (NULL == service)
282                 return FALSE;
283
284         DBG("Agent fields requested for service: %s", service);
285
286         builder = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
287
288         g_variant_get(fields, "a{sv}", &iter);
289         while (g_variant_iter_loop(iter, "{sv}", &field, &r_value)) {
290
291                 if (g_strcmp0(field, NETCONFIG_AGENT_FIELD_PASSPHRASE) == 0 &&
292                                 agent.passphrase != NULL) {
293                         g_variant_builder_add(builder, "{sv}", NETCONFIG_AGENT_FIELD_PASSPHRASE,
294                                                         g_variant_new_string(agent.passphrase));
295
296                         updated = TRUE;
297                         DBG("Setting [%s] - []", field);
298                 } else if (g_strcmp0(field, NETCONFIG_AGENT_FIELD_WPS) == 0 &&
299                                 (agent.wps_pbc == TRUE || agent.wps_pin != NULL)) {
300                         if (agent.wps_pbc == TRUE) {
301                                 /* Sending empty string for WPS push button method */
302                                 g_variant_builder_add(builder, "{sv}", NETCONFIG_AGENT_FIELD_WPS, g_variant_new_string(""));
303
304                                 updated = TRUE;
305                                 DBG("Setting empty string for [%s]", field);
306                         } else if (agent.wps_pin != NULL) {
307                                 g_variant_builder_add(builder, "{sv}", NETCONFIG_AGENT_FIELD_WPS, g_variant_new_string(agent.wps_pin));
308
309                                 updated = TRUE;
310                                 DBG("Setting string [%s] - []", field);
311                         }
312                 } else if (g_strcmp0(field, NETCONFIG_AGENT_FIELD_NAME) == 0 &&
313                                 agent.name != NULL) {
314                         g_variant_builder_add(builder, "{sv}", NETCONFIG_AGENT_FIELD_NAME, g_variant_new_string(agent.name));
315
316                         updated = TRUE;
317                         DBG("Settings [%s] - []", field);
318                 } else if (g_strcmp0(field, NETCONFIG_AGENT_FIELD_SSID) == 0 &&
319                                 agent.ssid != NULL) {
320                         int i = 0;
321                         GVariantBuilder *builder1 = NULL;
322                         builder1 = g_variant_builder_new(G_VARIANT_TYPE("ay"));
323
324                         for (i = 0; i < (agent.ssid->len); i++)
325                                 g_variant_builder_add(builder1, "y", agent.ssid->data[i]);
326
327                         g_variant_builder_add(builder, "{sv}", NETCONFIG_AGENT_FIELD_SSID, g_variant_builder_end(builder1));
328                         if (builder1 != NULL)
329                                 g_variant_builder_unref(builder1);
330
331                         updated = TRUE;
332                         DBG("Settings [%s] - []", field);
333                 } else if (g_strcmp0(field, NETCONFIG_AGENT_FIELD_IDENTITY) == 0 &&
334                                 agent.identity != NULL) {
335                         g_variant_builder_add(builder, "{sv}", NETCONFIG_AGENT_FIELD_IDENTITY, g_variant_new_string(agent.identity));
336
337                         updated = TRUE;
338                         DBG("Settings [%s] - []", field);
339                 }
340         }
341
342         out_table = g_variant_new("(@a{sv})", g_variant_builder_end(builder));
343
344         if (builder)
345                 g_variant_builder_unref(builder);
346
347         g_variant_iter_free(iter);
348
349
350         if (NULL == out_table) {
351                 net_connman_agent_complete_request_input(connman_agent, context, out_table);
352
353                 return FALSE;
354         }
355
356         if (updated == TRUE)
357                 g_dbus_method_invocation_return_value(context, out_table);
358         else {
359                 GError *error = NULL;
360                 error = g_error_new(G_DBUS_ERROR,
361                                 G_DBUS_ERROR_AUTH_FAILED,
362                                 "net.connman.Agent.Error.Canceled");
363
364                 g_dbus_method_invocation_return_gerror(context, error);
365                 g_clear_error(&error);
366         }
367
368         __netconfig_agent_clear_fields();
369         g_variant_unref(out_table);
370
371         return updated;
372 }
373
374
375 gboolean handle_report_error(NetConnmanAgent *connman_agent,
376                 GDBusMethodInvocation *context, const gchar *service, const gchar *error)
377 {
378         gboolean ret = TRUE;
379
380         g_return_val_if_fail(connman_agent != NULL, FALSE);
381
382         net_connman_agent_complete_report_error(connman_agent, context);
383         DBG("Agent error for service[%s] - [%s]", service, error);
384
385         /* Do something when it failed to make a connection */
386
387         return ret;
388 }
389
390 #if defined TIZEN_CAPTIVE_PORTAL
391 #if defined TIZEN_WEARABLE
392 #define QUERY_FOR_INTERNET_INTERVAL                     2
393 #define TIMER_THRESHOLD                                         4
394 #else
395 #define QUERY_FOR_INTERNET_INTERVAL                     20
396 #define TIMER_THRESHOLD                                         120
397 #endif
398
399 static gboolean is_monitor_notifier_registered = FALSE;
400
401 #if defined TIZEN_WEARABLE
402 static gboolean is_portal_msg_shown = FALSE;
403 static guint portal_msg_timer = 0;
404 #endif
405
406 struct poll_timer_data {
407         guint time_elapsed;
408         guint timer_id;
409         void* data;
410 };
411
412 static struct poll_timer_data timer_data = {
413                 QUERY_FOR_INTERNET_INTERVAL, 0, NULL};
414
415 static gboolean __check_ignore_portal_list(const char * ssid)
416 {
417         char def_str[1024];
418         int i = 0;
419         int ignore_ap_count = 0;
420
421         if (ssid == NULL)
422                 return FALSE;
423
424         DBG("checking ssid [%s]", ssid);
425
426         DBG("csc string [%s]", def_str);
427         gchar ** ignore_ap_list = g_strsplit(def_str, ",", 0);
428         ignore_ap_count = g_strv_length(ignore_ap_list);
429         for (i = 0; i < ignore_ap_count; i++) {
430                 DBG("[%d] - [%s]", i, ignore_ap_list[i]);
431                 if (strncmp(ignore_ap_list[i], ssid, strlen(ssid)) == 0) {
432                         g_strfreev(ignore_ap_list);
433                         return TRUE;
434                 }
435         }
436
437         g_strfreev(ignore_ap_list);
438         return FALSE;
439 }
440
441 static void __wifi_state_monitor(wifi_service_state_e state,
442                 void *user_data);
443
444 static wifi_state_notifier wifi_state_monitor_notifier = {
445                 .wifi_state_changed = __wifi_state_monitor,
446                 .user_data = NULL,
447 };
448
449 static void __wifi_state_monitor(wifi_service_state_e state,
450                 void *user_data)
451 {
452         DBG("Wi-Fi state: %x", state);
453
454         if (state == NETCONFIG_WIFI_CONNECTED)
455                 return;
456
457         if (is_monitor_notifier_registered == TRUE) {
458                 wifi_state_notifier_unregister(&wifi_state_monitor_notifier);
459                 is_monitor_notifier_registered = FALSE;
460         }
461
462 #if defined TIZEN_WEARABLE
463         is_portal_msg_shown = FALSE;
464 #endif
465
466         /* suspend if Internet check activity in progress */
467         if (timer_data.timer_id == 0)
468                 return;
469
470         netconfig_stop_timer(&timer_data.timer_id);
471         netconfig_stop_internet_check();
472
473         DBG("Stopped Internet accessibility check");
474 }
475
476 static gboolean __netconfig_wifi_portal_login_timeout(gpointer data)
477 {
478         char *service_profile = NULL;
479         GVariant *reply = NULL;
480
481         DBG("");
482
483         struct poll_timer_data *timer = (struct poll_timer_data *)data;
484         if (timer == NULL)
485                 return FALSE;
486
487         if (TRUE == netconfig_get_internet_status()) {
488                 if (is_monitor_notifier_registered == TRUE) {
489                         wifi_state_notifier_unregister(&wifi_state_monitor_notifier);
490                         is_monitor_notifier_registered = FALSE;
491                 }
492
493                 DBG("Portal logged in successfully and update ConnMan state");
494                 return FALSE; /* to stop the timer */
495         } else {
496                 if (timer->time_elapsed >= TIMER_THRESHOLD) {
497                         DBG("Login failed, update ConnMan");
498
499                         if (is_monitor_notifier_registered == TRUE) {
500                                 wifi_state_notifier_unregister(&wifi_state_monitor_notifier);
501                                 is_monitor_notifier_registered = FALSE;
502                         }
503
504                         /* Disconnect and forget the AP */
505                         service_profile = (char*) netconfig_get_default_profile();
506                         if (service_profile && netconfig_is_wifi_profile(service_profile)) {
507                                 /* Now forget the AP*/
508                                 reply = netconfig_invoke_dbus_method(CONNMAN_SERVICE,
509                                                 service_profile, CONNMAN_SERVICE_INTERFACE, "Remove",
510                                                 NULL);
511
512                                 if (reply != NULL)
513                                         g_variant_unref(reply);
514                                 else
515                                         ERR("Failed to forget the AP ");
516                         }
517                 } else {
518                         if (NETCONFIG_WIFI_CONNECTED ==
519                                         wifi_state_get_service_state()) {
520                                 /* check Internet availability by sending and receiving data*/
521                                 netconfig_check_internet_accessibility();
522                                 /* Returning TRUE itself is enough to restart the timer */
523                                 timer->time_elapsed = timer->time_elapsed +
524                                                                         QUERY_FOR_INTERNET_INTERVAL;
525                                 return TRUE;
526                         }
527                 }
528         }
529
530         return FALSE;
531 }
532
533 #if defined TIZEN_WEARABLE
534 static gboolean __netconfig_display_portal_msg(gpointer data)
535 {
536         DBG("");
537         wc_launch_popup(WC_POPUP_TYPE_CAPTIVE_PORTAL);
538
539         netconfig_stop_timer(&portal_msg_timer);
540
541         return FALSE;
542 }
543 #endif
544
545 static void __netconfig_wifi_portal_login_timer_start(struct poll_timer_data
546                 *data)
547 {
548         DBG("__netconfig_wifi_browser_start_timer...starting timer");
549
550         if (data == NULL)
551                 return;
552
553         netconfig_stop_timer(&(data->timer_id));
554
555         /* Timer logic: After successful launch of browser, we would check for
556          * Internet status for every 20s until a threshold of 120s
557          */
558
559         data->time_elapsed = QUERY_FOR_INTERNET_INTERVAL;
560         netconfig_start_timer_seconds(QUERY_FOR_INTERNET_INTERVAL,
561                 __netconfig_wifi_portal_login_timeout, data, &(data->timer_id));
562 }
563 #endif
564
565 gboolean handle_request_browser(NetConnmanAgent *connman_agent,
566                 GDBusMethodInvocation *context, const gchar *service, const gchar *url)
567 {
568 #if defined TIZEN_CAPTIVE_PORTAL
569         gboolean ret = FALSE;
570         gboolean ignore_portal = FALSE;
571         const char * ssid = NULL;
572
573         g_return_val_if_fail(connman_agent != NULL, FALSE);
574
575         DBG("service[%s] - url[%s]", service, url);
576
577         ssid = netconfig_wifi_get_connected_essid(netconfig_get_default_profile());
578         if (ssid == NULL) {
579                 ERR("Connected AP name is NULL!!");
580                 net_connman_agent_complete_request_browser(connman_agent, context);
581                 return FALSE;
582         }
583
584         ignore_portal = __check_ignore_portal_list(ssid);
585
586         if (ignore_portal == TRUE) {
587                 net_connman_agent_complete_request_browser(connman_agent, context);
588                 return TRUE;
589         }
590         /* Register for Wifi state change notifier*/
591         if (is_monitor_notifier_registered == FALSE) {
592                 wifi_state_notifier_register(&wifi_state_monitor_notifier);
593                 is_monitor_notifier_registered = TRUE;
594         }
595
596 #if defined TIZEN_WEARABLE
597         if (is_portal_msg_shown) {
598                 net_connman_agent_complete_request_browser(connman_agent, context);
599                 return TRUE;
600         }
601
602         is_portal_msg_shown = TRUE;
603         netconfig_start_timer_seconds(4, __netconfig_display_portal_msg, NULL, &portal_msg_timer);
604 #else
605         ret = netconfig_send_notification_to_net_popup(NETCONFIG_ADD_PORTAL_NOTI, ssid);
606 #endif
607
608         timer_data.time_elapsed = 0;
609         __netconfig_wifi_portal_login_timer_start(&timer_data);
610
611         net_connman_agent_complete_request_browser(connman_agent, context);
612         return ret;
613 #else
614         GError *error = NULL;
615         error = g_error_new(G_DBUS_ERROR,
616                         G_DBUS_ERROR_AUTH_FAILED,
617                         CONNMAN_ERROR_INTERFACE ".NotSupported");
618
619         g_dbus_method_invocation_return_gerror(context, error);
620         g_clear_error(&error);
621
622         return FALSE;
623 #endif
624 }