07619480c622c47de2f96adb70c06a886c7f3a7c
[libicd-wpa] / networks.c
1 /**
2   @file networks.c
3
4   Copyright (C) 2009 Javier S. Pedro
5
6   @author Javier S. Pedro <javispedro@javispedro.com>
7
8   This file is part of libicd-network-wpa.
9
10   This program is free software; you can redistribute it and/or modify it
11   under the terms of the GNU General Public License as published by the
12   Free Software Foundation; either version 2 of the License, or (at your
13   option) any later version.
14
15   This program is distributed in the hope that it will be useful, but
16   WITHOUT ANY WARRANTY; without even the implied warranty of
17   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18   General Public License for more details.
19
20   You should have received a copy of the GNU General Public License along
21   with this program; if not, write to the Free Software Foundation, Inc.,
22   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23
24 */
25
26 #include <libgen.h>
27 #include <string.h>
28
29 #include <glib.h>
30 #include <gconf/gconf-client.h>
31
32 #include <icd/osso-ic-gconf.h>
33 #include <icd/osso-ic-dbus.h>
34
35 #include "common.h"
36 #include "log.h"
37 #include "wlan.h"
38 #include "networks.h"
39 #include "gconf.h"
40
41 #define FAILED_SCAN_RETRY_WAIT 3 * 1000
42 #define FAILED_SCAN_RETRY_TRIES 3
43
44 #define GCONF_NETWORK_TYPE "/type"
45
46 static GSList *net_list = NULL;
47
48 int networks_initialize()
49 {
50         DLOG_DEBUG(__func__);
51         GConfClient *client = gconf_client_get_default();
52         GError *error = NULL;
53         
54         net_list = NULL;
55         
56         if (!client) {
57                 DLOG_ERR("Cannot get gconf client");
58                 return -1;
59         }
60         
61         net_list = gconf_client_all_dirs(client, ICD_GCONF_PATH, &error);
62         if (error) {
63                 DLOG_ERR("Could not get setting:%s, error:%s", ICD_GCONF_PATH, 
64                          error->message);
65                 g_clear_error(&error);
66                 g_object_unref(client);
67                 return -1;
68         }
69         
70         // Now, filter networks
71         GSList *prev = NULL;
72         GSList *cur = net_list;
73         while (cur) {
74                 gchar* path = g_strconcat(cur->data, WPA_GCONF_NETWORK_TYPE, NULL);
75                 gchar* type = gconf_client_get_string(client, path, &error);
76                 
77                 g_free(path);
78                 
79                 if (error) {
80                         DLOG_ERR("Could not get setting:%s, error:%s", path, 
81                                  error->message);
82                         g_clear_error(&error);
83                         
84                         goto initialize_error;
85                 } else if (!type) {
86                         DLOG_ERR("Could not get setting:%s", path);
87
88                         goto initialize_error;
89                 }
90                 
91                 if (strcmp(type, WPA_GCONF_NETWORK_TYPE_VALUE) != 0) {
92                         // Remove this network from the list
93                         DLOG_DEBUG("Ignoring %s from IAP (type is %s)", 
94                                 (gchar*)cur->data, type);
95                         g_free(cur->data);
96                         if (prev) {
97                                 GSList *del = cur;
98                                 cur = g_slist_next(cur);
99                                 g_slist_free1(del);
100                                 prev->next = cur;
101                         } else {
102                                 net_list = g_slist_next(cur);
103                                 g_slist_free1(cur);
104                                 cur = net_list;
105                         }
106                 } else {
107                         DLOG_DEBUG("Added network %s from IAP", (gchar*)cur->data);
108                         
109                         prev = cur;
110                         cur = g_slist_next(cur);
111                 }
112                 
113                 g_free(type);
114         }
115         
116         return 0;
117         
118 initialize_error:
119         g_object_unref(client);
120         networks_free();
121         
122         return -1;
123 }
124
125 static void free_list_item(gpointer data, gpointer user_data)
126 {
127         g_free(data);
128 }
129
130 void networks_free()
131 {
132         DLOG_DEBUG(__func__);
133         if (!net_list) return;
134         
135         g_slist_foreach(net_list, free_list_item, NULL);
136         
137         g_slist_free(net_list);
138         net_list = NULL;
139 }
140
141
142 /* Scanning networks */
143
144 static GSList *scan_cur_net = NULL;
145 static gchar *scan_cur_ssid = NULL;
146 static int scan_tries = 0;
147 static guint scan_retry_timer = 0;
148 static networks_search_found scan_found_cb = NULL;
149 static gpointer scan_found_cb_data = NULL;
150
151 static void networks_search_iteration();
152
153 static void networks_search_finished()
154 {
155         DLOG_DEBUG(__func__);
156         
157         scan_found_cb(SEARCH_FINISHED, NULL,
158                 NULL, NULL, 0, scan_found_cb_data);
159         scan_cur_net = NULL;
160                 
161         scan_found_cb = NULL;
162         scan_found_cb_data = NULL;
163         // Usually a new network_search will not happen on the FINISED cb,
164         //  but if you do, you'll have to take care of the pseudorace here.
165         
166         networks_search_stop();
167 }
168
169 static void networks_search_scan_ap_found(int status,
170         const char * ssid, const char * ap, int dB)
171 {
172         DLOG_DEBUG(__func__);
173         
174         if (!scan_cur_net) return;
175         if (scan_retry_timer) return; // Interface was already scanning
176
177         if (status == SEARCH_FINISHED) {
178                 DLOG_DEBUG("Searching for %s done, iterating", scan_cur_ssid);
179                 // Go to next network
180                 scan_cur_net = g_slist_next(scan_cur_net);
181                 if (scan_cur_net) {
182                         g_free(scan_cur_ssid);
183                         scan_cur_ssid = NULL;
184                         scan_tries = 0;
185                         networks_search_iteration();
186                 } else {
187                         // No more networks to search
188                         networks_search_finished();
189                 }
190         } else if (status == SEARCH_CONTINUE) {
191                 DLOG_DEBUG("Found ssid %s", ssid);
192                 if (strcmp(ssid, scan_cur_ssid) == 0) 
193                 {
194                         // This is our man                      
195                         gchar *full_name = g_strdup(scan_cur_net->data);
196                         
197                         gchar *base_name = g_strdup(basename(full_name));
198                         
199                         DLOG_INFO("%s (%s) was found",
200                                 base_name, ssid);
201                         
202                         scan_found_cb(SEARCH_CONTINUE, base_name,
203                                 ssid, ap, dB, scan_found_cb_data);
204                                 
205                         g_free(base_name);
206                         g_free(full_name);
207                 }
208         }
209 }
210
211 static gboolean networks_search_retry(gpointer data)
212 {
213         DLOG_DEBUG(__func__);
214         
215         scan_retry_timer = 0;
216         if (scan_cur_net) networks_search_iteration();
217         // Always disable this timeout, 
218         //  _search_iteration() will put a newer one if needed
219         return FALSE;
220 }
221
222 static void networks_search_iteration()
223 {
224         DLOG_DEBUG(__func__);
225         
226         GConfClient *client = gconf_client_get_default();
227         GError *error = NULL;
228         
229         if (!client) {
230                 DLOG_ERR("Cannot get gconf client");
231                 goto get_gconf_error;
232         }
233         
234         DLOG_DEBUG("Test if %s is active", (gchar*)scan_cur_net->data);
235         
236         gchar *path = g_strconcat(scan_cur_net->data, WPA_GCONF_SSID, NULL);
237         scan_cur_ssid = gconf_client_get_string(client, path, &error);
238         
239         g_object_unref(client);
240         
241         if (error) {
242                 DLOG_ERR("Could not get setting:%s, error:%s", path, 
243                          error->message);
244                 g_clear_error(&error);
245                 
246                 goto get_ssid_error;
247         }
248         else if (!scan_cur_ssid) {
249                 DLOG_ERR("Could not get setting:%s", path);
250
251                 goto get_ssid_error;
252         }
253         
254         scan_retry_timer = 0;
255         
256         int result = wlan_scan(scan_cur_ssid, networks_search_scan_ap_found);
257                  
258         if (result != 0) {
259                 // Something went wront scanning this network, set timeout and 
260                 // try again
261                 scan_tries++;
262                 if (scan_tries >= FAILED_SCAN_RETRY_TRIES) {
263                         // Give up
264                         goto scan_error;
265                 } else {
266                         scan_retry_timer = g_timeout_add(FAILED_SCAN_RETRY_WAIT,
267                                                         networks_search_retry,
268                                                         NULL);
269                 }
270         }
271         
272         return;
273 get_ssid_error:
274         g_free(path);
275         
276 get_gconf_error:
277 scan_error:
278         networks_search_finished();
279 }
280
281 void networks_search_start(networks_search_found found_cb, gpointer user_data)
282 {
283         DLOG_DEBUG(__func__);
284
285         if (scan_cur_net)
286                 return; // Already active
287         
288         scan_cur_net = net_list;
289         scan_tries = 0;
290         scan_found_cb = found_cb;
291         scan_found_cb_data = user_data;
292         
293         if (scan_cur_net)
294                 networks_search_iteration();
295         else // No networks to scan?
296                 networks_search_finished();
297 }
298
299 void networks_search_stop()
300 {
301         DLOG_DEBUG(__func__);
302         
303         if (scan_found_cb) 
304                 scan_found_cb(SEARCH_STOPPED,
305                                  NULL, NULL, NULL, 0, scan_found_cb_data);
306         scan_cur_net = NULL;
307         
308         scan_found_cb = NULL;
309         scan_found_cb_data = NULL;
310
311         g_free(scan_cur_ssid);
312         scan_cur_ssid = NULL;
313         
314         DLOG_DEBUG("Search stopped");
315 }
316
317 /* Connecting to networks */
318
319 static networks_connect_result connect_result_cb = NULL;
320 static gpointer connect_result_cb_data = NULL;
321
322 static void networks_connected(int status, const char *error)
323 {
324         DLOG_DEBUG("%s: %d", __func__, status);
325         
326         if (!connect_result_cb) return;
327         
328         if (status) {
329                 connect_result_cb(status, ICD_DBUS_ERROR_NETWORK_ERROR, 
330                         connect_result_cb_data);
331         } else {
332                 connect_result_cb(0, NULL, 
333                         connect_result_cb_data);
334         }
335         
336         connect_result_cb = NULL;
337 }
338
339 void networks_connect(const char * id,
340         networks_connect_result result_cb, gpointer user_data)
341 {
342         DLOG_DEBUG("%s: %s", __func__, id);
343         GConfClient *client = gconf_client_get_default();
344         
345         connect_result_cb = result_cb;
346         connect_result_cb_data = user_data;
347         
348         gchar *net = g_strconcat(ICD_GCONF_PATH, "/", id, NULL);
349         gchar *path, *value;
350         
351         // Get ICD network type
352         path = g_strconcat(net, WPA_GCONF_NETWORK_TYPE, NULL);
353         value = gconf_get_string(client, path);
354         
355         if (!value || strcmp(value, WPA_GCONF_NETWORK_TYPE_VALUE) != 0) {
356                 result_cb(-1, ICD_DBUS_ERROR_INVALID_IAP, user_data);
357                 goto connect_error;
358         }
359
360         g_free(value);
361         
362         path = g_strconcat(net, WPA_GCONF_SSID, NULL);
363         value = gconf_get_string(client, path);
364         
365         if (!value) {
366                 result_cb(-1, ICD_DBUS_ERROR_INVALID_IAP, user_data);
367                 goto connect_error;
368         }
369         
370         wlan_connect(value, networks_connected);
371         
372 connect_error:
373         if (value) g_free(value);
374 }
375
376 void networks_disconnect(const char * id)
377 {
378         wlan_disconnect();
379 }
380
381 /* -- STATUS -- */
382
383 static networks_status_result status_result_cb = NULL;
384 static gpointer status_result_cb_data = NULL;
385
386 static void networks_status_reply(int status,
387          const char * essid, int essid_len,
388          const char * bssid, int bssid_len,
389          int qual, int channel, unsigned long security, unsigned long capability, 
390          const char * data)
391 {
392         if (!status_result_cb) return;
393         
394         if (status == 0) {
395                 status_result_cb(status, essid, qual, status_result_cb_data);
396         } else {
397                 status_result_cb(status, data, 0, status_result_cb_data);
398         }
399 }
400
401 void networks_status(networks_status_result result_cb, gpointer user_data)
402 {
403         status_result_cb = result_cb;
404         status_result_cb_data = user_data;
405         
406         wlan_get_status(networks_status_reply);
407 }
408