session: Store user AllowedBearers configuration
[platform/upstream/connman.git] / src / session.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
6  *  Copyright (C) 2011  BWM CarIT GmbH. All rights reserved.
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License version 2 as
10  *  published by the Free Software Foundation.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  *
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <errno.h>
28
29 #include <gdbus.h>
30
31 #include <connman/session.h>
32
33 #include "connman.h"
34
35 static DBusConnection *connection;
36 static GHashTable *session_hash;
37 static connman_bool_t sessionmode;
38 static struct connman_session *ecall_session;
39 static GSList *policy_list;
40
41 enum connman_session_trigger {
42         CONNMAN_SESSION_TRIGGER_UNKNOWN         = 0,
43         CONNMAN_SESSION_TRIGGER_SETTING         = 1,
44         CONNMAN_SESSION_TRIGGER_CONNECT         = 2,
45         CONNMAN_SESSION_TRIGGER_DISCONNECT      = 3,
46         CONNMAN_SESSION_TRIGGER_SERVICE         = 4,
47         CONNMAN_SESSION_TRIGGER_ECALL           = 5,
48 };
49
50 enum connman_session_reason {
51         CONNMAN_SESSION_REASON_UNKNOWN          = 0,
52         CONNMAN_SESSION_REASON_CONNECT          = 1,
53         CONNMAN_SESSION_REASON_FREE_RIDE        = 2,
54 };
55
56 enum connman_session_state {
57         CONNMAN_SESSION_STATE_DISCONNECTED   = 0,
58         CONNMAN_SESSION_STATE_CONNECTED      = 1,
59         CONNMAN_SESSION_STATE_ONLINE         = 2,
60 };
61
62 struct service_entry {
63         /* track why this service was selected */
64         enum connman_session_reason reason;
65         enum connman_service_state state;
66         const char *name;
67         struct connman_service *service;
68         char *ifname;
69         const char *bearer;
70         GSList *pending_timeouts;
71 };
72
73 struct session_info {
74         struct connman_session_config config;
75         enum connman_session_state state;
76         struct service_entry *entry;
77         enum connman_session_reason reason;
78 };
79
80 struct connman_session {
81         char *owner;
82         char *session_path;
83         char *notify_path;
84         guint notify_watch;
85
86         struct connman_session_policy *policy;
87
88         connman_bool_t append_all;
89         struct session_info *info;
90         struct session_info *info_last;
91         struct connman_session_config *policy_config;
92         GSList *user_allowed_bearers;
93
94         connman_bool_t ecall;
95
96         GSequence *service_list;
97         GHashTable *service_hash;
98 };
99
100 static const char *trigger2string(enum connman_session_trigger trigger)
101 {
102         switch (trigger) {
103         case CONNMAN_SESSION_TRIGGER_UNKNOWN:
104                 break;
105         case CONNMAN_SESSION_TRIGGER_SETTING:
106                 return "setting";
107         case CONNMAN_SESSION_TRIGGER_CONNECT:
108                 return "connect";
109         case CONNMAN_SESSION_TRIGGER_DISCONNECT:
110                 return "disconnect";
111         case CONNMAN_SESSION_TRIGGER_SERVICE:
112                 return "service";
113         case CONNMAN_SESSION_TRIGGER_ECALL:
114                 return "ecall";
115         }
116
117         return NULL;
118 }
119
120 static const char *reason2string(enum connman_session_reason reason)
121 {
122         switch (reason) {
123         case CONNMAN_SESSION_REASON_UNKNOWN:
124                 return "unknown";
125         case CONNMAN_SESSION_REASON_CONNECT:
126                 return "connect";
127         case CONNMAN_SESSION_REASON_FREE_RIDE:
128                 return "free-ride";
129         }
130
131         return NULL;
132 }
133
134 static const char *state2string(enum connman_session_state state)
135 {
136         switch (state) {
137         case CONNMAN_SESSION_STATE_DISCONNECTED:
138                 return "disconnected";
139         case CONNMAN_SESSION_STATE_CONNECTED:
140                 return "connected";
141         case CONNMAN_SESSION_STATE_ONLINE:
142                 return "online";
143         }
144
145         return NULL;
146 }
147
148 static const char *type2string(enum connman_session_type type)
149 {
150         switch (type) {
151         case CONNMAN_SESSION_TYPE_UNKNOWN:
152                 return "";
153         case CONNMAN_SESSION_TYPE_ANY:
154                 return "any";
155         case CONNMAN_SESSION_TYPE_LOCAL:
156                 return "local";
157         case CONNMAN_SESSION_TYPE_INTERNET:
158                 return "internet";
159         }
160
161         return NULL;
162 }
163
164 enum connman_session_roaming_policy connman_session_parse_roaming_policy(const char *policy)
165 {
166         if (g_strcmp0(policy, "default") == 0)
167                 return CONNMAN_SESSION_ROAMING_POLICY_DEFAULT;
168         else if (g_strcmp0(policy, "always") == 0)
169                 return CONNMAN_SESSION_ROAMING_POLICY_ALWAYS;
170         else if (g_strcmp0(policy, "forbidden") == 0)
171                 return CONNMAN_SESSION_ROAMING_POLICY_FORBIDDEN;
172         else if (g_strcmp0(policy, "national") == 0)
173                 return CONNMAN_SESSION_ROAMING_POLICY_NATIONAL;
174         else if (g_strcmp0(policy, "international") == 0)
175                 return CONNMAN_SESSION_ROAMING_POLICY_INTERNATIONAL;
176         else
177                 return CONNMAN_SESSION_ROAMING_POLICY_UNKNOWN;
178 }
179
180 enum connman_session_type connman_session_parse_connection_type(const char *type)
181 {
182         if (g_strcmp0(type, "any") == 0)
183                 return CONNMAN_SESSION_TYPE_ANY;
184         if (g_strcmp0(type, "local") == 0)
185                 return CONNMAN_SESSION_TYPE_LOCAL;
186         else if (g_strcmp0(type, "internet") == 0)
187                 return CONNMAN_SESSION_TYPE_INTERNET;
188
189         return CONNMAN_SESSION_TYPE_UNKNOWN;
190 }
191
192 static int bearer2service(const char *bearer, enum connman_service_type *type)
193 {
194         if (g_strcmp0(bearer, "ethernet") == 0)
195                 *type = CONNMAN_SERVICE_TYPE_ETHERNET;
196         else if (g_strcmp0(bearer, "wifi") == 0)
197                 *type = CONNMAN_SERVICE_TYPE_WIFI;
198         else if (g_strcmp0(bearer, "wimax") == 0)
199                 *type = CONNMAN_SERVICE_TYPE_WIMAX;
200         else if (g_strcmp0(bearer, "bluetooth") == 0)
201                 *type = CONNMAN_SERVICE_TYPE_BLUETOOTH;
202         else if (g_strcmp0(bearer, "cellular") == 0)
203                 *type = CONNMAN_SERVICE_TYPE_CELLULAR;
204         else if (g_strcmp0(bearer, "vpn") == 0)
205                 *type = CONNMAN_SERVICE_TYPE_VPN;
206         else if (g_strcmp0(bearer, "*") == 0)
207                 *type = CONNMAN_SERVICE_TYPE_UNKNOWN;
208         else
209                 return -EINVAL;
210
211         return 0;
212 }
213
214 static char *service2bearer(enum connman_service_type type)
215 {
216         switch (type) {
217         case CONNMAN_SERVICE_TYPE_ETHERNET:
218                 return "ethernet";
219         case CONNMAN_SERVICE_TYPE_WIFI:
220                 return "wifi";
221         case CONNMAN_SERVICE_TYPE_WIMAX:
222                 return "wimax";
223         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
224                 return "bluetooth";
225         case CONNMAN_SERVICE_TYPE_CELLULAR:
226                 return "cellular";
227         case CONNMAN_SERVICE_TYPE_VPN:
228                 return "vpn";
229         case CONNMAN_SERVICE_TYPE_UNKNOWN:
230         case CONNMAN_SERVICE_TYPE_SYSTEM:
231         case CONNMAN_SERVICE_TYPE_GPS:
232         case CONNMAN_SERVICE_TYPE_GADGET:
233                 return "";
234         }
235
236         return "";
237 }
238
239 static int assign_policy_plugin(struct connman_session *session)
240 {
241         if (session->policy != NULL)
242                 return -EALREADY;
243
244         if (policy_list == NULL)
245                 return 0;
246
247         session->policy = policy_list->data;
248
249         return 0;
250 }
251
252 static int create_policy_config(struct connman_session *session,
253                                 connman_session_config_cb callback,
254                                 void *user_data)
255 {
256         struct connman_session_config *config;
257
258         if (session->policy == NULL) {
259                 config = connman_session_create_default_config();
260                 if (config == NULL)
261                         return -ENOMEM;
262
263                 session->policy_config = config;
264
265                 return 0;
266         }
267
268         return (*session->policy->create)(session, callback, user_data);
269 }
270
271 static void destroy_policy_config(struct connman_session *session)
272 {
273         if (session->policy == NULL) {
274                 g_free(session->policy_config);
275                 return;
276         }
277
278         (*session->policy->destroy)(session);
279 }
280
281 static void probe_policy(struct connman_session_policy *policy)
282 {
283
284         GHashTableIter iter;
285         gpointer key, value;
286         struct connman_session *session;
287
288         DBG("policy %p name %s", policy, policy->name);
289
290         g_hash_table_iter_init(&iter, session_hash);
291
292         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
293                 session = value;
294
295                 if (session->policy != NULL)
296                         continue;
297
298                 assign_policy_plugin(session);
299         }
300 }
301
302 static void remove_policy(struct connman_session_policy *policy)
303 {
304         GHashTableIter iter;
305         gpointer key, value;
306         struct connman_session *session;
307
308         DBG("policy %p name %s", policy, policy->name);
309
310         g_hash_table_iter_init(&iter, session_hash);
311
312         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
313                 session = value;
314
315                 if (session->policy != policy)
316                         continue;
317
318                 session->policy = NULL;
319                 assign_policy_plugin(session);
320         }
321 }
322
323 static gint compare_priority(gconstpointer a, gconstpointer b)
324 {
325         const struct connman_session_policy *policy1 = a;
326         const struct connman_session_policy *policy2 = b;
327
328         return policy2->priority - policy1->priority;
329 }
330
331
332 int connman_session_policy_register(struct connman_session_policy *policy)
333 {
334         DBG("name %s", policy->name);
335
336         if (policy->create == NULL || policy->destroy == NULL)
337                 return -EINVAL;
338
339         policy_list = g_slist_insert_sorted(policy_list, policy,
340                                                 compare_priority);
341
342         probe_policy(policy);
343
344         return 0;
345 }
346
347 void connman_session_policy_unregister(struct connman_session_policy *policy)
348 {
349         DBG("name %s", policy->name);
350
351         policy_list = g_slist_remove(policy_list, policy);
352
353         remove_policy(policy);
354 }
355
356 int connman_session_set_default_config(struct connman_session_config *config)
357 {
358         config->priority = FALSE;
359         config->roaming_policy = CONNMAN_SESSION_ROAMING_POLICY_DEFAULT;
360         config->type = CONNMAN_SESSION_TYPE_ANY;
361         config->ecall = FALSE;
362
363         g_slist_free(config->allowed_bearers);
364         config->allowed_bearers = g_slist_prepend(NULL,
365                                 GINT_TO_POINTER(CONNMAN_SERVICE_TYPE_UNKNOWN));
366         if (config->allowed_bearers == NULL)
367                 return -ENOMEM;
368
369         return 0;
370 }
371
372 struct connman_session_config *connman_session_create_default_config(void)
373 {
374         struct connman_session_config *config;
375
376         config = g_try_new0(struct connman_session_config, 1);
377         if (config == NULL)
378                 return NULL;
379
380         if (connman_session_set_default_config(config) < 0) {
381                 g_free(config);
382                 return NULL;
383         }
384
385         return config;
386 }
387
388 static enum connman_session_type apply_policy_on_type(
389                         enum connman_session_type policy,
390                         enum connman_session_type type)
391 {
392         if (type == CONNMAN_SESSION_TYPE_UNKNOWN)
393                 return CONNMAN_SESSION_TYPE_UNKNOWN;
394
395         if (policy == CONNMAN_SESSION_TYPE_ANY)
396                 return type;
397
398         if (policy == CONNMAN_SESSION_TYPE_LOCAL)
399                 return CONNMAN_SESSION_TYPE_LOCAL;
400
401         return CONNMAN_SESSION_TYPE_INTERNET;
402 }
403
404 int connman_session_parse_bearers(const char *token, GSList **list)
405 {
406         enum connman_service_type bearer;
407         int err;
408
409         if (g_strcmp0(token, "") == 0)
410                 return 0;
411
412         err = bearer2service(token, &bearer);
413         if (err < 0)
414                 return err;
415
416         *list = g_slist_append(*list, GINT_TO_POINTER(bearer));
417
418         return 0;
419 }
420
421 static int parse_bearers(DBusMessageIter *iter, GSList **list)
422 {
423         DBusMessageIter array;
424         int type, err;
425
426         dbus_message_iter_recurse(iter, &array);
427
428         *list = NULL;
429
430         while ((type = dbus_message_iter_get_arg_type(&array)) !=
431                         DBUS_TYPE_INVALID) {
432                 char *bearer_name = NULL;
433
434                 if (type != DBUS_TYPE_STRING) {
435                         g_slist_free(*list);
436                         *list = NULL;
437                         return -EINVAL;
438                 }
439
440                 dbus_message_iter_get_basic(&array, &bearer_name);
441
442                 err = connman_session_parse_bearers(bearer_name, list);
443                 if (err < 0) {
444                         g_slist_free(*list);
445                         *list = NULL;
446                         return err;
447                 }
448
449                 dbus_message_iter_next(&array);
450         }
451
452         return 0;
453 }
454
455 static int filter_bearer(GSList *policy_bearers,
456                                 enum connman_service_type bearer,
457                                 GSList **list)
458 {
459         enum connman_service_type policy;
460         GSList *it;
461
462         if (policy_bearers == NULL)
463                 goto clone;
464
465         for (it = policy_bearers; it != NULL; it = it->next) {
466                 policy = GPOINTER_TO_INT(it->data);
467
468                 if (policy != CONNMAN_SERVICE_TYPE_UNKNOWN && policy != bearer)
469                         continue;
470
471                 goto clone;
472         }
473
474         *list = NULL;
475
476         return 0;
477
478 clone:
479         *list = g_slist_append(*list, GINT_TO_POINTER(bearer));
480
481         return 0;
482 }
483
484 static int apply_policy_on_bearers(GSList *policy_bearers, GSList *bearers,
485                                 GSList **list)
486 {
487         enum connman_service_type bearer;
488         GSList *it;
489         int err;
490
491         *list = NULL;
492
493         for (it = bearers; it != NULL; it = it->next) {
494                 bearer = GPOINTER_TO_INT(it->data);
495
496                 err = filter_bearer(policy_bearers, bearer, list);
497                 if (err < 0)
498                         return err;
499         }
500
501         return 0;
502 }
503
504 const char *connman_session_get_owner(struct connman_session *session)
505 {
506         return session->owner;
507 }
508
509 static void append_allowed_bearers(DBusMessageIter *iter, void *user_data)
510 {
511         struct session_info *info = user_data;
512         GSList *list;
513
514         for (list = info->config.allowed_bearers;
515                         list != NULL; list = list->next) {
516                 enum connman_service_type bearer = GPOINTER_TO_INT(list->data);
517                 const char *name = __connman_service_type2string(bearer);
518
519                 if (name == NULL)
520                         name = "*";
521
522                 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
523                                                 &name);
524         }
525 }
526
527 static void append_ipconfig_ipv4(DBusMessageIter *iter, void *user_data)
528 {
529         struct connman_service *service = user_data;
530         struct connman_ipconfig *ipconfig_ipv4;
531
532         if (service == NULL)
533                 return;
534
535         if (__connman_service_is_connected_state(service,
536                                 CONNMAN_IPCONFIG_TYPE_IPV4) == FALSE) {
537                 return;
538         }
539
540         ipconfig_ipv4 = __connman_service_get_ip4config(service);
541         if (ipconfig_ipv4 == NULL)
542                 return;
543
544         __connman_ipconfig_append_ipv4(ipconfig_ipv4, iter);
545 }
546
547 static void append_ipconfig_ipv6(DBusMessageIter *iter, void *user_data)
548 {
549         struct connman_service *service = user_data;
550         struct connman_ipconfig *ipconfig_ipv4, *ipconfig_ipv6;
551
552         if (service == NULL)
553                 return;
554
555         if (__connman_service_is_connected_state(service,
556                                 CONNMAN_IPCONFIG_TYPE_IPV6) == FALSE) {
557                 return;
558         }
559
560         ipconfig_ipv4 = __connman_service_get_ip4config(service);
561         ipconfig_ipv6 = __connman_service_get_ip6config(service);
562         if (ipconfig_ipv6 == NULL)
563                 return;
564
565         __connman_ipconfig_append_ipv6(ipconfig_ipv6, iter, ipconfig_ipv4);
566 }
567
568 static void append_notify(DBusMessageIter *dict,
569                                         struct connman_session *session)
570 {
571         struct session_info *info = session->info;
572         struct session_info *info_last = session->info_last;
573         struct connman_service *service;
574         const char *name, *ifname, *bearer;
575
576         if (session->append_all == TRUE ||
577                         info->state != info_last->state) {
578                 const char *state = state2string(info->state);
579
580                 connman_dbus_dict_append_basic(dict, "State",
581                                                 DBUS_TYPE_STRING,
582                                                 &state);
583                 info_last->state = info->state;
584         }
585
586         if (session->append_all == TRUE ||
587                         info->entry != info_last->entry) {
588                 if (info->entry == NULL) {
589                         name = "";
590                         ifname = "";
591                         service = NULL;
592                         bearer = "";
593                 } else {
594                         name = info->entry->name;
595                         ifname = info->entry->ifname;
596                         service = info->entry->service;
597                         bearer = info->entry->bearer;
598                 }
599
600                 connman_dbus_dict_append_basic(dict, "Name",
601                                                 DBUS_TYPE_STRING,
602                                                 &name);
603
604                 connman_dbus_dict_append_dict(dict, "IPv4",
605                                                 append_ipconfig_ipv4,
606                                                 service);
607
608                 connman_dbus_dict_append_dict(dict, "IPv6",
609                                                 append_ipconfig_ipv6,
610                                                 service);
611
612                 connman_dbus_dict_append_basic(dict, "Interface",
613                                                 DBUS_TYPE_STRING,
614                                                 &ifname);
615
616                 connman_dbus_dict_append_basic(dict, "Bearer",
617                                                 DBUS_TYPE_STRING,
618                                                 &bearer);
619
620                 info_last->entry = info->entry;
621         }
622
623         if (session->append_all == TRUE ||
624                         info->config.type != info_last->config.type) {
625                 const char *type = type2string(info->config.type);
626
627                 connman_dbus_dict_append_basic(dict, "ConnectionType",
628                                                 DBUS_TYPE_STRING,
629                                                 &type);
630                 info_last->config.type = info->config.type;
631         }
632
633         if (session->append_all == TRUE ||
634                         info->config.allowed_bearers != info_last->config.allowed_bearers) {
635                 connman_dbus_dict_append_array(dict, "AllowedBearers",
636                                                 DBUS_TYPE_STRING,
637                                                 append_allowed_bearers,
638                                                 info);
639                 info_last->config.allowed_bearers = info->config.allowed_bearers;
640         }
641
642         session->append_all = FALSE;
643 }
644
645 static connman_bool_t is_type_matching_state(enum connman_session_state *state,
646                                                 enum connman_session_type type)
647 {
648         switch (type) {
649         case CONNMAN_SESSION_TYPE_UNKNOWN:
650                 return FALSE;
651         case CONNMAN_SESSION_TYPE_ANY:
652                 return TRUE;
653         case CONNMAN_SESSION_TYPE_LOCAL:
654                 if (*state >= CONNMAN_SESSION_STATE_CONNECTED) {
655                         *state = CONNMAN_SESSION_STATE_CONNECTED;
656                         return TRUE;
657                 }
658
659                 break;
660         case CONNMAN_SESSION_TYPE_INTERNET:
661                 if (*state == CONNMAN_SESSION_STATE_ONLINE)
662                         return TRUE;
663                 break;
664         }
665
666         return FALSE;
667 }
668
669 static connman_bool_t compute_notifiable_changes(struct connman_session *session)
670 {
671         struct session_info *info_last = session->info_last;
672         struct session_info *info = session->info;
673
674         if (session->append_all == TRUE)
675                 return TRUE;
676
677         if (info->state != info_last->state)
678                 return TRUE;
679
680         if (info->entry != info_last->entry &&
681                         info->state >= CONNMAN_SESSION_STATE_CONNECTED)
682                 return TRUE;
683
684         if (info->config.allowed_bearers != info_last->config.allowed_bearers ||
685                         info->config.type != info_last->config.type)
686                 return TRUE;
687
688         return FALSE;
689 }
690
691 static gboolean session_notify(gpointer user_data)
692 {
693         struct connman_session *session = user_data;
694         DBusMessage *msg;
695         DBusMessageIter array, dict;
696
697         if (compute_notifiable_changes(session) == FALSE)
698                 return FALSE;
699
700         DBG("session %p owner %s notify_path %s", session,
701                 session->owner, session->notify_path);
702
703         msg = dbus_message_new_method_call(session->owner, session->notify_path,
704                                                 CONNMAN_NOTIFICATION_INTERFACE,
705                                                 "Update");
706         if (msg == NULL)
707                 return FALSE;
708
709         dbus_message_iter_init_append(msg, &array);
710         connman_dbus_dict_open(&array, &dict);
711
712         append_notify(&dict, session);
713
714         connman_dbus_dict_close(&array, &dict);
715
716         g_dbus_send_message(connection, msg);
717
718         return FALSE;
719 }
720
721 static void ipconfig_ipv4_changed(struct connman_session *session)
722 {
723         struct session_info *info = session->info;
724
725         connman_dbus_setting_changed_dict(session->owner, session->notify_path,
726                                                 "IPv4", append_ipconfig_ipv4,
727                                                 info->entry->service);
728 }
729
730 static void ipconfig_ipv6_changed(struct connman_session *session)
731 {
732         struct session_info *info = session->info;
733
734         connman_dbus_setting_changed_dict(session->owner, session->notify_path,
735                                                 "IPv6", append_ipconfig_ipv6,
736                                                 info->entry->service);
737 }
738
739 static connman_bool_t service_type_match(struct connman_session *session,
740                                         struct connman_service *service)
741 {
742         struct session_info *info = session->info;
743         GSList *list;
744
745         for (list = info->config.allowed_bearers;
746                         list != NULL; list = list->next) {
747                 enum connman_service_type bearer = GPOINTER_TO_INT(list->data);
748                 enum connman_service_type service_type;
749
750                 if (bearer == CONNMAN_SERVICE_TYPE_UNKNOWN)
751                         return TRUE;
752
753                 service_type = connman_service_get_type(service);
754                 if (bearer == service_type)
755                         return TRUE;
756         }
757
758         return FALSE;
759 }
760
761 static connman_bool_t service_match(struct connman_session *session,
762                                         struct connman_service *service)
763 {
764         if (service_type_match(session, service) == FALSE)
765                 return FALSE;
766
767         return TRUE;
768 }
769
770 static int service_type_weight(enum connman_service_type type)
771 {
772         /*
773          * The session doesn't care which service
774          * to use. Nevertheless we have to sort them
775          * according their type. The ordering is
776          *
777          * 1. Ethernet
778          * 2. Bluetooth
779          * 3. WiFi/WiMAX
780          * 4. Cellular
781          */
782
783         switch (type) {
784         case CONNMAN_SERVICE_TYPE_ETHERNET:
785                 return 4;
786         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
787                 return 3;
788         case CONNMAN_SERVICE_TYPE_WIFI:
789         case CONNMAN_SERVICE_TYPE_WIMAX:
790                 return 2;
791         case CONNMAN_SERVICE_TYPE_CELLULAR:
792                 return 1;
793         case CONNMAN_SERVICE_TYPE_UNKNOWN:
794         case CONNMAN_SERVICE_TYPE_SYSTEM:
795         case CONNMAN_SERVICE_TYPE_GPS:
796         case CONNMAN_SERVICE_TYPE_VPN:
797         case CONNMAN_SERVICE_TYPE_GADGET:
798                 break;
799         }
800
801         return 0;
802 }
803
804 static gint sort_allowed_bearers(struct connman_service *service_a,
805                                         struct connman_service *service_b,
806                                         struct connman_session *session)
807 {
808         struct session_info *info = session->info;
809         GSList *list;
810         enum connman_service_type type_a, type_b;
811         int weight_a, weight_b;
812
813         type_a = connman_service_get_type(service_a);
814         type_b = connman_service_get_type(service_b);
815
816         for (list = info->config.allowed_bearers;
817                         list != NULL; list = list->next) {
818                 enum connman_service_type bearer = GPOINTER_TO_INT(list->data);
819
820                 if (bearer == CONNMAN_SERVICE_TYPE_UNKNOWN) {
821                         if (type_a != type_b) {
822                                 weight_a = service_type_weight(type_a);
823                                 weight_b = service_type_weight(type_b);
824
825                                 if (weight_a > weight_b)
826                                         return -1;
827
828                                 if (weight_a < weight_b)
829                                         return 1;
830
831                                 return 0;
832                         }
833                 }
834
835                 if (type_a == bearer && type_b == bearer)
836                         return 0;
837
838                 if (type_a == bearer && type_b != bearer)
839                         return -1;
840
841                 if (type_a != bearer && type_b == bearer)
842                         return 1;
843         }
844
845         return 0;
846 }
847
848 static gint sort_services(gconstpointer a, gconstpointer b, gpointer user_data)
849 {
850         struct service_entry *entry_a = (void *)a;
851         struct service_entry *entry_b = (void *)b;
852         struct connman_session *session = user_data;
853
854         return sort_allowed_bearers(entry_a->service, entry_b->service,
855                                 session);
856 }
857
858 static void free_session(struct connman_session *session)
859 {
860         if (session == NULL)
861                 return;
862
863         destroy_policy_config(session);
864         g_slist_free(session->info->config.allowed_bearers);
865         g_free(session->owner);
866         g_free(session->session_path);
867         g_free(session->notify_path);
868         g_free(session->info);
869         g_free(session->info_last);
870
871         g_free(session);
872 }
873
874 static void cleanup_session(gpointer user_data)
875 {
876         struct connman_session *session = user_data;
877         struct session_info *info = session->info;
878
879         DBG("remove %s", session->session_path);
880
881         g_slist_free(session->user_allowed_bearers);
882         g_hash_table_destroy(session->service_hash);
883         g_sequence_free(session->service_list);
884
885         if (info->entry != NULL &&
886                         info->entry->reason == CONNMAN_SESSION_REASON_CONNECT) {
887                 __connman_service_disconnect(info->entry->service);
888         }
889
890         free_session(session);
891 }
892
893 static enum connman_session_state service_to_session_state(enum connman_service_state state)
894 {
895         switch (state) {
896         case CONNMAN_SERVICE_STATE_UNKNOWN:
897         case CONNMAN_SERVICE_STATE_IDLE:
898         case CONNMAN_SERVICE_STATE_ASSOCIATION:
899         case CONNMAN_SERVICE_STATE_CONFIGURATION:
900         case CONNMAN_SERVICE_STATE_DISCONNECT:
901         case CONNMAN_SERVICE_STATE_FAILURE:
902                 break;
903         case CONNMAN_SERVICE_STATE_READY:
904                 return CONNMAN_SESSION_STATE_CONNECTED;
905         case CONNMAN_SERVICE_STATE_ONLINE:
906                 return CONNMAN_SESSION_STATE_ONLINE;
907         }
908
909         return CONNMAN_SESSION_STATE_DISCONNECTED;
910 }
911
912 static connman_bool_t is_connected(enum connman_service_state state)
913 {
914         switch (state) {
915         case CONNMAN_SERVICE_STATE_UNKNOWN:
916         case CONNMAN_SERVICE_STATE_IDLE:
917         case CONNMAN_SERVICE_STATE_ASSOCIATION:
918         case CONNMAN_SERVICE_STATE_CONFIGURATION:
919         case CONNMAN_SERVICE_STATE_DISCONNECT:
920         case CONNMAN_SERVICE_STATE_FAILURE:
921                 break;
922         case CONNMAN_SERVICE_STATE_READY:
923         case CONNMAN_SERVICE_STATE_ONLINE:
924                 return TRUE;
925         }
926
927         return FALSE;
928 }
929
930 static connman_bool_t is_connecting(enum connman_service_state state)
931 {
932         switch (state) {
933         case CONNMAN_SERVICE_STATE_UNKNOWN:
934         case CONNMAN_SERVICE_STATE_IDLE:
935                 break;
936         case CONNMAN_SERVICE_STATE_ASSOCIATION:
937         case CONNMAN_SERVICE_STATE_CONFIGURATION:
938                 return TRUE;
939         case CONNMAN_SERVICE_STATE_DISCONNECT:
940         case CONNMAN_SERVICE_STATE_FAILURE:
941         case CONNMAN_SERVICE_STATE_READY:
942         case CONNMAN_SERVICE_STATE_ONLINE:
943                 break;
944         }
945
946         return FALSE;
947 }
948
949 static connman_bool_t explicit_connect(enum connman_session_reason reason)
950 {
951         switch (reason) {
952         case CONNMAN_SESSION_REASON_UNKNOWN:
953         case CONNMAN_SESSION_REASON_FREE_RIDE:
954                 break;
955         case CONNMAN_SESSION_REASON_CONNECT:
956                 return TRUE;
957         }
958
959         return FALSE;
960 }
961
962 static connman_bool_t explicit_disconnect(struct session_info *info)
963 {
964         if (info->entry == NULL)
965                 return FALSE;
966
967         DBG("reason %s service %p state %d",
968                 reason2string(info->entry->reason),
969                 info->entry->service, info->entry->state);
970
971         if (info->entry->reason == CONNMAN_SESSION_REASON_UNKNOWN)
972                 return FALSE;
973
974         if (explicit_connect(info->entry->reason) == FALSE)
975                 return FALSE;
976
977         if (__connman_service_session_dec(info->entry->service) == FALSE)
978                 return FALSE;
979
980         return TRUE;
981 }
982
983 struct pending_data {
984         unsigned int timeout;
985         struct service_entry *entry;
986         gboolean (*cb)(gpointer);
987 };
988
989 static void pending_timeout_free(gpointer data, gpointer user_data)
990 {
991         struct pending_data *pending = data;
992
993         DBG("pending %p timeout %d", pending, pending->timeout);
994         g_source_remove(pending->timeout);
995         g_free(pending);
996 }
997
998 static void pending_timeout_remove_all(struct service_entry *entry)
999 {
1000         DBG("");
1001
1002         g_slist_foreach(entry->pending_timeouts, pending_timeout_free, NULL);
1003         g_slist_free(entry->pending_timeouts);
1004         entry->pending_timeouts = NULL;
1005 }
1006
1007 static gboolean pending_timeout_cb(gpointer data)
1008 {
1009         struct pending_data *pending = data;
1010         struct service_entry *entry = pending->entry;
1011         gboolean ret;
1012
1013         DBG("pending %p timeout %d", pending, pending->timeout);
1014
1015         ret = pending->cb(pending->entry);
1016         if (ret == FALSE) {
1017                 entry->pending_timeouts =
1018                         g_slist_remove(entry->pending_timeouts,
1019                                         pending);
1020                 g_free(pending);
1021         }
1022         return ret;
1023 }
1024
1025 static connman_bool_t pending_timeout_add(unsigned int seconds,
1026                                         gboolean (*cb)(gpointer),
1027                                         struct service_entry *entry)
1028 {
1029         struct pending_data *pending = g_try_new0(struct pending_data, 1);
1030
1031         if (pending == NULL || cb == NULL || entry == NULL) {
1032                 g_free(pending);
1033                 return FALSE;
1034         }
1035
1036         pending->cb = cb;
1037         pending->entry = entry;
1038         pending->timeout = g_timeout_add_seconds(seconds, pending_timeout_cb,
1039                                                 pending);
1040         entry->pending_timeouts = g_slist_prepend(entry->pending_timeouts,
1041                                                 pending);
1042
1043         DBG("pending %p entry %p timeout id %d", pending, entry,
1044                 pending->timeout);
1045
1046         return TRUE;
1047 }
1048
1049 static gboolean call_disconnect(gpointer user_data)
1050 {
1051         struct service_entry *entry = user_data;
1052         struct connman_service *service = entry->service;
1053
1054         /*
1055          * TODO: We should mark this entry as pending work. In case
1056          * disconnect fails we just unassign this session from the
1057          * service and can't do anything later on it
1058          */
1059         DBG("disconnect service %p", service);
1060         __connman_service_disconnect(service);
1061
1062         return FALSE;
1063 }
1064
1065 static gboolean call_connect(gpointer user_data)
1066 {
1067         struct service_entry *entry = user_data;
1068         struct connman_service *service = entry->service;
1069
1070         DBG("connect service %p", service);
1071         __connman_service_connect(service);
1072
1073         return FALSE;
1074 }
1075
1076 static void deselect_service(struct session_info *info)
1077 {
1078         struct service_entry *entry;
1079         connman_bool_t disconnect, connected;
1080
1081         DBG("");
1082
1083         if (info->entry == NULL)
1084                 return;
1085
1086         disconnect = explicit_disconnect(info);
1087
1088         connected = is_connecting(info->entry->state) == TRUE ||
1089                         is_connected(info->entry->state) == TRUE;
1090
1091         info->state = CONNMAN_SESSION_STATE_DISCONNECTED;
1092         info->entry->reason = CONNMAN_SESSION_REASON_UNKNOWN;
1093
1094         entry = info->entry;
1095         info->entry = NULL;
1096
1097         DBG("disconnect %d connected %d", disconnect, connected);
1098
1099         if (disconnect == TRUE && connected == TRUE)
1100                 pending_timeout_add(0, call_disconnect, entry);
1101 }
1102
1103 static void deselect_and_disconnect(struct connman_session *session)
1104 {
1105         struct session_info *info = session->info;
1106
1107         deselect_service(info);
1108
1109         info->reason = CONNMAN_SESSION_REASON_FREE_RIDE;
1110 }
1111
1112 static void select_connected_service(struct session_info *info,
1113                                         struct service_entry *entry)
1114 {
1115         enum connman_session_state state;
1116
1117         state = service_to_session_state(entry->state);
1118         if (is_type_matching_state(&state, info->config.type) == FALSE)
1119                 return;
1120
1121         info->state = state;
1122
1123         info->entry = entry;
1124         info->entry->reason = info->reason;
1125
1126         if (explicit_connect(info->reason) == FALSE)
1127                 return;
1128
1129         __connman_service_session_inc(info->entry->service);
1130 }
1131
1132 static void select_offline_service(struct session_info *info,
1133                                         struct service_entry *entry)
1134 {
1135         if (explicit_connect(info->reason) == FALSE)
1136                 return;
1137
1138         info->state = service_to_session_state(entry->state);
1139
1140         info->entry = entry;
1141         info->entry->reason = info->reason;
1142
1143         __connman_service_session_inc(info->entry->service);
1144         pending_timeout_add(0, call_connect, entry);
1145 }
1146
1147 static void select_service(struct session_info *info,
1148                                 struct service_entry *entry)
1149 {
1150         DBG("service %p", entry->service);
1151
1152         if (is_connected(entry->state) == TRUE)
1153                 select_connected_service(info, entry);
1154         else
1155                 select_offline_service(info, entry);
1156 }
1157
1158 static void select_and_connect(struct connman_session *session,
1159                                 enum connman_session_reason reason)
1160 {
1161         struct session_info *info = session->info;
1162         struct service_entry *entry = NULL;
1163         GSequenceIter *iter;
1164
1165         DBG("session %p reason %s", session, reason2string(reason));
1166
1167         info->reason = reason;
1168
1169         iter = g_sequence_get_begin_iter(session->service_list);
1170
1171         while (g_sequence_iter_is_end(iter) == FALSE) {
1172                 entry = g_sequence_get(iter);
1173
1174                 switch (entry->state) {
1175                 case CONNMAN_SERVICE_STATE_ASSOCIATION:
1176                 case CONNMAN_SERVICE_STATE_CONFIGURATION:
1177                 case CONNMAN_SERVICE_STATE_READY:
1178                 case CONNMAN_SERVICE_STATE_ONLINE:
1179                 case CONNMAN_SERVICE_STATE_IDLE:
1180                 case CONNMAN_SERVICE_STATE_DISCONNECT:
1181                         select_service(info, entry);
1182                         return;
1183                 case CONNMAN_SERVICE_STATE_UNKNOWN:
1184                 case CONNMAN_SERVICE_STATE_FAILURE:
1185                         break;
1186                 }
1187
1188                 iter = g_sequence_iter_next(iter);
1189         }
1190 }
1191
1192 static struct service_entry *create_service_entry(struct connman_service *service,
1193                                         const char *name,
1194                                         enum connman_service_state state)
1195 {
1196         struct service_entry *entry;
1197         enum connman_service_type type;
1198         int idx;
1199
1200         entry = g_try_new0(struct service_entry, 1);
1201         if (entry == NULL)
1202                 return entry;
1203
1204         entry->reason = CONNMAN_SESSION_REASON_UNKNOWN;
1205         entry->state = state;
1206         if (name != NULL)
1207                 entry->name = name;
1208         else
1209                 entry->name = "";
1210         entry->service = service;
1211
1212         idx = __connman_service_get_index(entry->service);
1213         entry->ifname = connman_inet_ifname(idx);
1214         if (entry->ifname == NULL)
1215                 entry->ifname = g_strdup("");
1216
1217         type = connman_service_get_type(entry->service);
1218         entry->bearer = service2bearer(type);
1219
1220         return entry;
1221 }
1222
1223 static void destroy_service_entry(gpointer data)
1224 {
1225         struct service_entry *entry = data;
1226
1227         pending_timeout_remove_all(entry);
1228         g_free(entry->ifname);
1229
1230         g_free(entry);
1231 }
1232
1233 static void populate_service_list(struct connman_session *session)
1234 {
1235         struct service_entry *entry;
1236         GSequenceIter *iter;
1237
1238         session->service_hash =
1239                 g_hash_table_new_full(g_direct_hash, g_direct_equal,
1240                                         NULL, NULL);
1241         session->service_list = __connman_service_get_list(session,
1242                                                         service_match,
1243                                                         create_service_entry,
1244                                                         destroy_service_entry);
1245
1246         g_sequence_sort(session->service_list, sort_services, session);
1247
1248         iter = g_sequence_get_begin_iter(session->service_list);
1249
1250         while (g_sequence_iter_is_end(iter) == FALSE) {
1251                 entry = g_sequence_get(iter);
1252
1253                 DBG("service %p type %s name %s", entry->service,
1254                         service2bearer(connman_service_get_type(entry->service)),
1255                         entry->name);
1256
1257                 g_hash_table_replace(session->service_hash,
1258                                         entry->service, iter);
1259
1260                 iter = g_sequence_iter_next(iter);
1261         }
1262 }
1263
1264 static void session_changed(struct connman_session *session,
1265                                 enum connman_session_trigger trigger)
1266 {
1267         struct session_info *info = session->info;
1268         struct session_info *info_last = session->info_last;
1269         GSequenceIter *service_iter = NULL, *service_iter_last = NULL;
1270         GSequence *service_list_last;
1271         GHashTable *service_hash_last;
1272
1273         /*
1274          * TODO: This only a placeholder for the 'real' algorithm to
1275          * play a bit around. So we are going to improve it step by step.
1276          */
1277
1278         DBG("session %p trigger %s reason %s", session, trigger2string(trigger),
1279                                                 reason2string(info->reason));
1280
1281         if (info->entry != NULL) {
1282                 enum connman_session_state state;
1283
1284                 state = service_to_session_state(info->entry->state);
1285
1286                 if (is_type_matching_state(&state, info->config.type) == TRUE)
1287                         info->state = state;
1288         }
1289
1290         switch (trigger) {
1291         case CONNMAN_SESSION_TRIGGER_UNKNOWN:
1292                 DBG("ignore session changed event");
1293                 return;
1294         case CONNMAN_SESSION_TRIGGER_SETTING:
1295                 if (info->config.allowed_bearers != info_last->config.allowed_bearers) {
1296
1297                         service_hash_last = session->service_hash;
1298                         service_list_last = session->service_list;
1299
1300                         populate_service_list(session);
1301
1302                         if (info->entry != NULL) {
1303                                 service_iter_last = g_hash_table_lookup(
1304                                                         service_hash_last,
1305                                                         info->entry->service);
1306                                 service_iter = g_hash_table_lookup(
1307                                                         session->service_hash,
1308                                                         info->entry->service);
1309                         }
1310
1311                         if (service_iter == NULL && service_iter_last != NULL) {
1312                                 /*
1313                                  * The currently selected service is
1314                                  * not part of this session anymore.
1315                                  */
1316                                 deselect_and_disconnect(session);
1317                         }
1318
1319                         g_hash_table_remove_all(service_hash_last);
1320                         g_sequence_free(service_list_last);
1321                 }
1322
1323                 if (info->config.type != info_last->config.type) {
1324                         if (info->state >= CONNMAN_SESSION_STATE_CONNECTED &&
1325                                         is_type_matching_state(&info->state,
1326                                                         info->config.type) == FALSE)
1327                                 deselect_and_disconnect(session);
1328                 }
1329
1330                 if (info->state == CONNMAN_SESSION_STATE_DISCONNECTED) {
1331                         select_and_connect(session,
1332                                         CONNMAN_SESSION_REASON_FREE_RIDE);
1333                 }
1334
1335                 break;
1336         case CONNMAN_SESSION_TRIGGER_ECALL:
1337                 /*
1338                  * For the time beeing we fallback to normal connect
1339                  * strategy.
1340                  */
1341         case CONNMAN_SESSION_TRIGGER_CONNECT:
1342                 if (info->state >= CONNMAN_SESSION_STATE_CONNECTED) {
1343                         if (info->entry->reason == CONNMAN_SESSION_REASON_CONNECT)
1344                                 break;
1345                         info->entry->reason = CONNMAN_SESSION_REASON_CONNECT;
1346                         __connman_service_session_inc(info->entry->service);
1347                         break;
1348                 }
1349
1350                 if (info->entry != NULL &&
1351                                 is_connecting(info->entry->state) == TRUE) {
1352                         break;
1353                 }
1354
1355                 select_and_connect(session,
1356                                 CONNMAN_SESSION_REASON_CONNECT);
1357
1358                 break;
1359         case CONNMAN_SESSION_TRIGGER_DISCONNECT:
1360                 deselect_and_disconnect(session);
1361
1362                 break;
1363         case CONNMAN_SESSION_TRIGGER_SERVICE:
1364                 if (info->entry != NULL &&
1365                         (is_connecting(info->entry->state) == TRUE ||
1366                                 is_connected(info->entry->state) == TRUE)) {
1367                         break;
1368                 }
1369
1370                 deselect_and_disconnect(session);
1371
1372                 if (info->reason == CONNMAN_SESSION_REASON_FREE_RIDE) {
1373                         select_and_connect(session, info->reason);
1374                 }
1375
1376                 break;
1377         }
1378
1379         session_notify(session);
1380 }
1381
1382 int connman_session_config_update(struct connman_session *session)
1383 {
1384         struct session_info *info = session->info;
1385         GSList *allowed_bearers;
1386         int err;
1387
1388         DBG("session %p", session);
1389
1390         /*
1391          * We update all configuration even though only one entry
1392          * might have changed. We can still optimize this later.
1393          */
1394
1395         err = apply_policy_on_bearers(
1396                 session->policy_config->allowed_bearers,
1397                 session->user_allowed_bearers,
1398                 &allowed_bearers);
1399         if (err < 0)
1400                 return err;
1401
1402         g_slist_free(info->config.allowed_bearers);
1403         info->config.allowed_bearers = allowed_bearers;
1404
1405         info->config.type = apply_policy_on_type(
1406                                 session->policy_config->type,
1407                                 info->config.type);
1408
1409         info->config.roaming_policy = session->policy_config->roaming_policy;
1410
1411         info->config.ecall = session->policy_config->ecall;
1412         if (info->config.ecall == TRUE)
1413                 ecall_session = session;
1414
1415         info->config.priority = session->policy_config->priority;
1416
1417         session_changed(session, CONNMAN_SESSION_TRIGGER_SETTING);
1418
1419         return 0;
1420 }
1421
1422 static DBusMessage *connect_session(DBusConnection *conn,
1423                                         DBusMessage *msg, void *user_data)
1424 {
1425         struct connman_session *session = user_data;
1426
1427         DBG("session %p", session);
1428
1429         if (ecall_session != NULL) {
1430                 if (ecall_session->ecall == TRUE && ecall_session != session)
1431                         return __connman_error_failed(msg, EBUSY);
1432
1433                 session->ecall = TRUE;
1434                 session_changed(session, CONNMAN_SESSION_TRIGGER_ECALL);
1435         } else
1436                 session_changed(session, CONNMAN_SESSION_TRIGGER_CONNECT);
1437
1438         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
1439 }
1440
1441 static DBusMessage *disconnect_session(DBusConnection *conn,
1442                                         DBusMessage *msg, void *user_data)
1443 {
1444         struct connman_session *session = user_data;
1445
1446         DBG("session %p", session);
1447
1448         if (ecall_session != NULL) {
1449                 if (ecall_session->ecall == TRUE && ecall_session != session)
1450                         return __connman_error_failed(msg, EBUSY);
1451
1452                 session->ecall = FALSE;
1453         }
1454
1455         session_changed(session, CONNMAN_SESSION_TRIGGER_DISCONNECT);
1456
1457         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
1458 }
1459
1460 static DBusMessage *change_session(DBusConnection *conn,
1461                                         DBusMessage *msg, void *user_data)
1462 {
1463         struct connman_session *session = user_data;
1464         struct session_info *info = session->info;
1465         DBusMessageIter iter, value;
1466         const char *name;
1467         const char *val;
1468         GSList *allowed_bearers;
1469         int err;
1470
1471         DBG("session %p", session);
1472         if (dbus_message_iter_init(msg, &iter) == FALSE)
1473                 return __connman_error_invalid_arguments(msg);
1474
1475         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
1476                 return __connman_error_invalid_arguments(msg);
1477
1478         dbus_message_iter_get_basic(&iter, &name);
1479         dbus_message_iter_next(&iter);
1480
1481         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
1482                 return __connman_error_invalid_arguments(msg);
1483
1484         dbus_message_iter_recurse(&iter, &value);
1485
1486         switch (dbus_message_iter_get_arg_type(&value)) {
1487         case DBUS_TYPE_ARRAY:
1488                 if (g_str_equal(name, "AllowedBearers") == TRUE) {
1489                         err = parse_bearers(&value, &allowed_bearers);
1490                         if (err < 0)
1491                                 return __connman_error_failed(msg, err);
1492
1493                         g_slist_free(info->config.allowed_bearers);
1494                         session->user_allowed_bearers = allowed_bearers;
1495
1496                         err = apply_policy_on_bearers(
1497                                         session->policy_config->allowed_bearers,
1498                                         session->user_allowed_bearers,
1499                                         &info->config.allowed_bearers);
1500
1501                         if (err < 0)
1502                                 return __connman_error_failed(msg, err);
1503                 } else {
1504                         goto err;
1505                 }
1506                 break;
1507         case DBUS_TYPE_STRING:
1508                 if (g_str_equal(name, "ConnectionType") == TRUE) {
1509                         dbus_message_iter_get_basic(&value, &val);
1510                         info->config.type = apply_policy_on_type(
1511                                 session->policy_config->type,
1512                                 connman_session_parse_connection_type(val));
1513                 } else {
1514                         goto err;
1515                 }
1516                 break;
1517         default:
1518                 goto err;
1519         }
1520
1521         session_changed(session, CONNMAN_SESSION_TRIGGER_SETTING);
1522
1523         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
1524
1525 err:
1526         return __connman_error_invalid_arguments(msg);
1527 }
1528
1529 static void release_session(gpointer key, gpointer value, gpointer user_data)
1530 {
1531         struct connman_session *session = value;
1532         DBusMessage *message;
1533
1534         DBG("owner %s path %s", session->owner, session->notify_path);
1535
1536         if (session->notify_watch > 0)
1537                 g_dbus_remove_watch(connection, session->notify_watch);
1538
1539         g_dbus_unregister_interface(connection, session->session_path,
1540                                                 CONNMAN_SESSION_INTERFACE);
1541
1542         message = dbus_message_new_method_call(session->owner,
1543                                                 session->notify_path,
1544                                                 CONNMAN_NOTIFICATION_INTERFACE,
1545                                                 "Release");
1546         if (message == NULL)
1547                 return;
1548
1549         dbus_message_set_no_reply(message, TRUE);
1550
1551         g_dbus_send_message(connection, message);
1552 }
1553
1554 static int session_disconnect(struct connman_session *session)
1555 {
1556         DBG("session %p, %s", session, session->owner);
1557
1558         if (session->notify_watch > 0)
1559                 g_dbus_remove_watch(connection, session->notify_watch);
1560
1561         g_dbus_unregister_interface(connection, session->session_path,
1562                                                 CONNMAN_SESSION_INTERFACE);
1563
1564         deselect_and_disconnect(session);
1565
1566         g_hash_table_remove(session_hash, session->session_path);
1567
1568         return 0;
1569 }
1570
1571 static void owner_disconnect(DBusConnection *conn, void *user_data)
1572 {
1573         struct connman_session *session = user_data;
1574
1575         DBG("session %p, %s died", session, session->owner);
1576
1577         session_disconnect(session);
1578 }
1579
1580 static DBusMessage *destroy_session(DBusConnection *conn,
1581                                         DBusMessage *msg, void *user_data)
1582 {
1583         struct connman_session *session = user_data;
1584
1585         DBG("session %p", session);
1586
1587         if (ecall_session != NULL && ecall_session != session)
1588                 return __connman_error_failed(msg, EBUSY);
1589
1590         session_disconnect(session);
1591
1592         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
1593 }
1594
1595 static const GDBusMethodTable session_methods[] = {
1596         { GDBUS_METHOD("Destroy", NULL, NULL, destroy_session) },
1597         { GDBUS_METHOD("Connect", NULL, NULL, connect_session) },
1598         { GDBUS_METHOD("Disconnect", NULL, NULL,
1599                         disconnect_session ) },
1600         { GDBUS_METHOD("Change",
1601                         GDBUS_ARGS({ "name", "s" }, { "value", "v" }),
1602                         NULL, change_session) },
1603         { },
1604 };
1605
1606 struct user_config {
1607         DBusMessage *pending;
1608
1609         enum connman_session_type type;
1610         GSList *allowed_bearers;
1611 };
1612
1613 static void session_create_cb(struct connman_session *session,
1614                                 struct connman_session_config *config,
1615                                 void *user_data, int err)
1616 {
1617         DBusMessage *reply;
1618         struct user_config *user_config = user_data;
1619         struct session_info *info, *info_last;
1620
1621         DBG("session %p config %p", session, config);
1622
1623         if (err != 0)
1624                 goto out;
1625
1626         session->policy_config = config;
1627
1628         info = session->info;
1629         info_last = session->info_last;
1630
1631         if (session->policy_config->ecall == TRUE)
1632                 ecall_session = session;
1633
1634         info->state = CONNMAN_SESSION_STATE_DISCONNECTED;
1635         info->config.type = apply_policy_on_type(
1636                                 session->policy_config->type,
1637                                 user_config->type);
1638         info->config.priority = session->policy_config->priority;
1639         info->config.roaming_policy = session->policy_config->roaming_policy;
1640         info->entry = NULL;
1641
1642         session->user_allowed_bearers = user_config->allowed_bearers;
1643
1644         err = apply_policy_on_bearers(
1645                         session->policy_config->allowed_bearers,
1646                         session->user_allowed_bearers,
1647                         &info->config.allowed_bearers);
1648         if (err < 0)
1649                 goto out;
1650
1651         g_hash_table_replace(session_hash, session->session_path, session);
1652
1653         DBG("add %s", session->session_path);
1654
1655         if (g_dbus_register_interface(connection, session->session_path,
1656                                         CONNMAN_SESSION_INTERFACE,
1657                                         session_methods, NULL,
1658                                         NULL, session, NULL) == FALSE) {
1659                 connman_error("Failed to register %s", session->session_path);
1660                 g_hash_table_remove(session_hash, session->session_path);
1661                 err = -EINVAL;
1662                 goto out;
1663         }
1664
1665         reply = g_dbus_create_reply(user_config->pending,
1666                                 DBUS_TYPE_OBJECT_PATH, &session->session_path,
1667                                 DBUS_TYPE_INVALID);
1668         g_dbus_send_message(connection, reply);
1669
1670         populate_service_list(session);
1671
1672         info_last->state = info->state;
1673         info_last->config.priority = info->config.priority;
1674         info_last->config.roaming_policy = info->config.roaming_policy;
1675         info_last->entry = info->entry;
1676         info_last->config.allowed_bearers = info->config.allowed_bearers;
1677
1678         session->append_all = TRUE;
1679
1680         session_changed(session, CONNMAN_SESSION_TRIGGER_SETTING);
1681
1682 out:
1683         if (err < 0) {
1684                 __connman_error_failed(user_config->pending, err);
1685                 free_session(session);
1686         }
1687
1688         dbus_message_unref(user_config->pending);
1689         g_free(user_config);
1690 }
1691
1692 int __connman_session_create(DBusMessage *msg)
1693 {
1694         const char *owner, *notify_path;
1695         char *session_path = NULL;
1696         DBusMessageIter iter, array;
1697         struct connman_session *session = NULL;
1698         struct user_config *user_config = NULL;
1699         connman_bool_t user_allowed_bearers = FALSE;
1700         connman_bool_t user_connection_type = FALSE;
1701         int err;
1702
1703         owner = dbus_message_get_sender(msg);
1704
1705         DBG("owner %s", owner);
1706
1707         if (ecall_session != NULL && ecall_session->ecall == TRUE) {
1708                 /*
1709                  * If there is an emergency call already going on,
1710                  * ignore session creation attempt
1711                  */
1712                 err = -EBUSY;
1713                 goto err;
1714         }
1715
1716         user_config = g_try_new0(struct user_config, 1);
1717         if (user_config == NULL) {
1718                 err = -ENOMEM;
1719                 goto err;
1720         }
1721
1722         user_config->pending = dbus_message_ref(msg);
1723
1724         dbus_message_iter_init(msg, &iter);
1725         dbus_message_iter_recurse(&iter, &array);
1726
1727         while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) {
1728                 DBusMessageIter entry, value;
1729                 const char *key, *val;
1730
1731                 dbus_message_iter_recurse(&array, &entry);
1732                 dbus_message_iter_get_basic(&entry, &key);
1733
1734                 dbus_message_iter_next(&entry);
1735                 dbus_message_iter_recurse(&entry, &value);
1736
1737                 switch (dbus_message_iter_get_arg_type(&value)) {
1738                 case DBUS_TYPE_ARRAY:
1739                         if (g_str_equal(key, "AllowedBearers") == TRUE) {
1740                                 err = parse_bearers(&value,
1741                                                 &user_config->allowed_bearers);
1742                                 if (err < 0)
1743                                         goto err;
1744
1745                                 user_allowed_bearers = TRUE;
1746                         } else {
1747                                 err = -EINVAL;
1748                                 goto err;
1749                         }
1750                         break;
1751                 case DBUS_TYPE_STRING:
1752                         if (g_str_equal(key, "ConnectionType") == TRUE) {
1753                                 dbus_message_iter_get_basic(&value, &val);
1754                                 user_config->type =
1755                                         connman_session_parse_connection_type(val);
1756
1757                                 user_connection_type = TRUE;
1758                         } else {
1759                                 err = -EINVAL;
1760                                 goto err;
1761                         }
1762                 }
1763                 dbus_message_iter_next(&array);
1764         }
1765
1766         /*
1767          * If the user hasn't provided a configuration, we set
1768          * the default configuration.
1769          *
1770          * For AllowedBearers this is '*', ...
1771          */
1772         if (user_allowed_bearers == FALSE) {
1773                 user_config->allowed_bearers =
1774                         g_slist_append(NULL,
1775                                 GINT_TO_POINTER(CONNMAN_SERVICE_TYPE_UNKNOWN));
1776                 if (user_config->allowed_bearers == NULL) {
1777                         err = -ENOMEM;
1778                         goto err;
1779                 }
1780         }
1781
1782         /* ... and for ConnectionType it is 'any'. */
1783         if (user_connection_type == FALSE)
1784                 user_config->type = CONNMAN_SESSION_TYPE_ANY;
1785
1786         dbus_message_iter_next(&iter);
1787         dbus_message_iter_get_basic(&iter, &notify_path);
1788
1789         if (notify_path == NULL) {
1790                 err = -EINVAL;
1791                 goto err;
1792         }
1793
1794         session_path = g_strdup_printf("/sessions%s", notify_path);
1795         if (session_path == NULL) {
1796                 err = -ENOMEM;
1797                 goto err;
1798         }
1799
1800         session = g_hash_table_lookup(session_hash, session_path);
1801         if (session != NULL) {
1802                 g_free(session_path);
1803                 session = NULL;
1804                 err = -EEXIST;
1805                 goto err;
1806         }
1807
1808         session = g_try_new0(struct connman_session, 1);
1809         if (session == NULL) {
1810                 g_free(session_path);
1811                 err = -ENOMEM;
1812                 goto err;
1813         }
1814
1815         session->session_path = session_path;
1816
1817         session->info = g_try_new0(struct session_info, 1);
1818         if (session->info == NULL) {
1819                 err = -ENOMEM;
1820                 goto err;
1821         }
1822
1823         session->info_last = g_try_new0(struct session_info, 1);
1824         if (session->info_last == NULL) {
1825                 err = -ENOMEM;
1826                 goto err;
1827         }
1828
1829         session->owner = g_strdup(owner);
1830         session->notify_path = g_strdup(notify_path);
1831         session->notify_watch =
1832                 g_dbus_add_disconnect_watch(connection, session->owner,
1833                                         owner_disconnect, session, NULL);
1834
1835         err = assign_policy_plugin(session);
1836         if (err < 0)
1837                 goto err;
1838
1839         err = create_policy_config(session, session_create_cb, user_config);
1840         if (err < 0)
1841                 goto err;
1842
1843         return -EINPROGRESS;
1844
1845 err:
1846         connman_error("Failed to create session");
1847
1848         free_session(session);
1849
1850         if (user_config != NULL) {
1851                 dbus_message_unref(user_config->pending);
1852                 g_slist_free(user_config->allowed_bearers);
1853                 g_free(user_config);
1854         }
1855         return err;
1856 }
1857
1858 void connman_session_destroy(struct connman_session *session)
1859 {
1860         DBG("session %p", session);
1861
1862         session_disconnect(session);
1863 }
1864
1865 int __connman_session_destroy(DBusMessage *msg)
1866 {
1867         const char *owner, *session_path;
1868         struct connman_session *session;
1869
1870         owner = dbus_message_get_sender(msg);
1871
1872         DBG("owner %s", owner);
1873
1874         dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &session_path,
1875                                                         DBUS_TYPE_INVALID);
1876         if (session_path == NULL)
1877                 return -EINVAL;
1878
1879         session = g_hash_table_lookup(session_hash, session_path);
1880         if (session == NULL)
1881                 return -EINVAL;
1882
1883         if (g_strcmp0(owner, session->owner) != 0)
1884                 return -EACCES;
1885
1886         connman_session_destroy(session);
1887
1888         return 0;
1889 }
1890
1891 connman_bool_t __connman_session_mode()
1892 {
1893         return sessionmode;
1894 }
1895
1896 void __connman_session_set_mode(connman_bool_t enable)
1897 {
1898         DBG("enable %d", enable);
1899
1900         if (sessionmode != enable) {
1901                 sessionmode = enable;
1902
1903                 connman_dbus_property_changed_basic(CONNMAN_MANAGER_PATH,
1904                                 CONNMAN_MANAGER_INTERFACE, "SessionMode",
1905                                 DBUS_TYPE_BOOLEAN, &sessionmode);
1906         }
1907
1908         if (sessionmode == TRUE)
1909                 __connman_service_disconnect_all();
1910 }
1911
1912 static void service_add(struct connman_service *service,
1913                         const char *name)
1914 {
1915         GHashTableIter iter;
1916         GSequenceIter *iter_service_list;
1917         gpointer key, value;
1918         struct connman_session *session;
1919         struct service_entry *entry;
1920
1921         DBG("service %p", service);
1922
1923         g_hash_table_iter_init(&iter, session_hash);
1924
1925         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
1926                 session = value;
1927
1928                 if (service_match(session, service) == FALSE)
1929                         continue;
1930
1931                 entry = create_service_entry(service, name,
1932                                                 CONNMAN_SERVICE_STATE_IDLE);
1933                 if (entry == NULL)
1934                         continue;
1935
1936                 iter_service_list =
1937                         g_sequence_insert_sorted(session->service_list,
1938                                                         entry, sort_services,
1939                                                         session);
1940
1941                 g_hash_table_replace(session->service_hash, service,
1942                                         iter_service_list);
1943
1944                 session_changed(session, CONNMAN_SESSION_TRIGGER_SERVICE);
1945         }
1946 }
1947
1948 static void service_remove(struct connman_service *service)
1949 {
1950
1951         GHashTableIter iter;
1952         gpointer key, value;
1953         struct connman_session *session;
1954         struct session_info *info;
1955
1956         DBG("service %p", service);
1957
1958         g_hash_table_iter_init(&iter, session_hash);
1959
1960         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
1961                 GSequenceIter *seq_iter;
1962                 session = value;
1963                 info = session->info;
1964
1965                 seq_iter = g_hash_table_lookup(session->service_hash, service);
1966                 if (seq_iter == NULL)
1967                         continue;
1968
1969                 g_sequence_remove(seq_iter);
1970
1971                 if (info->entry != NULL && info->entry->service == service)
1972                         info->entry = NULL;
1973                 session_changed(session, CONNMAN_SESSION_TRIGGER_SERVICE);
1974         }
1975 }
1976
1977 static void service_state_changed(struct connman_service *service,
1978                                         enum connman_service_state state)
1979 {
1980         GHashTableIter iter;
1981         gpointer key, value;
1982
1983         DBG("service %p state %d", service, state);
1984
1985         g_hash_table_iter_init(&iter, session_hash);
1986
1987         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
1988                 struct connman_session *session = value;
1989                 GSequenceIter *service_iter;
1990
1991                 service_iter = g_hash_table_lookup(session->service_hash, service);
1992                 if (service_iter != NULL) {
1993                         struct service_entry *entry;
1994
1995                         entry = g_sequence_get(service_iter);
1996                         entry->state = state;
1997                 }
1998
1999                 session_changed(session,
2000                                 CONNMAN_SESSION_TRIGGER_SERVICE);
2001         }
2002 }
2003
2004 static void ipconfig_changed(struct connman_service *service,
2005                                 struct connman_ipconfig *ipconfig)
2006 {
2007         GHashTableIter iter;
2008         gpointer key, value;
2009         struct connman_session *session;
2010         struct session_info *info;
2011         enum connman_ipconfig_type type;
2012
2013         DBG("service %p ipconfig %p", service, ipconfig);
2014
2015         type = __connman_ipconfig_get_config_type(ipconfig);
2016
2017         g_hash_table_iter_init(&iter, session_hash);
2018
2019         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
2020                 session = value;
2021                 info = session->info;
2022
2023                 if (info->state == CONNMAN_SESSION_STATE_DISCONNECTED)
2024                         continue;
2025
2026                 if (info->entry != NULL && info->entry->service == service) {
2027                         if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
2028                                 ipconfig_ipv4_changed(session);
2029                         else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
2030                                 ipconfig_ipv6_changed(session);
2031                 }
2032         }
2033 }
2034
2035 static struct connman_notifier session_notifier = {
2036         .name                   = "session",
2037         .service_add            = service_add,
2038         .service_remove         = service_remove,
2039         .service_state_changed  = service_state_changed,
2040         .ipconfig_changed       = ipconfig_changed,
2041 };
2042
2043 int __connman_session_init(void)
2044 {
2045         int err;
2046
2047         DBG("");
2048
2049         connection = connman_dbus_get_connection();
2050         if (connection == NULL)
2051                 return -1;
2052
2053         err = connman_notifier_register(&session_notifier);
2054         if (err < 0) {
2055                 dbus_connection_unref(connection);
2056                 return err;
2057         }
2058
2059         session_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
2060                                                 NULL, cleanup_session);
2061
2062         sessionmode = FALSE;
2063         return 0;
2064 }
2065
2066 void __connman_session_cleanup(void)
2067 {
2068         DBG("");
2069
2070         if (connection == NULL)
2071                 return;
2072
2073         connman_notifier_unregister(&session_notifier);
2074
2075         g_hash_table_foreach(session_hash, release_session, NULL);
2076         g_hash_table_destroy(session_hash);
2077         session_hash = NULL;
2078
2079         dbus_connection_unref(connection);
2080 }