Add wireguard related changes and test cases
[platform/core/api/vpn-setting.git] / dvpnlib / src / dvpnlib-vpn-connnection.c
1 #include "dvpnlib-internal.h"
2 #include "dvpnlib-vpn-connection.h"
3
4 static GList *vpn_connection_list;
5 static GHashTable *vpn_connection_hash;
6
7 struct connection_property_changed_cb {
8         vpn_connection_property_changed_cb property_changed_cb;
9         void *user_data;
10 };
11
12 struct vpn_connection {
13         GDBusProxy *dbus_proxy;
14         gchar *type;
15         gchar *path;
16         gchar *name;
17         gchar *domain;
18         gchar *host;
19         gboolean immutable;
20         gint index;
21         enum vpn_connection_state state;
22         struct vpn_connection_ipv4 *ipv4;
23         struct vpn_connection_ipv6 *ipv6;
24         gchar **nameservers;
25         GSList *user_routes; /*struct vpn_connection_route */
26         GSList *server_routes; /* struct vpn_connection_route */
27         GHashTable *property_changed_cb_hash;
28 };
29
30 static void free_vpn_connection_ipv4(struct vpn_connection_ipv4 *ipv4_info);
31 static void free_vpn_connection_ipv6(struct vpn_connection_ipv6 *ipv6_info);
32 static void free_vpn_connection_route(gpointer data);
33
34 //LCOV_EXCL_START
35 enum dvpnlib_err
36 vpn_connection_set_user_routes(struct vpn_connection *connection,
37                             struct vpn_connection_route **user_routes)
38 {
39         if (!connection || !user_routes)
40                 return DVPNLIB_ERR_INVALID_PARAMETER;
41
42         GVariantBuilder user_routes_b;
43         GVariant *user_routes_v;
44
45         g_variant_builder_init(&user_routes_b, G_VARIANT_TYPE("a(a{sv})"));
46
47         while (*user_routes != NULL) {
48
49                 g_variant_builder_open(&user_routes_b,
50                                 G_VARIANT_TYPE("(a{sv})"));
51                 g_variant_builder_open(&user_routes_b,
52                                 G_VARIANT_TYPE("a{sv}"));
53
54                 if ((*user_routes)->protocol_family) {
55                         g_variant_builder_add(&user_routes_b, "{sv}",
56                                 "ProtocolFamily",
57                                 g_variant_new_int32(
58                                         (*user_routes)->protocol_family));
59                 }
60
61                 if ((*user_routes)->network) {
62                         g_variant_builder_add(&user_routes_b, "{sv}",
63                                 "Network",
64                                 g_variant_new_string((*user_routes)->network));
65                 }
66
67                 if ((*user_routes)->netmask) {
68                         g_variant_builder_add(&user_routes_b, "{sv}",
69                                 "Netmask",
70                                 g_variant_new_string((*user_routes)->netmask));
71                 }
72
73                 if ((*user_routes)->gateway) {
74                         g_variant_builder_add(&user_routes_b, "{sv}",
75                                 "Gateway",
76                                 g_variant_new_string((*user_routes)->gateway));
77                 }
78
79                 g_variant_builder_close(&user_routes_b);
80
81                 user_routes++;
82         }
83
84         user_routes_v = g_variant_builder_end(&user_routes_b);
85
86         return common_set_property(connection->dbus_proxy, "UserRoutes",
87                         user_routes_v);
88
89 }
90
91 static struct connection_property_changed_cb
92                 *get_connection_property_changed_cb(
93                         struct vpn_connection *connection,
94                         enum vpn_connection_property_type type)
95 {
96         return g_hash_table_lookup(connection->property_changed_cb_hash,
97                                         GINT_TO_POINTER(type));
98 }
99
100 static void parse_connection_property_ipv4(
101                                 struct vpn_connection *connection,
102                                 GVariant *ipv4)
103 {
104         DBG("");
105
106         GVariantIter *iter;
107         gchar *key;
108         GVariant *value;
109
110         g_variant_get(ipv4, "a{sv}", &iter);
111         if (g_variant_iter_n_children(iter) == 0) {
112                 g_variant_iter_free(iter);
113                 return;
114         }
115
116         if (connection->ipv4)
117                 free_vpn_connection_ipv4(connection->ipv4);
118
119         connection->ipv4 = g_try_new0(struct vpn_connection_ipv4, 1);
120         if (connection->ipv4 == NULL) {
121                 ERROR("no memory");
122                 g_variant_iter_free(iter);
123                 return;
124         }
125
126         while (g_variant_iter_loop(iter, "{sv}", &key, &value)) {
127                 if (!g_strcmp0(key, "Address")) {
128                         const char *property_value =
129                                 g_variant_get_string(value, NULL);
130                         DBG("Address is %s", property_value);
131                         connection->ipv4->address = g_strdup(property_value);
132                 } else if (!g_strcmp0(key, "Netmask")) {
133                         const char *property_value =
134                                 g_variant_get_string(value, NULL);
135                         DBG("Netmask is %s", property_value);
136                         connection->ipv4->netmask = g_strdup(property_value);
137                 } else if (!g_strcmp0(key, "Gateway")) {
138                         const char *property_value =
139                                 g_variant_get_string(value, NULL);
140                         DBG("Gateway is %s", property_value);
141                         connection->ipv4->gateway = g_strdup(property_value);
142                 } else if (!g_strcmp0(key, "Peer")) {
143                         const char *property_value =
144                                 g_variant_get_string(value, NULL);
145                         DBG("Peer is %s", property_value);
146                         connection->ipv4->peer = g_strdup(property_value);
147                 }
148         }
149
150         g_variant_iter_free(iter);
151 }
152
153 static void parse_connection_property_ipv6(
154                                 struct vpn_connection *connection,
155                                 GVariant *ipv6)
156 {
157         DBG("");
158
159         GVariantIter *iter;
160         gchar *key;
161         GVariant *value;
162
163         g_variant_get(ipv6, "a{sv}", &iter);
164         if (g_variant_iter_n_children(iter) == 0) {
165                 g_variant_iter_free(iter);
166                 return;
167         }
168
169         if (connection->ipv6)
170                 free_vpn_connection_ipv6(connection->ipv6);
171
172         connection->ipv6 = g_try_new0(struct vpn_connection_ipv6, 1);
173         if (connection->ipv6 == NULL) {
174                 ERROR("no memory");
175                 g_variant_iter_free(iter);
176                 return;
177         }
178
179         while (g_variant_iter_loop(iter, "{sv}", &key, &value)) {
180                 if (!g_strcmp0(key, "Address")) {
181                         const char *property_value =
182                                 g_variant_get_string(value, NULL);
183                         DBG("Address is %s", property_value);
184                         connection->ipv6->address = g_strdup(property_value);
185                 } else if (!g_strcmp0(key, "PrefixLength")) {
186                         const char *property_value =
187                                 g_variant_get_string(value, NULL);
188                         DBG("PrefixLength is %s", property_value);
189                         connection->ipv6->prefix_length =
190                                 g_strdup(property_value);
191                 } else if (!g_strcmp0(key, "Gateway")) {
192                         const char *property_value =
193                                 g_variant_get_string(value, NULL);
194                         DBG("Gateway is %s", property_value);
195                         connection->ipv6->gateway = g_strdup(property_value);
196                 } else if (!g_strcmp0(key, "Peer")) {
197                         const char *property_value =
198                                 g_variant_get_string(value, NULL);
199                         DBG("Peer is %s", property_value);
200                         connection->ipv6->peer = g_strdup(property_value);
201                 }
202         }
203
204         g_variant_iter_free(iter);
205 }
206
207 static void parse_connection_property_nameservers(
208                                 struct vpn_connection *connection,
209                                 GVariant *nameservers)
210 {
211         DBG("");
212
213         GVariantIter *iter;
214         gchar *value;
215         int i = 0, n;
216
217         g_variant_get(nameservers, "as", &iter);
218         if (g_variant_iter_n_children(iter) == 0) {
219                 g_variant_iter_free(iter);
220                 return;
221         }
222
223         g_strfreev(connection->nameservers);
224         n = g_variant_iter_n_children(iter);
225         connection->nameservers = g_try_new0(char *, n+1);
226         if (connection->nameservers == NULL) {
227                 ERROR("no memory");
228                 g_variant_iter_free(iter);
229                 return;
230         }
231
232         while (g_variant_iter_loop(iter, "s", &value) && i < n) {
233                 DBG("Nameserver Entry is %s", value);
234                 connection->nameservers[i] = g_strdup(value);
235                 i++;
236         }
237         connection->nameservers[n] = NULL;
238
239         g_variant_iter_free(iter);
240 }
241
242 static void print_variant(const gchar *s, GVariant *v)
243 {
244         gchar *temp = g_variant_print(v, true);
245         DBG("%s => %s", s, temp);
246         g_free(temp);
247 }
248
249 static void parse_connection_property_user_routes(
250                                 struct vpn_connection *connection,
251                                 GVariant *user_routes)
252 {
253         DBG("");
254
255         GVariantIter outer;
256         GVariantIter *route_entry;
257
258         print_variant("Incoming : ", user_routes);
259
260         g_variant_iter_init(&outer, user_routes);
261         if (g_variant_iter_n_children(&outer) == 0)
262                 return;
263
264         if (connection->user_routes == NULL) {
265                 ERROR("connection->server_routes is NULL");
266                 return;
267         } else {
268                 g_slist_free_full(connection->user_routes,
269                                 free_vpn_connection_route);
270                 connection->user_routes = NULL;
271         }
272
273         while (g_variant_iter_loop(&outer, "(a{sv})", &route_entry)) {
274                 gchar *key;
275                 GVariant *value;
276
277                 if (g_variant_iter_n_children(route_entry) == 0)
278                         continue;
279
280                 struct vpn_connection_route *route =
281                         g_try_new0(struct vpn_connection_route, 1);
282                 if (route == NULL) {
283                         ERROR("no memory");
284                         return;
285                 }
286
287                 while (g_variant_iter_loop(route_entry, "{sv}", &key, &value)) {
288                         if (!g_strcmp0(key, "ProtocolFamily")) {
289                                 int property_value = g_variant_get_int32(value);
290                                 DBG("ProtocolFamily is %d", property_value);
291                                 route->protocol_family = property_value;
292                         } else if (!g_strcmp0(key, "Network")) {
293                                 const char *property_value =
294                                         g_variant_get_string(value, NULL);
295                                 DBG("Network is %s", property_value);
296                                 if (route->network)
297                                         g_free(route->network);
298                                 route->network = g_strdup(property_value);
299                         } else if (!g_strcmp0(key, "Netmask")) {
300                                 const char *property_value =
301                                         g_variant_get_string(value, NULL);
302                                 DBG("Netmask is %s", property_value);
303                                 if (route->netmask)
304                                         g_free(route->netmask);
305                                 route->netmask = g_strdup(property_value);
306                         } else if (!g_strcmp0(key, "Gateway")) {
307                                 const char *property_value =
308                                         g_variant_get_string(value, NULL);
309                                 DBG("Gateway is %s", property_value);
310                                 if (route->gateway)
311                                         g_free(route->gateway);
312                                 route->gateway = g_strdup(property_value);
313                         }
314                 }
315
316                 /*TODO: See if g_slist_prepend works better*/
317                 connection->user_routes =
318                         g_slist_append(connection->user_routes, route);
319
320         }
321 }
322
323 static void parse_connection_property_server_routes(
324                                 struct vpn_connection *connection,
325                                 GVariant *server_routes)
326 {
327         DBG("");
328
329         GVariantIter outer;
330         GVariantIter *route_entry;
331
332         g_variant_iter_init(&outer, server_routes);
333         if (g_variant_iter_n_children(&outer) == 0)
334                 return;
335
336         if (connection->server_routes == NULL) {
337                 ERROR("connection->server_routes is NULL");
338                 return;
339         } else {
340                 g_slist_free_full(connection->server_routes,
341                                 free_vpn_connection_route);
342                 connection->server_routes =  NULL;
343         }
344
345         while (g_variant_iter_loop(&outer, "(a{sv})", &route_entry)) {
346                 gchar *key;
347                 GVariant *value;
348
349                 if (g_variant_iter_n_children(route_entry) == 0)
350                         continue;
351
352                 struct vpn_connection_route *route =
353                         g_try_new0(struct vpn_connection_route, 1);
354                 if (route == NULL) {
355                         ERROR("no memory");
356                         return;
357                 }
358
359                 while (g_variant_iter_loop(route_entry, "{sv}", &key, &value)) {
360                         if (!g_strcmp0(key, "ProtocolFamily")) {
361                                 int property_value = g_variant_get_int32(value);
362                                 DBG("ProtocolFamily is %d", property_value);
363                                 route->protocol_family = property_value;
364                         } else if (!g_strcmp0(key, "Network")) {
365                                 const char *property_value =
366                                         g_variant_get_string(value, NULL);
367                                 DBG("Network is %s", property_value);
368                                 if (route->network)
369                                         g_free(route->network);
370                                 route->network = g_strdup(property_value);
371                         } else if (!g_strcmp0(key, "Netmask")) {
372                                 const char *property_value =
373                                         g_variant_get_string(value, NULL);
374                                 DBG("Netmask is %s", property_value);
375                                 if (route->netmask)
376                                         g_free(route->netmask);
377                                 route->netmask = g_strdup(property_value);
378                         } else if (!g_strcmp0(key, "Gateway")) {
379                                 const char *property_value =
380                                         g_variant_get_string(value, NULL);
381                                 DBG("Gateway is %s", property_value);
382                                 if (route->gateway)
383                                         g_free(route->gateway);
384                                 route->gateway = g_strdup(property_value);
385                         }
386                 }
387
388                 /*TODO: See if g_slist_prepend works better*/
389                 connection->server_routes =
390                         g_slist_append(connection->server_routes, route);
391         }
392 }
393
394 static enum vpn_connection_property_type parse_connection_property(
395                                         struct vpn_connection *connection,
396                                         gchar *key, GVariant *value)
397 {
398         assert(connection != NULL);
399         enum vpn_connection_property_type property_type = VPN_CONN_PROP_NONE;
400
401         if (!g_strcmp0(key, "State")) {
402                 const gchar *property_value;
403                 property_value = g_variant_get_string(value, NULL);
404                 DBG("connection state is %s", property_value);
405                 if (!g_strcmp0(property_value, "idle"))
406                         connection->state = VPN_CONN_STATE_IDLE;
407                 else if (!g_strcmp0(property_value, "failure"))
408                         connection->state = VPN_CONN_STATE_FAILURE;
409                 else if (!g_strcmp0(property_value, "configuration"))
410                         connection->state = VPN_CONN_STATE_CONFIGURATION;
411                 else if (!g_strcmp0(property_value, "ready"))
412                         connection->state = VPN_CONN_STATE_READY;
413                 else if (!g_strcmp0(property_value, "disconnect"))
414                         connection->state = VPN_CONN_STATE_DISCONNECT;
415                 property_type = VPN_CONN_PROP_STATE;
416         } else if (!g_strcmp0(key, "Type")) {
417                 const gchar *property_value;
418                 property_value = g_variant_get_string(value, NULL);
419                 DBG("connection type is %s", property_value);
420                 g_free(connection->type);
421                 connection->type = g_strdup(property_value);
422                 property_type = VPN_CONN_PROP_TYPE;
423         } else if (!g_strcmp0(key, "Name")) {
424                 const gchar *property_value;
425                 property_value = g_variant_get_string(value, NULL);
426                 g_free(connection->name);
427                 connection->name = g_strdup(property_value);
428                 property_type = VPN_CONN_PROP_NAME;
429         } else if (!g_strcmp0(key, "Domain")) {
430                 const gchar *property_value;
431                 property_value = g_variant_get_string(value, NULL);
432                 g_free(connection->domain);
433                 connection->domain = g_strdup(property_value);
434                 property_type = VPN_CONN_PROP_DOMAIN;
435         } else if (!g_strcmp0(key, "Host")) {
436                 const gchar *property_value;
437                 property_value = g_variant_get_string(value, NULL);
438                 g_free(connection->host);
439                 connection->host = g_strdup(property_value);
440                 property_type = VPN_CONN_PROP_HOST;
441         } else if (!g_strcmp0(key, "Immutable")) {
442                 connection->immutable = g_variant_get_boolean(value);
443                 property_type = VPN_CONN_PROP_IMMUTABLE;
444         } else if (!g_strcmp0(key, "Index")) {
445                 connection->index = g_variant_get_int32(value);
446                 property_type = VPN_CONN_PROP_INDEX;
447         }
448         /* TODO:
449          * Add IPv4/IPV6/Nameservers
450          * UserRoutes/ServerRoutes
451          * Parsing code */
452
453         else if (!g_strcmp0(key, "IPv4")) {
454                 parse_connection_property_ipv4(connection, value);
455                 property_type = VPN_CONN_PROP_IPV4;
456         } else if (!g_strcmp0(key, "IPv6")) {
457                 parse_connection_property_ipv6(connection, value);
458                 property_type = VPN_CONN_PROP_IPV6;
459         } else if (!g_strcmp0(key, "Nameservers")) {
460                 parse_connection_property_nameservers(connection, value);
461                 property_type = VPN_CONN_PROP_USERROUTES;
462         } else if (!g_strcmp0(key, "UserRoutes")) {
463                 parse_connection_property_user_routes(connection, value);
464                 property_type = VPN_CONN_PROP_USERROUTES;
465         } else if (!g_strcmp0(key, "ServerRoutes")) {
466                 parse_connection_property_server_routes(connection, value);
467                 property_type = VPN_CONN_PROP_SERVERROUTES;
468         }
469
470         return property_type;
471 }
472
473 static void parse_connection_properties(
474                                 struct vpn_connection *connection,
475                                 GVariantIter *properties)
476 {
477         gchar *key;
478         GVariant *value;
479
480         while (g_variant_iter_next(properties, "{sv}", &key, &value)) {
481                 parse_connection_property(connection, key, value);
482
483                 g_free(key);
484                 g_variant_unref(value);
485         }
486 }
487
488 static void connection_property_changed(
489                                 struct vpn_connection *connection,
490                                 GVariant *parameters)
491 {
492         gchar *key;
493         GVariant *value;
494         enum vpn_connection_property_type property_type;
495
496         DBG("");
497
498         g_variant_get(parameters, "(sv)", &key, &value);
499         property_type = parse_connection_property(connection, key, value);
500
501         if (property_type != VPN_CONN_PROP_NONE) {
502                 DBG("Now check property changed callback");
503
504                 struct connection_property_changed_cb *property_changed_cb_t;
505
506                 property_changed_cb_t = get_connection_property_changed_cb(
507                                                         connection,
508                                                         property_type);
509                 if (property_changed_cb_t != NULL) {
510                         DBG("property changed callback has been set");
511                         property_changed_cb_t->property_changed_cb(connection,
512                                         property_changed_cb_t->user_data);
513                 }
514         }
515
516         g_free(key);
517         g_variant_unref(value);
518 }
519
520 static void connection_signal_handler(GDBusProxy *proxy,
521                                            gchar *sender_name,
522                                            gchar *signal_name,
523                                            GVariant *parameters,
524                                            gpointer user_data)
525 {
526         DBG("signal_name: %s", signal_name);
527
528         struct vpn_connection *connection = user_data;
529
530         if (!g_strcmp0(signal_name, "PropertyChanged"))
531                 connection_property_changed(connection, parameters);
532 }
533
534 static void free_connection_property_changed_cb(gpointer data)
535 {
536         DBG("");
537         struct connection_property_changed_cb *property_changed_cb = data;
538
539         g_free(property_changed_cb);
540 }
541 //LCOV_EXCL_STOP
542
543 void destroy_vpn_connections(void)
544 {
545         if (vpn_connection_list != NULL) {
546                 g_list_free(vpn_connection_list); //LCOV_EXCL_LINE
547                 vpn_connection_list = NULL; //LCOV_EXCL_LINE
548         }
549         if (vpn_connection_hash != NULL) {
550                 g_hash_table_destroy(vpn_connection_hash);
551                 vpn_connection_hash = NULL;
552         }
553 }
554
555 //LCOV_EXCL_START
556 static struct vpn_connection *create_vpn_connection(
557                                                 gchar *object_path,
558                                                 GVariantIter *properties)
559 {
560         GDBusProxy *connection_proxy;
561         struct vpn_connection *connection;
562         GError *error = NULL;
563
564         DBG("");
565
566         connection_proxy = g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SYSTEM,
567                                         G_DBUS_PROXY_FLAGS_NONE, NULL,
568                                         VPN_NAME, object_path,
569                                         VPN_CONNECTION_INTERFACE, NULL, &error);
570         if (connection_proxy == NULL) {
571                 ERROR("error info: %s", error->message);
572                 g_error_free(error);
573                 return NULL;
574         }
575
576         connection = g_try_new0(struct vpn_connection, 1);
577         if (connection == NULL) {
578                 ERROR("no memory");
579                 g_object_unref(connection_proxy);
580                 return NULL;
581         }
582
583         connection->dbus_proxy = connection_proxy;
584         connection->path = g_strdup(object_path);
585
586         parse_connection_properties(connection, properties);
587
588         g_hash_table_insert(vpn_connection_hash,
589                                 (gpointer)connection->path,
590                                 (gpointer)connection);
591
592         vpn_connection_list = g_list_append(vpn_connection_list,
593                                                 connection);
594
595         connection->property_changed_cb_hash = g_hash_table_new_full(
596                                         g_direct_hash, g_direct_equal, NULL,
597                                         free_connection_property_changed_cb);
598
599         g_signal_connect(connection->dbus_proxy, "g-signal",
600                         G_CALLBACK(connection_signal_handler), connection);
601
602         return connection;
603 }
604
605 static void free_vpn_connection_ipv4(struct vpn_connection_ipv4 *ipv4_info)
606 {
607         DBG("");
608
609         g_free(ipv4_info->address);
610         g_free(ipv4_info->netmask);
611         g_free(ipv4_info->gateway);
612         g_free(ipv4_info->peer);
613         g_free(ipv4_info);
614 }
615
616 static void free_vpn_connection_ipv6(struct vpn_connection_ipv6 *ipv6_info)
617 {
618         DBG("");
619
620         g_free(ipv6_info->address);
621         g_free(ipv6_info->prefix_length);
622         g_free(ipv6_info->gateway);
623         g_free(ipv6_info->peer);
624         g_free(ipv6_info);
625 }
626
627 static void free_vpn_connection_route(gpointer data)
628 {
629         DBG("");
630
631         struct vpn_connection_route *route = data;
632
633         if (route == NULL) {
634                 DBG("Nothing to Delete!");
635                 return;
636         }
637
638         g_free(route->network);
639         g_free(route->netmask);
640         g_free(route->gateway);
641
642         g_free(route);
643 }
644
645 static void free_vpn_connection(gpointer data)
646 {
647         DBG("");
648
649         struct vpn_connection *connection = data;
650
651         if (connection == NULL)
652                 return;
653
654         if (connection->dbus_proxy != NULL)
655                 g_object_unref(connection->dbus_proxy);
656
657         if (connection->property_changed_cb_hash != NULL)
658                 g_hash_table_destroy(connection->property_changed_cb_hash);
659
660         g_free(connection->path);
661         g_free(connection->name);
662         g_free(connection->domain);
663         g_free(connection->host);
664
665         if (connection->ipv4)
666                 free_vpn_connection_ipv4(connection->ipv4);
667
668         if (connection->ipv6)
669                 free_vpn_connection_ipv6(connection->ipv6);
670
671         g_strfreev(connection->nameservers);
672
673         if (connection->user_routes)
674                 g_slist_free_full(connection->user_routes,
675                                 free_vpn_connection_route);
676
677         if (connection->server_routes)
678                 g_slist_free_full(connection->server_routes,
679                                 free_vpn_connection_route);
680
681         g_free(connection);
682 }
683 //LCOV_EXCL_STOP
684
685 static void create_vpn_connections(GVariant *connections)
686 {
687         GVariantIter *iter;
688         gchar *path;
689         GVariantIter *properties;
690
691         g_variant_get(connections, "(a(oa{sv}))", &iter);
692         if (g_variant_iter_n_children(iter) == 0) {
693                 g_variant_iter_free(iter);
694                 return;
695         }
696
697         while (g_variant_iter_loop(iter, "(oa{sv})", &path, &properties)) //LCOV_EXCL_LINE
698                 create_vpn_connection(path, properties); //LCOV_EXCL_LINE
699
700         g_variant_iter_free(iter); //LCOV_EXCL_LINE
701 }
702
703 //LCOV_EXCL_START
704 struct vpn_connection *get_connection_by_path(const gchar *path)
705 {
706         DBG("path: %s", path);
707
708         return g_hash_table_lookup(vpn_connection_hash, (gpointer)path);
709 }
710
711 gboolean add_vpn_connection(GVariant **parameters,
712                                 struct vpn_connection **connection)
713 {
714         gchar *print_str;
715         gchar *connection_path;
716         GVariantIter *properties;
717         gboolean ret;
718
719         g_variant_get(*parameters, "(oa{sv})", &connection_path, &properties);
720
721         print_str = g_variant_print(*parameters, TRUE);
722         DBG("connection path: %s, parameters: %s", connection_path, print_str);
723         g_free(print_str);
724
725         /*
726          * Lookup if it has existed in the hash table
727          */
728         *connection = g_hash_table_lookup(vpn_connection_hash,
729                                                 (gpointer) connection_path);
730         if (*connection != NULL) {
731                 DBG("Repetitive connection %s", (*connection)->name);
732
733                 ret = FALSE;
734         } else {
735                 *connection = create_vpn_connection(connection_path,
736                                                                 properties);
737                 if (*connection != NULL)
738                         ret = TRUE;
739                 else
740                         ret = FALSE;
741         }
742
743         g_variant_iter_free(properties);
744         return ret;
745 }
746
747 void remove_vpn_connection(struct vpn_connection *connection)
748 {
749         DBG("");
750
751         assert(connection != NULL);
752
753         vpn_connection_list = g_list_remove(vpn_connection_list,
754                                                 (gpointer)connection);
755
756         g_hash_table_remove(vpn_connection_hash,
757                         (gconstpointer)connection->path);
758 }
759 //LCOV_EXCL_STOP
760
761 void sync_vpn_connections(void)
762 {
763         DBG("");
764
765         gchar *print_str;
766         GVariant *connections;
767         GError *error = NULL;
768
769         connections = g_dbus_proxy_call_sync(get_vpn_manager_dbus_proxy(),
770                                                 "GetConnections", NULL,
771                                                 G_DBUS_CALL_FLAGS_NONE,
772                                                 -1, NULL, &error);
773         if (connections == NULL) {
774                 ERROR("error info: %s", error->message); //LCOV_EXCL_LINE
775                 g_error_free(error); //LCOV_EXCL_LINE
776                 return; //LCOV_EXCL_LINE
777         }
778
779         print_str = g_variant_print(connections, TRUE);
780         DBG("connections: %s", print_str);
781         g_free(print_str);
782
783         if (!vpn_connection_hash)
784                 vpn_connection_hash = g_hash_table_new_full(
785                                         g_str_hash, g_str_equal,
786                                         NULL, free_vpn_connection);
787         DBG("hash: %p", vpn_connection_hash);
788
789         create_vpn_connections(connections);
790
791         g_variant_unref(connections);
792 }
793
794 /**
795  * VPN Connection Methods
796  */
797 GList *vpn_get_connections(void)
798 {
799         DBG("");
800
801         return vpn_connection_list;
802 }
803
804 //LCOV_EXCL_START
805 struct vpn_connection *vpn_get_connection(
806                 const char *name, const char *host, const char *domain)
807 {
808         if (!name || !host)
809                 return NULL;
810
811         GList *iter;
812
813         for (iter = vpn_connection_list; iter != NULL;
814              iter = iter->next) {
815                 struct vpn_connection *connection =
816                     (struct vpn_connection *)(iter->data);
817
818                 if (g_str_equal(connection->name, name) &&
819                                 g_str_equal(connection->host, host) &&
820                                 (!domain || g_str_equal(connection->domain, domain)))
821                         return connection;
822         }
823
824         return NULL;
825 }
826
827 enum dvpnlib_err vpn_connection_clear_property(
828                                 struct vpn_connection *connection)
829 {
830         GVariant *value;
831
832         assert(connection != NULL);
833
834         /**
835          * Only supported the "UserRoutes" item now;
836          */
837         value = g_variant_new("(s)", "UserRoutes");
838
839         return common_set_interface_call_method_sync(connection->dbus_proxy,
840                                                 "ClearProperty", &value);
841 }
842
843 /**
844  * Asynchronous connect callback
845  */
846 static void connect_callback(GObject *source_object,
847                              GAsyncResult *res, gpointer user_data)
848 {
849         GError *error = NULL;
850         enum dvpnlib_err error_type = DVPNLIB_ERR_NONE;
851         GVariant *ret;
852         struct common_reply_data *reply_data;
853         struct vpn_connection *connection;
854
855         reply_data = user_data;
856         if (!reply_data)
857                 return;
858
859         connection = reply_data->user;
860         if (!connection)
861                 goto done;
862
863         if (!connection->dbus_proxy)
864                 goto done;
865
866         ret = g_dbus_proxy_call_finish(connection->dbus_proxy, res, &error);
867         if (!ret) {
868                 DBG("%s", error->message);
869                 error_type = get_error_type(error);
870
871                 g_error_free(error);
872         } else
873                 g_variant_unref(ret);
874
875         if (reply_data->cb) {
876                 dvpnlib_reply_cb callback = reply_data->cb;
877                 callback(error_type, reply_data->data);
878         }
879
880 done:
881         g_free(reply_data);
882 }
883
884 enum dvpnlib_err vpn_connection_connect(struct vpn_connection *connection,
885                                  dvpnlib_reply_cb callback,
886                                  void *user_data)
887 {
888         DBG("");
889
890         struct common_reply_data *reply_data;
891
892         assert(connection != NULL);
893
894         reply_data =
895             common_reply_data_new(callback, user_data, connection, TRUE);
896
897         return common_set_interface_call_method(connection->dbus_proxy,
898                                          "Connect", NULL,
899                                          (GAsyncReadyCallback)
900                                          connect_callback, reply_data);
901 }
902
903 enum dvpnlib_err
904 vpn_connection_disconnect(struct vpn_connection *connection)
905 {
906         DBG("");
907
908         assert(connection != NULL);
909
910         return common_set_interface_call_method_sync(connection->dbus_proxy,
911                                          "Disconnect", NULL);
912
913 }
914
915 const char *vpn_connection_get_type(
916                                         struct vpn_connection *connection)
917 {
918         assert(connection != NULL);
919
920         return connection->type;
921 }
922
923 const char *vpn_connection_get_name(
924                                         struct vpn_connection *connection)
925 {
926         assert(connection != NULL);
927
928         return connection->name;
929 }
930
931 const char *vpn_connection_get_path(
932                                 struct vpn_connection *connection)
933 {
934         assert(connection != NULL);
935
936         return connection->path;
937 }
938
939 const char *vpn_connection_get_domain(
940                                 struct vpn_connection *connection)
941 {
942         assert(connection != NULL);
943
944         return connection->domain;
945 }
946
947 const char *vpn_connection_get_host(
948                                 struct vpn_connection *connection)
949 {
950         assert(connection != NULL);
951
952         return connection->host;
953 }
954
955 bool vpn_connection_get_immutable(
956                                 struct vpn_connection *connection)
957 {
958         assert(connection != NULL);
959
960         return connection->immutable;
961 }
962
963 int vpn_connection_get_index(
964                                 struct vpn_connection *connection)
965 {
966         assert(connection != NULL);
967
968         return connection->index;
969 }
970
971 enum vpn_connection_state vpn_connection_get_state(
972                                 struct vpn_connection *connection)
973 {
974         assert(connection != NULL);
975
976         return connection->state;
977 }
978
979 const struct vpn_connection_ipv4 *vpn_connection_get_ipv4(
980                                 struct vpn_connection *connection)
981 {
982         assert(connection != NULL);
983
984         return connection->ipv4;
985 }
986
987 const struct vpn_connection_ipv6 *vpn_connection_get_ipv6(
988                                 struct vpn_connection *connection)
989 {
990         assert(connection != NULL);
991
992         return connection->ipv6;
993 }
994
995 char **vpn_connection_get_nameservers(
996                                 struct vpn_connection *connection)
997 {
998         assert(connection != NULL);
999
1000         return connection->nameservers;
1001 }
1002
1003 GSList *vpn_connection_get_user_routes(
1004                                 struct vpn_connection *connection)
1005 {
1006         assert(connection != NULL);
1007
1008         return connection->user_routes;
1009 }
1010
1011 GSList *vpn_connection_get_server_routes(
1012                                 struct vpn_connection *connection)
1013 {
1014         assert(connection != NULL);
1015
1016         return connection->server_routes;
1017 }
1018
1019 enum dvpnlib_err vpn_connection_set_property_changed_cb(
1020                                 struct vpn_connection *connection,
1021                                 enum vpn_connection_property_type type,
1022                                 vpn_connection_property_changed_cb cb,
1023                                 void *user_data)
1024 {
1025         DBG("");
1026
1027         if (connection == NULL)
1028                 return DVPNLIB_ERR_INVALID_PARAMETER;
1029
1030         struct connection_property_changed_cb *property_changed_cb_t =
1031                         g_try_new0(struct connection_property_changed_cb, 1);
1032         if (property_changed_cb_t == NULL) {
1033                 ERROR("no memory");
1034                 return DVPNLIB_ERR_FAILED;
1035         }
1036         property_changed_cb_t->property_changed_cb = cb;
1037         property_changed_cb_t->user_data = user_data;
1038
1039         g_hash_table_insert(connection->property_changed_cb_hash,
1040                                 GINT_TO_POINTER(type),
1041                                 (gpointer)property_changed_cb_t);
1042
1043         return DVPNLIB_ERR_NONE;
1044 }
1045
1046 enum dvpnlib_err vpn_connection_unset_property_changed_cb(
1047                                 struct vpn_connection *connection,
1048                                 enum vpn_connection_property_type type)
1049 {
1050         DBG("");
1051
1052         if (connection == NULL)
1053                 return DVPNLIB_ERR_INVALID_PARAMETER;
1054
1055         struct connection_property_changed_cb *property_changed_cb_t =
1056                         get_connection_property_changed_cb(connection, type);
1057
1058         if (property_changed_cb_t == NULL) {
1059                 DBG("Can't find connection property changed callback");
1060                 return DVPNLIB_ERR_FAILED;
1061         }
1062
1063         g_hash_table_remove(connection->property_changed_cb_hash,
1064                                 GINT_TO_POINTER(type));
1065
1066         return DVPNLIB_ERR_NONE;
1067 }
1068 //LCOV_EXCL_STOP