Export the VPN PAC file to the VPN service
[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, *first;
245                 int err;
246
247                 __connman_service_indicate_state(provider->vpn_service,
248                                         CONNMAN_SERVICE_STATE_CONFIGURATION);
249
250                 type = CONNMAN_ELEMENT_TYPE_IPV4;
251
252                 element = connman_element_create(NULL);
253                 if (element == NULL)
254                         return -ENOMEM;
255
256                 element->type  = type;
257                 element->index = provider->element.index;
258
259                 err = connman_provider_setup_vpn_ipv4(provider, element);
260                 if (err < 0) {
261                         connman_element_unref(element);
262
263                         __connman_service_indicate_state(service,
264                                                 CONNMAN_SERVICE_STATE_FAILURE);
265
266                         return err;
267                 }
268
269                 __connman_service_indicate_state(service,
270                                                 CONNMAN_SERVICE_STATE_READY);
271
272                 __connman_service_set_domainname(service, provider->domain);
273
274                 nameservers = g_strdup(provider->dns);
275                 value = nameservers;
276                 first = strchr(value, ' ');
277                 __connman_service_append_nameserver(service, first);
278                 name = connman_inet_ifname(provider->element.index);
279                 while (value) {
280                         char *next = strchr(value, ' ');
281                         if (next)
282                                 *(next++) = 0;
283
284                         connman_resolver_append(name, provider->domain, value);
285                         value = next;
286                 }
287
288                 g_free(nameservers);
289                 g_free(name);
290
291         } else {
292                 connman_element_unregister_children(&provider->element);
293                 __connman_service_indicate_state(service,
294                                                 CONNMAN_SERVICE_STATE_IDLE);
295         }
296
297         return 0;
298 }
299
300 int connman_provider_set_state(struct connman_provider *provider,
301                                         enum connman_provider_state state)
302 {
303         if (provider == NULL || provider->vpn_service == NULL)
304                 return -EINVAL;
305
306         switch (state) {
307         case CONNMAN_PROVIDER_STATE_UNKNOWN:
308                 return -EINVAL;
309         case CONNMAN_PROVIDER_STATE_IDLE:
310                 return set_connected(provider, FALSE);
311         case CONNMAN_PROVIDER_STATE_CONNECT:
312                 return __connman_service_indicate_state(provider->vpn_service,
313                                         CONNMAN_SERVICE_STATE_ASSOCIATION);
314         case CONNMAN_PROVIDER_STATE_READY:
315                 return set_connected(provider, TRUE);
316         case CONNMAN_PROVIDER_STATE_DISCONNECT:
317                 return __connman_service_indicate_state(provider->vpn_service,
318                                         CONNMAN_SERVICE_STATE_DISCONNECT);
319         case CONNMAN_PROVIDER_STATE_FAILURE:
320                 return __connman_service_indicate_state(provider->vpn_service,
321                                         CONNMAN_SERVICE_STATE_FAILURE);
322         }
323
324         return -EINVAL;
325 }
326
327 static void unregister_provider(gpointer data)
328 {
329         struct connman_provider *provider = data;
330
331         DBG("provider %p", provider);
332
333         __connman_provider_disconnect(provider);
334
335         connman_element_unregister(&provider->element);
336         connman_provider_unref(provider);
337 }
338
339 static void provider_destruct(struct connman_element *element)
340 {
341         struct connman_provider *provider = element->private;
342
343         DBG("provider %p", provider);
344
345         g_free(provider->name);
346         g_free(provider->type);
347         g_free(provider->domain);
348         g_free(provider->identifier);
349         g_free(provider->dns);
350         __connman_service_put(provider->vpn_service);
351 }
352
353 static void __connman_provider_initialize(struct connman_provider *provider)
354 {
355         DBG("provider %p", provider);
356
357         __connman_element_initialize(&provider->element);
358
359         provider->element.private = provider;
360         provider->element.destruct = provider_destruct;
361
362         provider->element.ipv4.address = NULL;
363         provider->element.ipv4.netmask = NULL;
364         provider->element.ipv4.gateway = NULL;
365         provider->element.ipv4.broadcast = NULL;
366         provider->element.ipv4.pac = NULL;
367
368         provider->name = NULL;
369         provider->type = NULL;
370         provider->dns = NULL;
371         provider->domain = NULL;
372         provider->identifier = NULL;
373 }
374
375 static struct connman_provider *connman_provider_new(void)
376 {
377         struct connman_provider *provider;
378
379         provider = g_try_new0(struct connman_provider, 1);
380         if (provider == NULL)
381                 return NULL;
382
383         DBG("provider %p", provider);
384         __connman_provider_initialize(provider);
385
386         return provider;
387 }
388
389 static struct connman_provider *connman_provider_get(const char *identifier)
390 {
391         struct connman_provider *provider;
392
393         provider = g_hash_table_lookup(provider_hash, identifier);
394         if (provider != NULL)
395                 return provider;
396
397         provider = connman_provider_new();
398         if (provider == NULL)
399                 return NULL;
400
401         DBG("provider %p", provider);
402
403         provider->identifier = g_strdup(identifier);
404
405         g_hash_table_insert(provider_hash, provider->identifier, provider);
406
407         provider->element.name = g_strdup(identifier);
408         connman_element_register(&provider->element, NULL);
409
410         return provider;
411 }
412
413 static void provider_dbus_ident(char *ident)
414 {
415         int i, len = strlen(ident);
416
417         for (i = 0; i < len; i++)
418                 if (ident[i] == '.')
419                         ident[i] = '_';
420 }
421
422 int __connman_provider_create_and_connect(DBusMessage *msg)
423 {
424         struct connman_provider *provider;
425         DBusMessageIter iter, array;
426         const char *type = NULL, *name = NULL, *service_path = NULL;
427         const char *host = NULL, *domain = NULL;
428         char *ident;
429         gboolean created = FALSE;
430         int err;
431
432         dbus_message_iter_init(msg, &iter);
433         dbus_message_iter_recurse(&iter, &array);
434
435         while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) {
436                 DBusMessageIter entry, value;
437                 const char *key;
438
439                 dbus_message_iter_recurse(&array, &entry);
440                 dbus_message_iter_get_basic(&entry, &key);
441
442                 dbus_message_iter_next(&entry);
443                 dbus_message_iter_recurse(&entry, &value);
444
445                 switch (dbus_message_iter_get_arg_type(&value)) {
446                 case DBUS_TYPE_STRING:
447                         if (g_str_equal(key, "Type") == TRUE)
448                                 dbus_message_iter_get_basic(&value, &type);
449                         else if (g_str_equal(key, "Name") == TRUE)
450                                 dbus_message_iter_get_basic(&value, &name);
451                         else if (g_str_equal(key, "Host") == TRUE)
452                                 dbus_message_iter_get_basic(&value, &host);
453                         else if (g_str_equal(key, "VPN.Domain") == TRUE)
454                                 dbus_message_iter_get_basic(&value, &domain);
455                         break;
456                 }
457
458                 dbus_message_iter_next(&array);
459         }
460
461         if (host == NULL && domain == NULL) {
462                 err = -EINVAL;
463                 goto failed;
464         }
465
466         DBG("Type %s name %s", type, name);
467
468         if (type == NULL || name == NULL) {
469                 err = -EOPNOTSUPP;
470                 goto failed;
471         }
472
473         ident = g_strdup_printf("%s_%s", host, domain);
474         provider_dbus_ident(ident);
475
476         DBG("ident %s", ident);
477
478         provider = connman_provider_lookup(ident);
479
480         if (provider == NULL) {
481                 created = TRUE;
482                 provider = connman_provider_get(ident);
483                 if (provider) {
484                         provider->host = g_strdup(host);
485                         provider->domain = g_strdup(domain);
486                         provider->name = g_strdup(name);
487                         provider->type = g_strdup(type);
488                 }
489         }
490
491         if (provider == NULL) {
492                 DBG("can not create provider");
493                 err = -EOPNOTSUPP;
494                 goto failed;
495         }
496         dbus_message_iter_init(msg, &iter);
497         dbus_message_iter_recurse(&iter, &array);
498
499         while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) {
500                 DBusMessageIter entry, value;
501                 const char *key, *str;
502
503                 dbus_message_iter_recurse(&array, &entry);
504                 dbus_message_iter_get_basic(&entry, &key);
505
506                 dbus_message_iter_next(&entry);
507                 dbus_message_iter_recurse(&entry, &value);
508
509                 switch (dbus_message_iter_get_arg_type(&value)) {
510                 case DBUS_TYPE_STRING:
511                         dbus_message_iter_get_basic(&value, &str);
512                         connman_provider_set_string(provider, key, str);
513                         break;
514                 }
515
516                 dbus_message_iter_next(&array);
517         }
518
519         g_free(ident);
520
521         if (provider == NULL) {
522                 err = -EOPNOTSUPP;
523                 goto failed;
524         }
525
526         if (created == TRUE)
527                 provider_probe(provider);
528
529         provider->vpn_service =
530                         __connman_service_create_from_provider(provider);
531         if (provider->vpn_service == NULL) {
532                 err = -EOPNOTSUPP;
533                 goto failed;
534         }
535
536         err = __connman_service_connect(provider->vpn_service);
537         if (err < 0 && err != -EINPROGRESS)
538                 goto failed;
539
540         service_path = __connman_service_get_path(provider->vpn_service);
541         g_dbus_send_reply(connection, msg,
542                                 DBUS_TYPE_OBJECT_PATH, &service_path,
543                                                         DBUS_TYPE_INVALID);
544         return 0;
545
546 failed:
547         if (provider != NULL && created == TRUE) {
548                 DBG("can not connect delete provider");
549                 connman_provider_unref(provider);
550
551                 if (provider->vpn_service != NULL)
552                         __connman_service_put(provider->vpn_service);
553         }
554
555         return err;
556 }
557
558 const char * __connman_provider_get_ident(struct connman_provider *provider)
559 {
560         if (provider == NULL)
561                 return NULL;
562
563         return provider->identifier;
564 }
565
566 int connman_provider_set_string(struct connman_provider *provider,
567                                         const char *key, const char *value)
568 {
569         DBG("provider %p key %s value %s", provider, key, value);
570
571         if (g_str_equal(key, "Type") == TRUE) {
572                 g_free(provider->type);
573                 provider->type = g_strdup(value);
574         } else if (g_str_equal(key, "Name") == TRUE) {
575                 g_free(provider->name);
576                 provider->name = g_strdup(value);
577         } else if (g_str_equal(key, "Gateway") == TRUE) {
578                 g_free(provider->element.ipv4.gateway);
579                 provider->element.ipv4.gateway = g_strdup(value);
580         } else if (g_str_equal(key, "Address") == TRUE) {
581                 g_free(provider->element.ipv4.address);
582                 provider->element.ipv4.address = g_strdup(value);
583         } else if (g_str_equal(key, "Netmask") == TRUE) {
584                 g_free(provider->element.ipv4.netmask);
585                 provider->element.ipv4.netmask = g_strdup(value);
586         } else if (g_str_equal(key, "PAC") == TRUE) {
587                 g_free(provider->element.ipv4.pac);
588                 provider->element.ipv4.pac = g_strdup(value);
589                 __connman_service_set_proxy_autoconfig(provider->vpn_service,
590                                                                         value);
591         } else if (g_str_equal(key, "DNS") == TRUE) {
592                 g_free(provider->dns);
593                 provider->dns = g_strdup(value);
594         } else if (g_str_equal(key, "Domain") == TRUE) {
595                 g_free(provider->domain);
596                 provider->domain = g_strdup(value);
597         }
598
599         return connman_element_set_string(&provider->element, key, value);
600 }
601
602 const char *connman_provider_get_string(struct connman_provider *provider,
603                                                         const char *key)
604 {
605         DBG("provider %p key %s", provider, key);
606
607         if (g_str_equal(key, "Type") == TRUE)
608                 return provider->type;
609         else if (g_str_equal(key, "Name") == TRUE)
610                 return provider->name;
611
612         return connman_element_get_string(&provider->element, key);
613 }
614
615 void *connman_provider_get_data(struct connman_provider *provider)
616 {
617         return provider->driver_data;
618 }
619
620 void connman_provider_set_data(struct connman_provider *provider, void *data)
621 {
622         provider->driver_data = data;
623 }
624
625 void connman_provider_set_index(struct connman_provider *provider, int index)
626 {
627         struct connman_service *service = provider->vpn_service;
628         struct connman_ipconfig *ipconfig;
629
630         DBG("");
631
632         ipconfig = __connman_service_get_ipconfig(service);
633
634         if (ipconfig == NULL) {
635                 __connman_service_create_ipconfig(service, index);
636
637                 ipconfig = __connman_service_get_ipconfig(service);
638                 if (ipconfig == NULL) {
639                         DBG("Couldnt create ipconfig");
640                         goto done;
641                 }
642         }
643
644         connman_ipconfig_set_method(ipconfig, CONNMAN_IPCONFIG_METHOD_FIXED);
645         __connman_ipconfig_set_index(ipconfig, index);
646
647
648 done:
649         provider->element.index = index;
650 }
651
652 int connman_provider_get_index(struct connman_provider *provider)
653 {
654         return provider->element.index;
655 }
656
657 static gint compare_priority(gconstpointer a, gconstpointer b)
658 {
659         return 0;
660 }
661
662 static void clean_provider(gpointer key, gpointer value, gpointer user_data)
663 {
664         struct connman_provider *provider = value;
665
666         if (provider->driver != NULL && provider->driver->remove)
667                 provider->driver->remove(provider);
668 }
669
670 int connman_provider_driver_register(struct connman_provider_driver *driver)
671 {
672         DBG("driver %p name %s", driver, driver->name);
673
674         driver_list = g_slist_insert_sorted(driver_list, driver,
675                                                         compare_priority);
676         return 0;
677 }
678
679 void connman_provider_driver_unregister(struct connman_provider_driver *driver)
680 {
681         DBG("driver %p name %s", driver, driver->name);
682
683         driver_list = g_slist_remove(driver_list, driver);
684 }
685
686 int __connman_provider_init(void)
687 {
688         DBG("");
689
690         connection = connman_dbus_get_connection();
691
692         provider_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
693                                                 NULL, unregister_provider);
694         return 0;
695 }
696
697 void __connman_provider_cleanup(void)
698 {
699         DBG("");
700
701         g_hash_table_foreach(provider_hash, clean_provider, NULL);
702
703         g_hash_table_destroy(provider_hash);
704         provider_hash = NULL;
705
706         dbus_connection_unref(connection);
707 }