Ensure that all provider object paths are valid
[framework/connectivity/connman.git] / src / provider.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 <stdio.h>
27 #include <string.h>
28 #include <gdbus.h>
29
30 #include "connman.h"
31
32 static DBusConnection *connection = NULL;
33
34 static GHashTable *provider_hash = NULL;
35
36 static GSList *driver_list = NULL;
37
38 struct connman_provider {
39         struct connman_element element;
40         struct connman_service *vpn_service;
41         char *identifier;
42         char *name;
43         char *type;
44         char *host;
45         char *dns;
46         char *domain;
47         struct connman_provider_driver *driver;
48         void *driver_data;
49 };
50
51 void __connman_provider_append_properties(struct connman_provider *provider,
52                                                         DBusMessageIter *iter)
53 {
54         if (provider->host != NULL)
55                 connman_dbus_dict_append_basic(iter, "Host",
56                                         DBUS_TYPE_STRING, &provider->host);
57
58         if (provider->domain != NULL)
59                 connman_dbus_dict_append_basic(iter, "Domain",
60                                         DBUS_TYPE_STRING, &provider->domain);
61
62         if (provider->type != NULL)
63                 connman_dbus_dict_append_basic(iter, "Type", DBUS_TYPE_STRING,
64                                                  &provider->type);
65 }
66
67 static struct connman_provider *connman_provider_lookup(const char *identifier)
68 {
69         struct connman_provider *provider = NULL;
70
71         provider = g_hash_table_lookup(provider_hash, identifier);
72
73         return provider;
74 }
75
76 static int connman_provider_setup_vpn_ipv4(struct connman_provider *provider,
77                                                 struct connman_element *element)
78 {
79         if (element == NULL || provider == NULL)
80                 return -EINVAL;
81
82         DBG("set vpn type %d", element->type);
83
84         g_free(element->ipv4.address);
85         element->ipv4.address = g_strdup(provider->element.ipv4.address);
86
87         g_free(element->ipv4.netmask);
88         element->ipv4.netmask = g_strdup(provider->element.ipv4.netmask);
89
90         g_free(element->ipv4.gateway);
91         element->ipv4.gateway = g_strdup(provider->element.ipv4.gateway);
92
93         g_free(element->ipv4.broadcast);
94         element->ipv4.broadcast = g_strdup(provider->element.ipv4.broadcast);
95
96         g_free(element->ipv4.pac);
97         element->ipv4.pac = g_strdup(provider->element.ipv4.pac);
98
99         return connman_element_register(element, &provider->element);
100 }
101
102 struct connman_provider *connman_provider_ref(struct connman_provider *provider)
103 {
104         DBG("provider %p", provider);
105
106         if (connman_element_ref(&provider->element) == NULL)
107                 return NULL;
108
109         return provider;
110 }
111
112 void connman_provider_unref(struct connman_provider *provider)
113 {
114         DBG("provider %p", provider);
115
116         connman_element_unref(&provider->element);
117 }
118
119 static gboolean match_driver(struct connman_provider *provider,
120                                 struct connman_provider_driver *driver)
121 {
122         if (g_strcmp0(driver->name, provider->type) == 0)
123                 return TRUE;
124
125         return FALSE;
126 }
127
128 static int provider_probe(struct connman_provider *provider)
129 {
130         GSList *list;
131
132         DBG("provider %p name %s", provider, provider->name);
133
134         if (provider->driver != NULL)
135                 return -EALREADY;
136
137         for (list = driver_list; list; list = list->next) {
138                 struct connman_provider_driver *driver = list->data;
139
140                 if (match_driver(provider, driver) == FALSE)
141                         continue;
142
143                 DBG("driver %p name %s", driver, driver->name);
144
145                 if (driver->probe != NULL && driver->probe(provider) == 0) {
146                         provider->driver = driver;
147                         break;
148                 }
149         }
150
151         if (provider->driver == NULL)
152                 return -ENODEV;
153
154         return 0;
155 }
156
157 int __connman_provider_disconnect(struct connman_provider *provider)
158 {
159         int err;
160
161         DBG("provider %p", provider);
162
163         if (provider->driver != NULL && provider->driver->disconnect != NULL)
164                 err = provider->driver->disconnect(provider);
165         else
166                 return -EOPNOTSUPP;
167
168         __connman_service_indicate_state(provider->vpn_service,
169                                         CONNMAN_SERVICE_STATE_DISCONNECT);
170         if (err < 0) {
171                 if (err != -EINPROGRESS)
172                         return err;
173
174                 return -EINPROGRESS;
175         }
176
177         return 0;
178 }
179
180 int __connman_provider_connect(struct connman_provider *provider)
181 {
182         int err;
183
184         DBG("provider %p", provider);
185
186         g_free(provider->element.ipv4.address);
187         g_free(provider->element.ipv4.netmask);
188         g_free(provider->element.ipv4.gateway);
189         g_free(provider->element.ipv4.broadcast);
190         g_free(provider->element.ipv4.pac);
191
192         provider->element.ipv4.address = NULL;
193         provider->element.ipv4.netmask = NULL;
194         provider->element.ipv4.gateway = NULL;
195         provider->element.ipv4.broadcast = NULL;
196         provider->element.ipv4.pac = NULL;
197
198         if (provider->driver != NULL && provider->driver->connect != NULL)
199                 err = provider->driver->connect(provider);
200         else
201                 return -EOPNOTSUPP;
202
203         if (err < 0) {
204                 if (err != -EINPROGRESS)
205                         return err;
206
207                 __connman_service_indicate_state(provider->vpn_service,
208                                         CONNMAN_SERVICE_STATE_ASSOCIATION);
209                 return -EINPROGRESS;
210         }
211
212         return 0;
213 }
214
215 int __connman_provider_remove(const char *path)
216 {
217         struct connman_provider *provider;
218
219         DBG("path %s", path);
220
221         provider = g_hash_table_lookup(provider_hash, path);
222         if (provider == NULL) {
223                 DBG("patch %s not found", path);
224                 return -ENXIO;
225         }
226
227         g_hash_table_remove(provider_hash, path);
228
229         return 0;
230 }
231
232 static int set_connected(struct connman_provider *provider,
233                                         connman_bool_t connected)
234 {
235         struct connman_service *service = provider->vpn_service;
236
237         if (service == NULL)
238                 return -ENODEV;
239
240         if (connected == TRUE) {
241                 enum connman_element_type type = CONNMAN_ELEMENT_TYPE_UNKNOWN;
242                 struct connman_element *element;
243                 char *nameservers = NULL, *name = NULL;
244                 const char *value;
245                 char *second_ns;
246                 int err;
247
248                 __connman_service_indicate_state(provider->vpn_service,
249                                         CONNMAN_SERVICE_STATE_CONFIGURATION);
250
251                 type = CONNMAN_ELEMENT_TYPE_IPV4;
252
253                 element = connman_element_create(NULL);
254                 if (element == NULL)
255                         return -ENOMEM;
256
257                 element->type  = type;
258                 element->index = provider->element.index;
259
260                 err = connman_provider_setup_vpn_ipv4(provider, element);
261                 if (err < 0) {
262                         connman_element_unref(element);
263
264                         __connman_service_indicate_state(service,
265                                                 CONNMAN_SERVICE_STATE_FAILURE);
266
267                         return err;
268                 }
269
270                 __connman_service_indicate_state(service,
271                                                 CONNMAN_SERVICE_STATE_READY);
272
273                 __connman_service_set_domainname(service, provider->domain);
274
275                 name = connman_inet_ifname(provider->element.index);
276
277                 nameservers = g_strdup(provider->dns);
278                 value = nameservers;
279                 second_ns = strchr(value, ' ');
280                 if (second_ns)
281                         *(second_ns++) = 0;
282                 __connman_service_append_nameserver(service, value);
283                 value = second_ns;
284
285                 while (value) {
286                         char *next = strchr(value, ' ');
287                         if (next)
288                                 *(next++) = 0;
289
290                         connman_resolver_append(name, provider->domain, value);
291                         value = next;
292                 }
293
294                 g_free(nameservers);
295                 g_free(name);
296
297         } else {
298                 connman_element_unregister_children(&provider->element);
299                 __connman_service_indicate_state(service,
300                                                 CONNMAN_SERVICE_STATE_IDLE);
301         }
302
303         return 0;
304 }
305
306 int connman_provider_set_state(struct connman_provider *provider,
307                                         enum connman_provider_state state)
308 {
309         if (provider == NULL || provider->vpn_service == NULL)
310                 return -EINVAL;
311
312         switch (state) {
313         case CONNMAN_PROVIDER_STATE_UNKNOWN:
314                 return -EINVAL;
315         case CONNMAN_PROVIDER_STATE_IDLE:
316                 return set_connected(provider, FALSE);
317         case CONNMAN_PROVIDER_STATE_CONNECT:
318                 return __connman_service_indicate_state(provider->vpn_service,
319                                         CONNMAN_SERVICE_STATE_ASSOCIATION);
320         case CONNMAN_PROVIDER_STATE_READY:
321                 return set_connected(provider, TRUE);
322         case CONNMAN_PROVIDER_STATE_DISCONNECT:
323                 return __connman_service_indicate_state(provider->vpn_service,
324                                         CONNMAN_SERVICE_STATE_DISCONNECT);
325         case CONNMAN_PROVIDER_STATE_FAILURE:
326                 return __connman_service_indicate_state(provider->vpn_service,
327                                         CONNMAN_SERVICE_STATE_FAILURE);
328         }
329
330         return -EINVAL;
331 }
332
333 static void unregister_provider(gpointer data)
334 {
335         struct connman_provider *provider = data;
336
337         DBG("provider %p", provider);
338
339         __connman_service_put(provider->vpn_service);
340
341         connman_element_unregister(&provider->element);
342         connman_provider_unref(provider);
343 }
344
345 static void provider_destruct(struct connman_element *element)
346 {
347         struct connman_provider *provider = element->private;
348
349         DBG("provider %p", provider);
350
351         g_free(provider->name);
352         g_free(provider->type);
353         g_free(provider->domain);
354         g_free(provider->identifier);
355         g_free(provider->dns);
356 }
357
358 static void provider_initialize(struct connman_provider *provider)
359 {
360         DBG("provider %p", provider);
361
362         __connman_element_initialize(&provider->element);
363
364         provider->element.private = provider;
365         provider->element.destruct = provider_destruct;
366
367         provider->element.ipv4.address = NULL;
368         provider->element.ipv4.netmask = NULL;
369         provider->element.ipv4.gateway = NULL;
370         provider->element.ipv4.broadcast = NULL;
371         provider->element.ipv4.pac = NULL;
372
373         provider->name = NULL;
374         provider->type = NULL;
375         provider->dns = NULL;
376         provider->domain = NULL;
377         provider->identifier = NULL;
378 }
379
380 static struct connman_provider *connman_provider_new(void)
381 {
382         struct connman_provider *provider;
383
384         provider = g_try_new0(struct connman_provider, 1);
385         if (provider == NULL)
386                 return NULL;
387
388         DBG("provider %p", provider);
389         provider_initialize(provider);
390
391         return provider;
392 }
393
394 static struct connman_provider *connman_provider_get(const char *identifier)
395 {
396         struct connman_provider *provider;
397
398         provider = g_hash_table_lookup(provider_hash, identifier);
399         if (provider != NULL)
400                 return provider;
401
402         provider = connman_provider_new();
403         if (provider == NULL)
404                 return NULL;
405
406         DBG("provider %p", provider);
407
408         provider->identifier = g_strdup(identifier);
409
410         g_hash_table_insert(provider_hash, provider->identifier, provider);
411
412         provider->element.name = g_strdup(identifier);
413         connman_element_register(&provider->element, NULL);
414
415         return provider;
416 }
417
418 static void provider_dbus_ident(char *ident)
419 {
420         int i, len = strlen(ident);
421
422         for (i = 0; i < len; i++) {
423                 if (ident[i] >= '0' && ident[i] <= '9')
424                         continue;
425                 if (ident[i] >= 'a' && ident[i] <= 'z')
426                         continue;
427                 if (ident[i] >= 'A' && ident[i] <= 'Z')
428                         continue;
429                 ident[i] = '_';
430         }
431 }
432
433 int __connman_provider_create_and_connect(DBusMessage *msg)
434 {
435         struct connman_provider *provider;
436         DBusMessageIter iter, array;
437         const char *type = NULL, *name = NULL, *service_path = NULL;
438         const char *host = NULL, *domain = NULL;
439         char *ident;
440         gboolean created = FALSE;
441         int err;
442
443         dbus_message_iter_init(msg, &iter);
444         dbus_message_iter_recurse(&iter, &array);
445
446         while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) {
447                 DBusMessageIter entry, value;
448                 const char *key;
449
450                 dbus_message_iter_recurse(&array, &entry);
451                 dbus_message_iter_get_basic(&entry, &key);
452
453                 dbus_message_iter_next(&entry);
454                 dbus_message_iter_recurse(&entry, &value);
455
456                 switch (dbus_message_iter_get_arg_type(&value)) {
457                 case DBUS_TYPE_STRING:
458                         if (g_str_equal(key, "Type") == TRUE)
459                                 dbus_message_iter_get_basic(&value, &type);
460                         else if (g_str_equal(key, "Name") == TRUE)
461                                 dbus_message_iter_get_basic(&value, &name);
462                         else if (g_str_equal(key, "Host") == TRUE)
463                                 dbus_message_iter_get_basic(&value, &host);
464                         else if (g_str_equal(key, "VPN.Domain") == TRUE)
465                                 dbus_message_iter_get_basic(&value, &domain);
466                         break;
467                 }
468
469                 dbus_message_iter_next(&array);
470         }
471
472         if (host == NULL || domain == NULL) {
473                 err = -EINVAL;
474                 goto failed;
475         }
476
477         DBG("Type %s name %s", type, name);
478
479         if (type == NULL || name == NULL) {
480                 err = -EOPNOTSUPP;
481                 goto failed;
482         }
483
484         ident = g_strdup_printf("%s_%s", host, domain);
485         provider_dbus_ident(ident);
486
487         DBG("ident %s", ident);
488
489         provider = connman_provider_lookup(ident);
490
491         if (provider == NULL) {
492                 created = TRUE;
493                 provider = connman_provider_get(ident);
494                 if (provider) {
495                         provider->host = g_strdup(host);
496                         provider->domain = g_strdup(domain);
497                         provider->name = g_strdup(name);
498                         provider->type = g_strdup(type);
499                 }
500         }
501
502         if (provider == NULL) {
503                 DBG("can not create provider");
504                 err = -EOPNOTSUPP;
505                 goto failed;
506         }
507         dbus_message_iter_init(msg, &iter);
508         dbus_message_iter_recurse(&iter, &array);
509
510         while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) {
511                 DBusMessageIter entry, value;
512                 const char *key, *str;
513
514                 dbus_message_iter_recurse(&array, &entry);
515                 dbus_message_iter_get_basic(&entry, &key);
516
517                 dbus_message_iter_next(&entry);
518                 dbus_message_iter_recurse(&entry, &value);
519
520                 switch (dbus_message_iter_get_arg_type(&value)) {
521                 case DBUS_TYPE_STRING:
522                         dbus_message_iter_get_basic(&value, &str);
523                         connman_provider_set_string(provider, key, str);
524                         break;
525                 }
526
527                 dbus_message_iter_next(&array);
528         }
529
530         g_free(ident);
531
532         if (provider == NULL) {
533                 err = -EOPNOTSUPP;
534                 goto failed;
535         }
536
537         if (created == TRUE)
538                 provider_probe(provider);
539
540         provider->vpn_service =
541                         __connman_service_create_from_provider(provider);
542         if (provider->vpn_service == NULL) {
543                 err = -EOPNOTSUPP;
544                 goto failed;
545         }
546
547         err = __connman_service_connect(provider->vpn_service);
548         if (err < 0 && err != -EINPROGRESS)
549                 goto failed;
550
551         service_path = __connman_service_get_path(provider->vpn_service);
552         g_dbus_send_reply(connection, msg,
553                                 DBUS_TYPE_OBJECT_PATH, &service_path,
554                                                         DBUS_TYPE_INVALID);
555         return 0;
556
557 failed:
558         if (provider != NULL && created == TRUE) {
559                 DBG("can not connect delete provider");
560                 connman_provider_unref(provider);
561
562                 if (provider->vpn_service != NULL)
563                         __connman_service_put(provider->vpn_service);
564         }
565
566         return err;
567 }
568
569 const char * __connman_provider_get_ident(struct connman_provider *provider)
570 {
571         if (provider == NULL)
572                 return NULL;
573
574         return provider->identifier;
575 }
576
577 int connman_provider_set_string(struct connman_provider *provider,
578                                         const char *key, const char *value)
579 {
580         DBG("provider %p key %s value %s", provider, key, value);
581
582         if (g_str_equal(key, "Type") == TRUE) {
583                 g_free(provider->type);
584                 provider->type = g_strdup(value);
585         } else if (g_str_equal(key, "Name") == TRUE) {
586                 g_free(provider->name);
587                 provider->name = g_strdup(value);
588         } else if (g_str_equal(key, "Gateway") == TRUE) {
589                 g_free(provider->element.ipv4.gateway);
590                 provider->element.ipv4.gateway = g_strdup(value);
591         } else if (g_str_equal(key, "Address") == TRUE) {
592                 g_free(provider->element.ipv4.address);
593                 provider->element.ipv4.address = g_strdup(value);
594         } else if (g_str_equal(key, "Netmask") == TRUE) {
595                 g_free(provider->element.ipv4.netmask);
596                 provider->element.ipv4.netmask = g_strdup(value);
597         } else if (g_str_equal(key, "PAC") == TRUE) {
598                 g_free(provider->element.ipv4.pac);
599                 provider->element.ipv4.pac = g_strdup(value);
600                 __connman_service_set_proxy_autoconfig(provider->vpn_service,
601                                                                         value);
602         } else if (g_str_equal(key, "DNS") == TRUE) {
603                 g_free(provider->dns);
604                 provider->dns = g_strdup(value);
605         } else if (g_str_equal(key, "Domain") == TRUE) {
606                 g_free(provider->domain);
607                 provider->domain = g_strdup(value);
608         }
609
610         return connman_element_set_string(&provider->element, key, value);
611 }
612
613 const char *connman_provider_get_string(struct connman_provider *provider,
614                                                         const char *key)
615 {
616         DBG("provider %p key %s", provider, key);
617
618         if (g_str_equal(key, "Type") == TRUE)
619                 return provider->type;
620         else if (g_str_equal(key, "Name") == TRUE)
621                 return provider->name;
622
623         return connman_element_get_string(&provider->element, key);
624 }
625
626 void *connman_provider_get_data(struct connman_provider *provider)
627 {
628         return provider->driver_data;
629 }
630
631 void connman_provider_set_data(struct connman_provider *provider, void *data)
632 {
633         provider->driver_data = data;
634 }
635
636 void connman_provider_set_index(struct connman_provider *provider, int index)
637 {
638         struct connman_service *service = provider->vpn_service;
639         struct connman_ipconfig *ipconfig;
640
641         DBG("");
642
643         ipconfig = __connman_service_get_ipconfig(service);
644
645         if (ipconfig == NULL) {
646                 __connman_service_create_ipconfig(service, index);
647
648                 ipconfig = __connman_service_get_ipconfig(service);
649                 if (ipconfig == NULL) {
650                         DBG("Couldnt create ipconfig");
651                         goto done;
652                 }
653         }
654
655         connman_ipconfig_set_method(ipconfig, CONNMAN_IPCONFIG_METHOD_FIXED);
656         __connman_ipconfig_set_index(ipconfig, index);
657
658
659 done:
660         provider->element.index = index;
661 }
662
663 int connman_provider_get_index(struct connman_provider *provider)
664 {
665         return provider->element.index;
666 }
667
668 static gint compare_priority(gconstpointer a, gconstpointer b)
669 {
670         return 0;
671 }
672
673 static void clean_provider(gpointer key, gpointer value, gpointer user_data)
674 {
675         struct connman_provider *provider = value;
676
677         if (provider->driver != NULL && provider->driver->remove)
678                 provider->driver->remove(provider);
679 }
680
681 int connman_provider_driver_register(struct connman_provider_driver *driver)
682 {
683         DBG("driver %p name %s", driver, driver->name);
684
685         driver_list = g_slist_insert_sorted(driver_list, driver,
686                                                         compare_priority);
687         return 0;
688 }
689
690 void connman_provider_driver_unregister(struct connman_provider_driver *driver)
691 {
692         DBG("driver %p name %s", driver, driver->name);
693
694         driver_list = g_slist_remove(driver_list, driver);
695 }
696
697 static void provider_disconnect(gpointer key, gpointer value,
698                                                 gpointer user_data)
699 {
700         struct connman_provider *provider = value;
701
702         __connman_provider_disconnect(provider);
703 }
704
705 static void provider_offline_mode(connman_bool_t enabled)
706 {
707         DBG("enabled %d", enabled);
708
709         if (enabled == TRUE)
710                 g_hash_table_foreach(provider_hash, provider_disconnect, NULL);
711
712 }
713
714 static struct connman_notifier provider_notifier = {
715         .name                   = "provider",
716         .offline_mode           = provider_offline_mode,
717 };
718
719 int __connman_provider_init(void)
720 {
721         int err;
722
723         DBG("");
724
725         connection = connman_dbus_get_connection();
726
727         provider_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
728                                                 NULL, unregister_provider);
729
730         err = connman_notifier_register(&provider_notifier);
731         if (err < 0) {
732                 g_hash_table_destroy(provider_hash);
733                 dbus_connection_unref(connection);
734         }
735
736         return err;
737 }
738
739 void __connman_provider_cleanup(void)
740 {
741         DBG("");
742
743         connman_notifier_unregister(&provider_notifier);
744
745         g_hash_table_foreach(provider_hash, clean_provider, NULL);
746
747         g_hash_table_destroy(provider_hash);
748         provider_hash = NULL;
749
750         dbus_connection_unref(connection);
751 }