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