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