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