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