session: Keep a copy of last settings
[framework/connectivity/connman.git] / src / session.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2010  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 <gdbus.h>
28
29 #include "connman.h"
30
31 static DBusConnection *connection;
32 static GHashTable *session_hash;
33 static connman_bool_t sessionmode;
34
35 enum connman_session_roaming_policy {
36         CONNMAN_SESSION_ROAMING_POLICY_UNKNOWN          = 0,
37         CONNMAN_SESSION_ROAMING_POLICY_DEFAULT          = 1,
38         CONNMAN_SESSION_ROAMING_POLICY_ALWAYS           = 2,
39         CONNMAN_SESSION_ROAMING_POLICY_FORBIDDEN        = 3,
40         CONNMAN_SESSION_ROAMING_POLICY_NATIONAL         = 4,
41         CONNMAN_SESSION_ROAMING_POLICY_INTERNATIONAL    = 5,
42 };
43
44 struct session_info {
45         char *bearer;
46         const char *name;
47         char *ifname;
48         connman_bool_t connect;
49         connman_bool_t online;
50         connman_bool_t priority;
51         GSList *allowed_bearers;
52         connman_bool_t avoid_handover;
53         connman_bool_t stay_connected;
54         unsigned int periodic_connect;
55         unsigned int idle_timeout;
56         connman_bool_t ecall;
57         enum connman_session_roaming_policy roaming_policy;
58         unsigned int marker;
59
60         struct connman_service *service;
61 };
62
63 struct connman_session {
64         char *owner;
65         char *session_path;
66         char *notify_path;
67         guint notify_watch;
68
69         struct session_info info;
70         struct session_info info_last;
71
72         GSequence *service_list;
73 };
74
75 struct bearer_info {
76         char *name;
77         connman_bool_t match_all;
78         enum connman_service_type service_type;
79 };
80
81 static const char *roamingpolicy2string(enum connman_session_roaming_policy policy)
82 {
83         switch (policy) {
84         case CONNMAN_SESSION_ROAMING_POLICY_UNKNOWN:
85                 break;
86         case CONNMAN_SESSION_ROAMING_POLICY_DEFAULT:
87                 return "default";
88         case CONNMAN_SESSION_ROAMING_POLICY_ALWAYS:
89                 return "always";
90         case CONNMAN_SESSION_ROAMING_POLICY_FORBIDDEN:
91                 return "forbidden";
92         case CONNMAN_SESSION_ROAMING_POLICY_NATIONAL:
93                 return "national";
94         case CONNMAN_SESSION_ROAMING_POLICY_INTERNATIONAL:
95                 return "international";
96         }
97
98         return NULL;
99 }
100
101 static enum connman_session_roaming_policy string2roamingpolicy(const char *policy)
102 {
103         if (g_strcmp0(policy, "default") == 0)
104                 return CONNMAN_SESSION_ROAMING_POLICY_DEFAULT;
105         else if (g_strcmp0(policy, "always") == 0)
106                 return CONNMAN_SESSION_ROAMING_POLICY_ALWAYS;
107         else if (g_strcmp0(policy, "forbidden") == 0)
108                 return CONNMAN_SESSION_ROAMING_POLICY_FORBIDDEN;
109         else if (g_strcmp0(policy, "national") == 0)
110                 return CONNMAN_SESSION_ROAMING_POLICY_NATIONAL;
111         else if (g_strcmp0(policy, "international") == 0)
112                 return CONNMAN_SESSION_ROAMING_POLICY_INTERNATIONAL;
113         else
114                 return CONNMAN_SESSION_ROAMING_POLICY_UNKNOWN;
115 }
116
117 static enum connman_service_type bearer2service(const char *bearer)
118 {
119         if (bearer == NULL)
120                 return CONNMAN_SERVICE_TYPE_UNKNOWN;
121
122         if (g_strcmp0(bearer, "ethernet") == 0)
123                 return CONNMAN_SERVICE_TYPE_ETHERNET;
124         else if (g_strcmp0(bearer, "wifi") == 0)
125                 return CONNMAN_SERVICE_TYPE_WIFI;
126         else if (g_strcmp0(bearer, "wimax") == 0)
127                 return CONNMAN_SERVICE_TYPE_WIMAX;
128         else if (g_strcmp0(bearer, "bluetooth") == 0)
129                 return CONNMAN_SERVICE_TYPE_BLUETOOTH;
130         else if (g_strcmp0(bearer, "3g") == 0)
131                 return CONNMAN_SERVICE_TYPE_CELLULAR;
132         else
133                 return CONNMAN_SERVICE_TYPE_UNKNOWN;
134 }
135
136 static char *service2bearer(enum connman_service_type type)
137 {
138         switch (type) {
139         case CONNMAN_SERVICE_TYPE_ETHERNET:
140                 return "ethernet";
141         case CONNMAN_SERVICE_TYPE_WIFI:
142                 return "wifi";
143         case CONNMAN_SERVICE_TYPE_WIMAX:
144                 return "wimax";
145         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
146                 return "bluetooth";
147         case CONNMAN_SERVICE_TYPE_CELLULAR:
148                 return "3g";
149         case CONNMAN_SERVICE_TYPE_UNKNOWN:
150         case CONNMAN_SERVICE_TYPE_SYSTEM:
151         case CONNMAN_SERVICE_TYPE_GPS:
152         case CONNMAN_SERVICE_TYPE_VPN:
153         case CONNMAN_SERVICE_TYPE_GADGET:
154                 return "";
155         }
156
157         return "";
158 }
159
160 static char *session2bearer(struct connman_session *session)
161 {
162         struct session_info *info = &session->info;
163         GSList *list;
164         struct bearer_info *bearer_info;
165         enum connman_service_type type;
166
167         if (info->service == NULL)
168                 return "";
169
170         type = connman_service_get_type(info->service);
171
172         for (list = info->allowed_bearers;
173                         list != NULL; list = list->next) {
174                 bearer_info = list->data;
175
176                 if (bearer_info->match_all)
177                         return service2bearer(type);
178
179                 if (bearer_info->service_type == CONNMAN_SERVICE_TYPE_UNKNOWN)
180                         return bearer_info->name;
181
182                 if (bearer_info->service_type == type)
183                         return service2bearer(type);
184         }
185
186         return "";
187
188 }
189
190 static void cleanup_bearer_info(gpointer data, gpointer user_data)
191 {
192         struct bearer_info *info = data;
193
194         g_free(info->name);
195         g_free(info);
196 }
197
198 static GSList *session_parse_allowed_bearers(DBusMessageIter *iter)
199 {
200         struct bearer_info *info;
201         DBusMessageIter array;
202         GSList *list = NULL;
203
204         dbus_message_iter_recurse(iter, &array);
205
206         while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING) {
207                 char *bearer = NULL;
208
209                 dbus_message_iter_get_basic(&array, &bearer);
210
211                 info = g_try_new0(struct bearer_info, 1);
212                 if (info == NULL) {
213                         g_slist_foreach(list, cleanup_bearer_info, NULL);
214                         g_slist_free(list);
215
216                         return NULL;
217                 }
218
219                 info->name = g_strdup(bearer);
220                 info->service_type = bearer2service(info->name);
221
222                 if (info->service_type == CONNMAN_SERVICE_TYPE_UNKNOWN &&
223                                 g_strcmp0(info->name, "*") == 0) {
224                         info->match_all = TRUE;
225                 } else {
226                         info->match_all = FALSE;
227                 }
228
229                 list = g_slist_append(list, info);
230
231                 dbus_message_iter_next(&array);
232         }
233
234         return list;
235 }
236
237 static GSList *session_allowed_bearers_any(void)
238 {
239         struct bearer_info *info;
240         GSList *list = NULL;
241
242         info = g_try_new0(struct bearer_info, 1);
243         if (info == NULL) {
244                 g_slist_free(list);
245
246                 return NULL;
247         }
248
249         info->name = g_strdup("");
250         info->match_all = TRUE;
251         info->service_type = CONNMAN_SERVICE_TYPE_UNKNOWN;
252
253         list = g_slist_append(list, info);
254
255         return list;
256 }
257
258 static void append_allowed_bearers(DBusMessageIter *iter, void *user_data)
259 {
260         struct session_info *info = user_data;
261         GSList *list;
262
263         for (list = info->allowed_bearers;
264                         list != NULL; list = list->next) {
265                 struct bearer_info *info = list->data;
266
267                 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
268                                                 &info->name);
269         }
270 }
271
272 static void append_ipconfig_ipv4(DBusMessageIter *iter, void *user_data)
273 {
274         struct connman_service *service = user_data;
275         struct connman_ipconfig *ipconfig_ipv4;
276
277         if (service == NULL)
278                 return;
279
280         ipconfig_ipv4 = __connman_service_get_ip4config(service);
281         if (ipconfig_ipv4 == NULL)
282                 return;
283
284         __connman_ipconfig_append_ipv4(ipconfig_ipv4, iter);
285 }
286
287 static void append_ipconfig_ipv6(DBusMessageIter *iter, void *user_data)
288 {
289         struct connman_service *service = user_data;
290         struct connman_ipconfig *ipconfig_ipv4, *ipconfig_ipv6;
291
292         if (service == NULL)
293                 return;
294
295         ipconfig_ipv4 = __connman_service_get_ip4config(service);
296         ipconfig_ipv6 = __connman_service_get_ip6config(service);
297         if (ipconfig_ipv6 == NULL)
298                 return;
299
300         __connman_ipconfig_append_ipv6(ipconfig_ipv6, iter, ipconfig_ipv4);
301 }
302
303 static void append_notify_all(DBusMessageIter *dict,
304                                         struct connman_session *session)
305 {
306         struct session_info *info = &session->info;
307         struct session_info *info_last = &session->info_last;
308         const char *policy;
309
310         if (info->bearer != info_last->bearer) {
311                 connman_dbus_dict_append_basic(dict, "Bearer",
312                                                 DBUS_TYPE_STRING,
313                                                 &info->bearer);
314                 info_last->bearer = info->bearer;
315         }
316
317         if (info->online != info_last->online) {
318                 connman_dbus_dict_append_basic(dict, "Online",
319                                                 DBUS_TYPE_BOOLEAN,
320                                                 &info->online);
321                 info_last->online = info->online;
322         }
323
324         if (info->name != info_last->name) {
325                 connman_dbus_dict_append_basic(dict, "Name",
326                                                 DBUS_TYPE_STRING,
327                                                 &info->name);
328                 info_last->name = info->name;
329         }
330
331         if (info->service != info_last->service) {
332                 connman_dbus_dict_append_dict(dict, "IPv4",
333                                                 append_ipconfig_ipv4,
334                                                 info->service);
335
336                 connman_dbus_dict_append_dict(dict, "IPv6",
337                                                 append_ipconfig_ipv6,
338                                                 info->service);
339
340                 connman_dbus_dict_append_basic(dict, "Interface",
341                                                 DBUS_TYPE_STRING,
342                                                 &info->ifname);
343
344                 info_last->service = info->service;
345         }
346
347
348         if (info->priority != info_last->priority) {
349                 connman_dbus_dict_append_basic(dict, "Priority",
350                                                 DBUS_TYPE_BOOLEAN,
351                                                 &info->priority);
352                 info_last->priority = info->priority;
353         }
354
355         if (info->allowed_bearers != info_last->allowed_bearers) {
356                 connman_dbus_dict_append_array(dict, "AllowedBearers",
357                                                 DBUS_TYPE_STRING,
358                                                 append_allowed_bearers,
359                                                 info);
360                 info_last->allowed_bearers = info->allowed_bearers;
361         }
362
363         if (info->avoid_handover != info_last->avoid_handover) {
364                 connman_dbus_dict_append_basic(dict, "AvoidHandover",
365                                                 DBUS_TYPE_BOOLEAN,
366                                                 &info->avoid_handover);
367                 info_last->avoid_handover = info->avoid_handover;
368         }
369
370         if (info->stay_connected != info_last->stay_connected) {
371                 connman_dbus_dict_append_basic(dict, "StayConnected",
372                                                 DBUS_TYPE_BOOLEAN,
373                                                 &info->stay_connected);
374                 info_last->stay_connected = info->stay_connected;
375         }
376
377         if (info->periodic_connect != info_last->periodic_connect) {
378                 connman_dbus_dict_append_basic(dict, "PeriodicConnect",
379                                                 DBUS_TYPE_UINT32,
380                                                 &info->periodic_connect);
381                 info_last->periodic_connect = info->periodic_connect;
382         }
383
384         if (info->idle_timeout != info_last->idle_timeout) {
385                 connman_dbus_dict_append_basic(dict, "IdleTimeout",
386                                                 DBUS_TYPE_UINT32,
387                                                 &info->idle_timeout);
388                 info_last->idle_timeout = info->idle_timeout;
389         }
390
391         if (info->ecall != info_last->ecall) {
392                 connman_dbus_dict_append_basic(dict, "EmergencyCall",
393                                                 DBUS_TYPE_BOOLEAN,
394                                                 &info->ecall);
395                 info_last->ecall = info->ecall;
396         }
397
398         if (info->roaming_policy != info_last->roaming_policy) {
399                 policy = roamingpolicy2string(info->roaming_policy);
400                 connman_dbus_dict_append_basic(dict, "RoamingPolicy",
401                                                 DBUS_TYPE_STRING,
402                                                 &policy);
403                 info_last->roaming_policy = info->roaming_policy;
404         }
405
406         if (info->marker != info_last->marker) {
407                 connman_dbus_dict_append_basic(dict, "SessionMarker",
408                                                 DBUS_TYPE_UINT32,
409                                                 &info->marker);
410                 info_last->marker = info->marker;
411         }
412 }
413
414 static gboolean session_notify_all(gpointer user_data)
415 {
416         struct connman_session *session = user_data;
417         DBusMessage *msg;
418         DBusMessageIter array, dict;
419
420         DBG("session %p owner %s notify_path %s", session,
421                 session->owner, session->notify_path);
422
423         msg = dbus_message_new_method_call(session->owner, session->notify_path,
424                                                 CONNMAN_NOTIFICATION_INTERFACE,
425                                                 "Update");
426         if (msg == NULL) {
427                 connman_error("Could not create notification message");
428                 return FALSE;
429         }
430
431         dbus_message_iter_init_append(msg, &array);
432         connman_dbus_dict_open(&array, &dict);
433
434         append_notify_all(&dict, session);
435
436         connman_dbus_dict_close(&array, &dict);
437
438         g_dbus_send_message(connection, msg);
439
440         return FALSE;
441 }
442
443 static gboolean service_changed(gpointer user_data)
444 {
445         struct connman_session *session = user_data;
446
447
448         DBusMessage *msg;
449         DBusMessageIter array, dict;
450
451         DBG("session %p owner %s notify_path %s", session,
452                 session->owner, session->notify_path);
453
454         msg = dbus_message_new_method_call(session->owner, session->notify_path,
455                                                 CONNMAN_NOTIFICATION_INTERFACE,
456                                                 "Update");
457         if (msg == NULL) {
458                 connman_error("Could not create notification message");
459                 return FALSE;
460         }
461
462         dbus_message_iter_init_append(msg, &array);
463         connman_dbus_dict_open(&array, &dict);
464
465         append_notify_all(&dict, session);
466
467         connman_dbus_dict_close(&array, &dict);
468
469         g_dbus_send_message(connection, msg);
470
471         return FALSE;
472 }
473
474 static void online_changed(struct connman_session *session)
475 {
476         struct session_info *info = &session->info;
477
478         connman_dbus_setting_changed_basic(session->owner, session->notify_path,
479                                                 "Online", DBUS_TYPE_BOOLEAN,
480                                                 &info->online);
481 }
482
483 static void ipconfig_ipv4_changed(struct connman_session *session)
484 {
485         struct session_info *info = &session->info;
486
487         connman_dbus_setting_changed_dict(session->owner, session->notify_path,
488                                                 "IPv4", append_ipconfig_ipv4,
489                                                 info->service);
490 }
491
492 static void ipconfig_ipv6_changed(struct connman_session *session)
493 {
494         struct session_info *info = &session->info;
495
496         connman_dbus_setting_changed_dict(session->owner, session->notify_path,
497                                                 "IPv6", append_ipconfig_ipv6,
498                                                 info->service);
499 }
500
501 static void update_service(struct connman_session *session)
502 {
503         struct session_info *info = &session->info;
504         int idx;
505
506         if (info->service != NULL) {
507                 info->bearer = session2bearer(session);
508                 info->online = __connman_service_is_connected(info->service);
509                 info->name = __connman_service_get_name(info->service);
510                 idx = __connman_service_get_index(info->service);
511                 info->ifname = connman_inet_ifname(idx);
512
513         } else {
514                 info->bearer = "";
515                 info->online = FALSE;
516                 info->name = "";
517                 info->ifname = "";
518         }
519 }
520
521 static connman_bool_t service_type_match(struct connman_session *session,
522                                         struct connman_service *service)
523 {
524         struct session_info *info = &session->info;
525         GSList *list;
526
527         for (list = info->allowed_bearers;
528                         list != NULL; list = list->next) {
529                 struct bearer_info *info = list->data;
530                 enum connman_service_type service_type;
531
532                 if (info->match_all == TRUE)
533                         return TRUE;
534
535                 service_type = connman_service_get_type(service);
536                 if (info->service_type == service_type)
537                         return TRUE;
538         }
539
540         return FALSE;
541 }
542
543 static connman_bool_t service_match(struct connman_session *session,
544                                         struct connman_service *service)
545 {
546         if (service_type_match(session, service) == FALSE)
547                 return FALSE;
548
549         return TRUE;
550 }
551
552 static int service_type_weight(enum connman_service_type type)
553 {
554         /*
555          * The session doesn't care which service
556          * to use. Nevertheless we have to sort them
557          * according their type. The ordering is
558          *
559          * 1. Ethernet
560          * 2. Bluetooth
561          * 3. WiFi/WiMAX
562          * 4. GSM/UTMS/3G
563          */
564
565         switch (type) {
566         case CONNMAN_SERVICE_TYPE_ETHERNET:
567                 return 4;
568         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
569                 return 3;
570         case CONNMAN_SERVICE_TYPE_WIFI:
571         case CONNMAN_SERVICE_TYPE_WIMAX:
572                 return 2;
573         case CONNMAN_SERVICE_TYPE_CELLULAR:
574                 return 1;
575         case CONNMAN_SERVICE_TYPE_UNKNOWN:
576         case CONNMAN_SERVICE_TYPE_SYSTEM:
577         case CONNMAN_SERVICE_TYPE_GPS:
578         case CONNMAN_SERVICE_TYPE_VPN:
579         case CONNMAN_SERVICE_TYPE_GADGET:
580                 break;
581         }
582
583         return 0;
584 }
585
586 static gint sort_allowed_bearers(struct connman_service *service_a,
587                                         struct connman_service *service_b,
588                                         struct connman_session *session)
589 {
590         struct session_info *info = &session->info;
591         GSList *list;
592         enum connman_service_type type_a, type_b;
593         int weight_a, weight_b;
594
595         type_a = connman_service_get_type(service_a);
596         type_b = connman_service_get_type(service_b);
597
598         for (list = info->allowed_bearers;
599                         list != NULL; list = list->next) {
600                 struct bearer_info *info = list->data;
601
602                 if (info->match_all == TRUE) {
603                         if (type_a != type_b) {
604                                 weight_a = service_type_weight(type_a);
605                                 weight_b = service_type_weight(type_b);
606
607                                 if (weight_a > weight_b)
608                                         return -1;
609
610                                 if (weight_a < weight_b)
611                                         return 1;
612
613                                 return 0;
614                         }
615                 }
616
617                 if (type_a == info->service_type &&
618                                 type_b == info->service_type) {
619                         return 0;
620                 }
621
622                 if (type_a == info->service_type &&
623                                 type_b != info->service_type) {
624                         return -1;
625                 }
626
627                 if (type_a != info->service_type &&
628                                 type_b == info->service_type) {
629                         return 1;
630                 }
631         }
632
633         return 0;
634 }
635
636 static gint sort_services(gconstpointer a, gconstpointer b, gpointer user_data)
637 {
638         struct connman_service *service_a = (void *)a;
639         struct connman_service *service_b = (void *)b;
640         struct connman_session *session = user_data;
641
642         return sort_allowed_bearers(service_a, service_b, session);
643 }
644
645 static void print_name(gpointer data, gpointer user_data)
646 {
647         struct connman_service *service = data;
648
649         DBG("service %p type %s name %s", service,
650                 service2bearer(connman_service_get_type(service)),
651                 __connman_service_get_name(service));
652 }
653
654 static connman_bool_t session_select_service(struct connman_session *session)
655 {
656         struct session_info *info = &session->info;
657         struct connman_service *service;
658         GSequenceIter *iter;
659
660         session->service_list =
661                 __connman_service_get_list(session, service_match);
662
663         if (session->service_list == NULL)
664                 return FALSE;
665
666         g_sequence_sort(session->service_list, sort_services, session);
667         g_sequence_foreach(session->service_list, print_name, NULL);
668
669         iter = g_sequence_get_begin_iter(session->service_list);
670
671         while (g_sequence_iter_is_end(iter) == FALSE) {
672                 service = g_sequence_get(iter);
673
674                 if (__connman_service_is_connecting(service) == TRUE ||
675                                 __connman_service_is_connected(service) == TRUE) {
676                         info->service = service;
677                         return TRUE;
678                 }
679
680                 iter = g_sequence_iter_next(iter);
681         }
682
683         return FALSE;
684 }
685
686 static void cleanup_session(gpointer user_data)
687 {
688         struct connman_session *session = user_data;
689         struct session_info *info = &session->info;
690
691         DBG("remove %s", session->session_path);
692
693         g_sequence_free(session->service_list);
694
695         g_slist_foreach(info->allowed_bearers, cleanup_bearer_info, NULL);
696         g_slist_free(info->allowed_bearers);
697
698         g_free(session->owner);
699         g_free(session->session_path);
700         g_free(session->notify_path);
701
702         g_free(session);
703 }
704
705 static void release_session(gpointer key, gpointer value, gpointer user_data)
706 {
707         struct connman_session *session = value;
708         DBusMessage *message;
709
710         DBG("owner %s path %s", session->owner, session->notify_path);
711
712         if (session->notify_watch > 0)
713                 g_dbus_remove_watch(connection, session->notify_watch);
714
715         g_dbus_unregister_interface(connection, session->session_path,
716                                                 CONNMAN_SESSION_INTERFACE);
717
718         message = dbus_message_new_method_call(session->owner,
719                                                 session->notify_path,
720                                                 CONNMAN_NOTIFICATION_INTERFACE,
721                                                 "Release");
722         if (message == NULL)
723                 return;
724
725         dbus_message_set_no_reply(message, TRUE);
726
727         g_dbus_send_message(connection, message);
728 }
729
730 static int session_disconnect(struct connman_session *session)
731 {
732         DBG("session %p, %s", session, session->owner);
733
734         if (session->notify_watch > 0)
735                 g_dbus_remove_watch(connection, session->notify_watch);
736
737         g_dbus_unregister_interface(connection, session->session_path,
738                                                 CONNMAN_SESSION_INTERFACE);
739
740         g_hash_table_remove(session_hash, session->session_path);
741
742         return 0;
743 }
744
745 static void owner_disconnect(DBusConnection *conn, void *user_data)
746 {
747         struct connman_session *session = user_data;
748
749         DBG("session %p, %s died", session, session->owner);
750
751         session_disconnect(session);
752 }
753
754 static DBusMessage *destroy_session(DBusConnection *conn,
755                                         DBusMessage *msg, void *user_data)
756 {
757         struct connman_session *session = user_data;
758
759         DBG("session %p", session);
760
761         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
762 }
763
764 static gboolean session_do_connect(gpointer user_data)
765 {
766         struct connman_session *session = user_data;
767         struct session_info *info = &session->info;
768
769         __connman_service_connect(info->service);
770
771         service_changed(session);
772         return FALSE;
773 }
774
775 static DBusMessage *connect_session(DBusConnection *conn,
776                                         DBusMessage *msg, void *user_data)
777 {
778         struct connman_session *session = user_data;
779         struct session_info *info = &session->info;
780         struct connman_service *service = NULL;
781         GSourceFunc callback = NULL;
782         GSequenceIter *iter;
783
784         DBG("session %p", session);
785
786         info->connect = TRUE;
787
788         if (session->service_list != NULL)
789                 g_sequence_free(session->service_list);
790
791         session->service_list = __connman_service_get_list(session,
792                                                                 service_match);
793
794         g_sequence_sort(session->service_list, sort_services, session);
795         g_sequence_foreach(session->service_list, print_name, NULL);
796
797         iter = g_sequence_get_begin_iter(session->service_list);
798
799         while (g_sequence_iter_is_end(iter) == FALSE) {
800                 service = g_sequence_get(iter);
801
802                 if (__connman_service_is_connecting(service) == TRUE ||
803                                 __connman_service_is_connected(service) == TRUE) {
804                         callback = service_changed;
805                         break;
806                 }
807
808                 if (__connman_service_is_idle(service) == TRUE) {
809                         callback = session_do_connect;
810                         break;
811                 }
812
813                 service = NULL;
814
815                 iter = g_sequence_iter_next(iter);
816         }
817
818         if (service != NULL) {
819                 info->service = service;
820                 update_service(session);
821                 g_timeout_add_seconds(0, callback, session);
822         }
823
824         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
825 }
826
827 static gboolean session_do_disconnect(gpointer user_data)
828 {
829         struct connman_session *session = user_data;
830         struct session_info *info = &session->info;
831
832         if (__connman_service_disconnect(info->service) < 0)
833                 return FALSE;
834
835         info->service = NULL;
836         update_service(session);
837         service_changed(session);
838
839         return FALSE;
840 }
841
842 static DBusMessage *disconnect_session(DBusConnection *conn,
843                                         DBusMessage *msg, void *user_data)
844 {
845         struct connman_session *session = user_data;
846         struct session_info *info = &session->info;
847
848         DBG("session %p", session);
849
850         info->connect = FALSE;
851
852         if (info->service == NULL)
853                 return __connman_error_already_disabled(msg);
854
855         g_timeout_add_seconds(0, session_do_disconnect, session);
856
857         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
858 }
859
860 static DBusMessage *change_session(DBusConnection *conn,
861                                         DBusMessage *msg, void *user_data)
862 {
863         struct connman_session *session = user_data;
864         struct session_info *info = &session->info;
865         DBusMessageIter iter, value;
866         DBusMessage *reply;
867         DBusMessageIter reply_array, reply_dict;
868         const char *name;
869         GSList *allowed_bearers;
870
871         DBG("session %p", session);
872         if (dbus_message_iter_init(msg, &iter) == FALSE)
873                 return __connman_error_invalid_arguments(msg);
874
875         reply = dbus_message_new_method_call(session->owner,
876                                                 session->notify_path,
877                                                 CONNMAN_NOTIFICATION_INTERFACE,
878                                                 "Update");
879         if (reply == NULL)
880                 return __connman_error_failed(msg, ENOMEM);
881
882         dbus_message_iter_init_append(reply, &reply_array);
883         connman_dbus_dict_open(&reply_array, &reply_dict);
884
885         dbus_message_iter_get_basic(&iter, &name);
886         dbus_message_iter_next(&iter);
887         dbus_message_iter_recurse(&iter, &value);
888
889         switch (dbus_message_iter_get_arg_type(&value)) {
890         case DBUS_TYPE_ARRAY:
891                 if (g_str_equal(name, "AllowedBearers") == TRUE) {
892                         allowed_bearers = session_parse_allowed_bearers(&value);
893
894                         g_slist_foreach(info->allowed_bearers,
895                                         cleanup_bearer_info, NULL);
896                         g_slist_free(info->allowed_bearers);
897
898                         if (allowed_bearers == NULL) {
899                                 allowed_bearers = session_allowed_bearers_any();
900
901                                 if (allowed_bearers == NULL) {
902                                         dbus_message_unref(reply);
903                                         return __connman_error_failed(msg, ENOMEM);
904                                 }
905                         }
906
907                         info->allowed_bearers = allowed_bearers;
908
909                         /* update_allowed_bearers(); */
910                 } else {
911                         goto err;
912                 }
913                 break;
914         case DBUS_TYPE_BOOLEAN:
915                 if (g_str_equal(name, "Priority") == TRUE) {
916                         dbus_message_iter_get_basic(&value,
917                                         &info->priority);
918
919                         /* update_priority(); */
920                 } else if (g_str_equal(name, "AvoidHandover") == TRUE) {
921                         dbus_message_iter_get_basic(&value,
922                                         &info->avoid_handover);
923
924                         /* update_avoid_handover(); */
925                 } else if (g_str_equal(name, "StayConnected") == TRUE) {
926                         dbus_message_iter_get_basic(&value,
927                                         &info->stay_connected);
928
929                         /* update_stay_connected(); */
930                 } else if (g_str_equal(name, "EmergencyCall") == TRUE) {
931                         dbus_message_iter_get_basic(&value,
932                                         &info->ecall);
933
934                         /* update_ecall(); */
935                 } else {
936                         goto err;
937                 }
938                 break;
939         case DBUS_TYPE_UINT32:
940                 if (g_str_equal(name, "PeriodicConnect") == TRUE) {
941                         dbus_message_iter_get_basic(&value,
942                                         &info->periodic_connect);
943
944                         /* update_periodic_update(); */
945                 } else if (g_str_equal(name, "IdleTimeout") == TRUE) {
946                         dbus_message_iter_get_basic(&value,
947                                         &info->idle_timeout);
948
949                         /* update_idle_timeout(); */
950                 } else {
951                         goto err;
952                 }
953                 break;
954         case DBUS_TYPE_STRING:
955                 if (g_str_equal(name, "RoamingPolicy") == TRUE) {
956                         const char *val;
957                         dbus_message_iter_get_basic(&value, &val);
958                         info->roaming_policy =
959                                         string2roamingpolicy(val);
960
961                         /* update_roaming_allowed(); */
962                 } else {
963                         goto err;
964                 }
965                 break;
966         }
967
968         append_notify_all(&reply_dict, session);
969
970         connman_dbus_dict_close(&reply_array, &reply_dict);
971
972         g_dbus_send_message(connection, reply);
973
974         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
975
976 err:
977         dbus_message_unref(reply);
978         return __connman_error_invalid_arguments(msg);
979 }
980
981 static GDBusMethodTable session_methods[] = {
982         { "Destroy",    "",   "", destroy_session    },
983         { "Connect",    "",   "", connect_session    },
984         { "Disconnect", "",   "", disconnect_session },
985         { "Change",     "sv", "", change_session     },
986         { },
987 };
988
989 int __connman_session_create(DBusMessage *msg)
990 {
991         const char *owner, *notify_path;
992         char *session_path;
993         DBusMessageIter iter, array;
994         struct connman_session *session;
995         struct session_info *info, *info_last;
996
997         connman_bool_t priority = FALSE, avoid_handover = FALSE;
998         connman_bool_t stay_connected = FALSE, ecall = FALSE;
999         enum connman_session_roaming_policy roaming_policy =
1000                                 CONNMAN_SESSION_ROAMING_POLICY_FORBIDDEN;
1001         GSList *allowed_bearers = NULL;
1002         unsigned int periodic_connect = 0;
1003         unsigned int idle_timeout = 0;
1004
1005         int err;
1006
1007         owner = dbus_message_get_sender(msg);
1008
1009         DBG("owner %s", owner);
1010
1011         dbus_message_iter_init(msg, &iter);
1012         dbus_message_iter_recurse(&iter, &array);
1013
1014         while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) {
1015                 DBusMessageIter entry, value;
1016                 const char *key, *val;
1017
1018                 dbus_message_iter_recurse(&array, &entry);
1019                 dbus_message_iter_get_basic(&entry, &key);
1020
1021                 dbus_message_iter_next(&entry);
1022                 dbus_message_iter_recurse(&entry, &value);
1023
1024                 switch (dbus_message_iter_get_arg_type(&value)) {
1025                 case DBUS_TYPE_ARRAY:
1026                         if (g_str_equal(key, "AllowedBearers") == TRUE) {
1027                                 allowed_bearers =
1028                                         session_parse_allowed_bearers(&value);
1029                         } else {
1030                                 return -EINVAL;
1031                         }
1032                         break;
1033                 case DBUS_TYPE_BOOLEAN:
1034                         if (g_str_equal(key, "Priority") == TRUE) {
1035                                 dbus_message_iter_get_basic(&value,
1036                                                         &priority);
1037                         } else if (g_str_equal(key, "AvoidHandover") == TRUE) {
1038                                 dbus_message_iter_get_basic(&value,
1039                                                         &avoid_handover);
1040                         } else if (g_str_equal(key, "StayConnected") == TRUE) {
1041                                 dbus_message_iter_get_basic(&value,
1042                                                         &stay_connected);
1043                         } else if (g_str_equal(key, "EmergencyCall") == TRUE) {
1044                                 dbus_message_iter_get_basic(&value,
1045                                                         &ecall);
1046                         } else {
1047                                 return -EINVAL;
1048                         }
1049                         break;
1050                 case DBUS_TYPE_UINT32:
1051                         if (g_str_equal(key, "PeriodicConnect") == TRUE) {
1052                                 dbus_message_iter_get_basic(&value,
1053                                                         &periodic_connect);
1054                         } else if (g_str_equal(key, "IdleTimeout") == TRUE) {
1055                                 dbus_message_iter_get_basic(&value,
1056                                                         &idle_timeout);
1057                         } else {
1058                                 return -EINVAL;
1059                         }
1060                         break;
1061                 case DBUS_TYPE_STRING:
1062                         if (g_str_equal(key, "RoamingPolicy") == TRUE) {
1063                                 dbus_message_iter_get_basic(&value, &val);
1064                                 roaming_policy = string2roamingpolicy(val);
1065                         } else {
1066                                 return -EINVAL;
1067                         }
1068                 }
1069                 dbus_message_iter_next(&array);
1070         }
1071
1072         dbus_message_iter_next(&iter);
1073         dbus_message_iter_get_basic(&iter, &notify_path);
1074
1075         if (notify_path == NULL) {
1076                 session_path = NULL;
1077                 err = -EINVAL;
1078                 goto err;
1079         }
1080
1081         session_path = g_strdup_printf("/sessions%s", notify_path);
1082         if (session_path == NULL) {
1083                 err = -ENOMEM;
1084                 goto err;
1085         }
1086
1087         session = g_hash_table_lookup(session_hash, session_path);
1088         if (session != NULL) {
1089                 err = -EEXIST;
1090                 goto err;
1091         }
1092
1093         session = g_try_new0(struct connman_session, 1);
1094         if (session == NULL) {
1095                 err = -ENOMEM;
1096                 goto err;
1097         }
1098
1099         info = &session->info;
1100         info_last = &session->info_last;
1101
1102         session->owner = g_strdup(owner);
1103         session->session_path = session_path;
1104         session->notify_path = g_strdup(notify_path);
1105         session->notify_watch =
1106                 g_dbus_add_disconnect_watch(connection, session->owner,
1107                                         owner_disconnect, session, NULL);
1108
1109         info->bearer = "";
1110         info->online = FALSE;
1111         info->priority = priority;
1112         info->avoid_handover = avoid_handover;
1113         info->stay_connected = stay_connected;
1114         info->periodic_connect = periodic_connect;
1115         info->idle_timeout = idle_timeout;
1116         info->ecall = ecall;
1117         info->roaming_policy = roaming_policy;
1118
1119         if (allowed_bearers == NULL) {
1120                 info->allowed_bearers =
1121                                 session_allowed_bearers_any();
1122
1123                 if (info->allowed_bearers == NULL) {
1124                         err = -ENOMEM;
1125                         goto err;
1126                 }
1127         } else {
1128                 info->allowed_bearers = allowed_bearers;
1129         }
1130
1131         info_last->bearer = NULL;
1132         info_last->online = !priority;
1133         info_last->avoid_handover = !avoid_handover;
1134         info_last->stay_connected = !stay_connected;
1135         info_last->periodic_connect = !periodic_connect;
1136         info_last->idle_timeout = !idle_timeout;
1137         info_last->ecall = !ecall;
1138         info_last->roaming_policy =
1139                         CONNMAN_SESSION_ROAMING_POLICY_UNKNOWN;
1140         info_last->allowed_bearers = NULL;
1141         info_last->service = NULL;
1142         info_last->marker = info->marker + 1;
1143
1144         session->service_list = NULL;
1145
1146         update_service(session);
1147
1148         g_hash_table_replace(session_hash, session->session_path, session);
1149
1150         DBG("add %s", session->session_path);
1151
1152         if (g_dbus_register_interface(connection, session->session_path,
1153                                         CONNMAN_SESSION_INTERFACE,
1154                                         session_methods, NULL,
1155                                         NULL, session, NULL) == FALSE) {
1156                 connman_error("Failed to register %s", session->session_path);
1157                 g_hash_table_remove(session_hash, session->session_path);
1158                 session = NULL;
1159
1160                 err = -EINVAL;
1161                 goto err;
1162         }
1163
1164         g_dbus_send_reply(connection, msg,
1165                                 DBUS_TYPE_OBJECT_PATH, &session->session_path,
1166                                 DBUS_TYPE_INVALID);
1167
1168
1169         /*
1170          * Check if the session info matches to a service which is
1171          * a already connected
1172          */
1173         if (session_select_service(session) == TRUE)
1174                 update_service(session);
1175         else
1176                 info_last->service = (void *)1;
1177
1178         g_timeout_add_seconds(0, session_notify_all, session);
1179
1180         return 0;
1181
1182 err:
1183         connman_error("Failed to create session");
1184         g_free(session_path);
1185
1186         g_slist_foreach(allowed_bearers, cleanup_bearer_info, NULL);
1187         g_slist_free(allowed_bearers);
1188
1189         return err;
1190 }
1191
1192 int __connman_session_destroy(DBusMessage *msg)
1193 {
1194         const char *owner, *session_path;
1195         struct connman_session *session;
1196
1197         owner = dbus_message_get_sender(msg);
1198
1199         DBG("owner %s", owner);
1200
1201         dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &session_path,
1202                                                         DBUS_TYPE_INVALID);
1203         if (session_path == NULL)
1204                 return -EINVAL;
1205
1206         session = g_hash_table_lookup(session_hash, session_path);
1207         if (session == NULL)
1208                 return -EINVAL;
1209
1210         if (g_strcmp0(owner, session->owner) != 0)
1211                 return -EACCES;
1212
1213         session_disconnect(session);
1214
1215         return 0;
1216 }
1217
1218 connman_bool_t __connman_session_mode()
1219 {
1220         return sessionmode;
1221 }
1222
1223 void __connman_session_set_mode(connman_bool_t enable)
1224 {
1225         DBG("enable %d", enable);
1226
1227         if (sessionmode == enable)
1228                 return;
1229
1230         sessionmode = enable;
1231
1232         if (sessionmode == TRUE)
1233                 __connman_service_disconnect_all();
1234 }
1235
1236 static void service_add(struct connman_service *service)
1237 {
1238         GHashTableIter iter;
1239         gpointer key, value;
1240         struct connman_session *session;
1241
1242         DBG("service %p", service);
1243
1244         g_hash_table_iter_init(&iter, session_hash);
1245
1246         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
1247                 session = value;
1248
1249                 if (service_match(session, service) == FALSE)
1250                         continue;
1251
1252                 g_sequence_insert_sorted(session->service_list, service,
1253                                                 sort_services, session);
1254         }
1255 }
1256
1257 static gint service_in_session(gconstpointer a, gconstpointer b,
1258                                 gpointer user_data)
1259 {
1260         if (a == b)
1261                 return 0;
1262
1263         return -1;
1264 }
1265
1266 static void service_remove(struct connman_service *service)
1267 {
1268
1269         GHashTableIter iter;
1270         gpointer key, value;
1271         GSequenceIter *seq_iter;
1272         struct connman_session *session;
1273         struct session_info *info;
1274         struct connman_service *found_service;
1275
1276         DBG("service %p", service);
1277
1278         g_hash_table_iter_init(&iter, session_hash);
1279
1280         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
1281                 session = value;
1282                 info = &session->info;
1283
1284                 if (session->service_list == NULL)
1285                         continue;
1286
1287                 seq_iter = g_sequence_search(session->service_list, service,
1288                                                 service_in_session, NULL);
1289                 if (g_sequence_iter_is_end(seq_iter) == TRUE)
1290                         continue;
1291
1292                 g_sequence_remove(seq_iter);
1293
1294                 found_service = g_sequence_get(seq_iter);
1295                 if (found_service != info->service)
1296                         continue;
1297
1298                 info->service = NULL;
1299                 update_service(session);
1300                 g_timeout_add_seconds(0, service_changed, session);
1301         }
1302 }
1303
1304 static void service_state_changed(struct connman_service *service,
1305                                         enum connman_service_state state)
1306 {
1307         GHashTableIter iter;
1308         gpointer key, value;
1309         struct connman_session *session;
1310         struct session_info *info;
1311         connman_bool_t online;
1312
1313         DBG("service %p state %d", service, state);
1314
1315         g_hash_table_iter_init(&iter, session_hash);
1316
1317         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
1318                 session = value;
1319                 info = &session->info;
1320
1321                 if (info->service == service) {
1322                         online = __connman_service_is_connected(service);
1323                         if (info->online == online)
1324                                 continue;
1325
1326                         info->online = online;
1327                         online_changed(session);
1328                 }
1329         }
1330 }
1331
1332 static void ipconfig_changed(struct connman_service *service,
1333                                 struct connman_ipconfig *ipconfig)
1334 {
1335         GHashTableIter iter;
1336         gpointer key, value;
1337         struct connman_session *session;
1338         struct session_info *info;
1339         enum connman_ipconfig_type type;
1340
1341         DBG("service %p ipconfig %p", service, ipconfig);
1342
1343         type = __connman_ipconfig_get_config_type(ipconfig);
1344
1345         g_hash_table_iter_init(&iter, session_hash);
1346
1347         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
1348                 session = value;
1349                 info = &session->info;
1350
1351                 if (info->service == service) {
1352                         if (type == CONNMAN_IPCONFIG_TYPE_IPV4)
1353                                 ipconfig_ipv4_changed(session);
1354                         else if (type == CONNMAN_IPCONFIG_TYPE_IPV6)
1355                                 ipconfig_ipv6_changed(session);
1356                 }
1357         }
1358 }
1359
1360 static struct connman_notifier session_notifier = {
1361         .name                   = "session",
1362         .service_add            = service_add,
1363         .service_remove         = service_remove,
1364         .service_state_changed  = service_state_changed,
1365         .ipconfig_changed       = ipconfig_changed,
1366 };
1367
1368 int __connman_session_init(void)
1369 {
1370         int err;
1371
1372         DBG("");
1373
1374         connection = connman_dbus_get_connection();
1375         if (connection == NULL)
1376                 return -1;
1377
1378         err = connman_notifier_register(&session_notifier);
1379         if (err < 0) {
1380                 dbus_connection_unref(connection);
1381                 return err;
1382         }
1383
1384         session_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
1385                                                 NULL, cleanup_session);
1386
1387         sessionmode = FALSE;
1388         return 0;
1389 }
1390
1391 void __connman_session_cleanup(void)
1392 {
1393         DBG("");
1394
1395         if (connection == NULL)
1396                 return;
1397
1398         connman_notifier_unregister(&session_notifier);
1399
1400         g_hash_table_foreach(session_hash, release_session, NULL);
1401         g_hash_table_destroy(session_hash);
1402
1403         dbus_connection_unref(connection);
1404 }