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