dnsproxy: Only one copy of the relevant buffers will be made to a TCP request
[framework/connectivity/connman.git] / plugins / dundee.c
index 1057235..045f639 100644 (file)
@@ -23,6 +23,7 @@
 #include <config.h>
 #endif
 
+#include <string.h>
 #include <errno.h>
 
 #include <gdbus.h>
 
 #define DUNDEE_SERVICE                 "org.ofono.dundee"
 #define DUNDEE_MANAGER_INTERFACE       DUNDEE_SERVICE ".Manager"
+#define DUNDEE_DEVICE_INTERFACE                DUNDEE_SERVICE ".Device"
 
 #define DEVICE_ADDED                   "DeviceAdded"
 #define DEVICE_REMOVED                 "DeviceRemoved"
+#define PROPERTY_CHANGED               "PropertyChanged"
 
+#define GET_PROPERTIES                 "GetProperties"
+#define SET_PROPERTY                   "SetProperty"
 #define GET_DEVICES                    "GetDevices"
 
 #define TIMEOUT 40000
@@ -52,6 +57,9 @@ struct dundee_data {
        char *path;
        char *name;
 
+       struct connman_device *device;
+       struct connman_network *network;
+
        connman_bool_t active;
 
        int index;
@@ -60,18 +68,236 @@ struct dundee_data {
        enum connman_ipconfig_method method;
        struct connman_ipaddress *address;
        char *nameservers;
+
+       DBusPendingCall *call;
 };
 
+static char *get_ident(const char *path)
+{
+       char *pos;
+
+       if (*path != '/')
+               return NULL;
+
+       pos = strrchr(path, '/');
+       if (pos == NULL)
+               return NULL;
+
+       return pos + 1;
+}
+
+static void create_device(struct dundee_data *info)
+{
+       struct connman_device *device;
+       char *ident;
+
+       DBG("%s", info->path);
+
+       ident = g_strdup(get_ident(info->path));
+       device = connman_device_create(ident, CONNMAN_DEVICE_TYPE_BLUETOOTH);
+       if (device == NULL)
+               goto out;
+
+       DBG("device %p", device);
+
+       connman_device_set_ident(device, ident);
+
+       connman_device_set_string(device, "Path", info->path);
+
+       connman_device_set_data(device, info);
+
+       if (connman_device_register(device) < 0) {
+               connman_error("Failed to register DUN device");
+               connman_device_unref(device);
+               goto out;
+       }
+
+       info->device = device;
+
+out:
+       g_free(ident);
+}
+
+static void destroy_device(struct dundee_data *info)
+{
+       connman_device_set_powered(info->device, FALSE);
+
+       if (info->call != NULL)
+               dbus_pending_call_cancel(info->call);
+
+       if (info->network != NULL) {
+               connman_device_remove_network(info->device, info->network);
+               connman_network_unref(info->network);
+               info->network = NULL;
+       }
+
+       connman_device_unregister(info->device);
+       connman_device_unref(info->device);
+
+       info->device = NULL;
+}
+
 static void device_destroy(gpointer data)
 {
        struct dundee_data *info = data;
 
+       if (info->device != NULL)
+               destroy_device(info);
+
        g_free(info->path);
        g_free(info->name);
 
        g_free(info);
 }
 
+static void create_network(struct dundee_data *info)
+{
+       struct connman_network *network;
+       const char *group;
+
+       DBG("%s", info->path);
+
+       network = connman_network_create(info->path,
+                               CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN);
+       if (network == NULL)
+               return;
+
+       DBG("network %p", network);
+
+       connman_network_set_data(network, info);
+
+       connman_network_set_string(network, "Path",
+                               info->path);
+
+       connman_network_set_name(network, info->name);
+
+       group = get_ident(info->path);
+       connman_network_set_group(network, group);
+
+       connman_network_set_available(network, TRUE);
+
+       if (connman_device_add_network(info->device, network) < 0) {
+               connman_network_unref(network);
+               return;
+       }
+
+       info->network = network;
+}
+
+static void set_connected(struct dundee_data *info)
+{
+       DBG("%s", info->path);
+
+       connman_inet_ifup(info->index);
+
+       connman_network_set_index(info->network, info->index);
+       connman_network_set_ipv4_method(info->network,
+                                       CONNMAN_IPCONFIG_METHOD_FIXED);
+       connman_network_set_ipaddress(info->network, info->address);
+       connman_network_set_nameservers(info->network, info->nameservers);
+
+       connman_network_set_connected(info->network, TRUE);
+}
+
+static void set_disconnected(struct dundee_data *info)
+{
+       DBG("%s", info->path);
+
+       connman_network_set_connected(info->network, FALSE);
+       connman_inet_ifdown(info->index);
+}
+
+static void set_property_reply(DBusPendingCall *call, void *user_data)
+{
+       struct dundee_data *info = user_data;
+       DBusMessage *reply;
+       DBusError error;
+
+       DBG("%s", info->path);
+
+       info->call = NULL;
+
+       dbus_error_init(&error);
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       if (dbus_set_error_from_message(&error, reply)) {
+               connman_error("Failed to change property: %s %s %s",
+                               info->path, error.name, error.message);
+               dbus_error_free(&error);
+
+               connman_network_set_error(info->network,
+                                       CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL);
+       }
+
+       dbus_message_unref(reply);
+
+       dbus_pending_call_unref(call);
+}
+
+static int set_property(struct dundee_data *info,
+                       const char *property, int type, void *value)
+{
+       DBusMessage *message;
+       DBusMessageIter iter;
+
+       DBG("%s %s", info->path, property);
+
+       message = dbus_message_new_method_call(DUNDEE_SERVICE, info->path,
+                                       DUNDEE_DEVICE_INTERFACE, SET_PROPERTY);
+       if (message == NULL)
+               return -ENOMEM;
+
+       dbus_message_iter_init_append(message, &iter);
+       connman_dbus_property_append_basic(&iter, property, type, value);
+
+       if (dbus_connection_send_with_reply(connection, message,
+                       &info->call, TIMEOUT) == FALSE) {
+               connman_error("Failed to change property: %s %s",
+                               info->path, property);
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
+
+       if (info->call == NULL) {
+               connman_error("D-Bus connection not available");
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
+
+       dbus_pending_call_set_notify(info->call, set_property_reply,
+                                       info, NULL);
+
+       dbus_message_unref(message);
+
+       return -EINPROGRESS;
+}
+
+static int device_set_active(struct dundee_data *info)
+{
+       dbus_bool_t active = TRUE;
+
+       DBG("%s", info->path);
+
+       return set_property(info, "Active", DBUS_TYPE_BOOLEAN,
+                               &active);
+}
+
+static int device_set_inactive(struct dundee_data *info)
+{
+       dbus_bool_t active = FALSE;
+       int err;
+
+       DBG("%s", info->path);
+
+       err = set_property(info, "Active", DBUS_TYPE_BOOLEAN,
+                               &active);
+       if (err == -EINPROGRESS)
+               return 0;
+
+       return err;
+}
+
 static int network_probe(struct connman_network *network)
 {
        DBG("network %p", network);
@@ -86,16 +312,20 @@ static void network_remove(struct connman_network *network)
 
 static int network_connect(struct connman_network *network)
 {
+       struct dundee_data *info = connman_network_get_data(network);
+
        DBG("network %p", network);
 
-       return 0;
+       return device_set_active(info);
 }
 
 static int network_disconnect(struct connman_network *network)
 {
+       struct dundee_data *info = connman_network_get_data(network);
+
        DBG("network %p", network);
 
-       return 0;
+       return device_set_inactive(info);
 }
 
 static struct connman_network_driver network_driver = {
@@ -109,9 +339,24 @@ static struct connman_network_driver network_driver = {
 
 static int dundee_probe(struct connman_device *device)
 {
+       GHashTableIter iter;
+       gpointer key, value;
+
        DBG("device %p", device);
 
-       return 0;
+       if (dundee_devices == NULL)
+               return -ENOTSUP;
+
+       g_hash_table_iter_init(&iter, dundee_devices);
+
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               struct dundee_data *info = value;
+
+               if (device == info->device)
+                       return 0;
+       }
+
+       return -ENOTSUP;
 }
 
 static void dundee_remove(struct connman_device *device)
@@ -245,6 +490,69 @@ out:
        g_free(gateway);
 }
 
+static gboolean device_changed(DBusConnection *connection,
+                               DBusMessage *message,
+                               void *user_data)
+{
+       const char *path = dbus_message_get_path(message);
+       struct dundee_data *info = NULL;
+       DBusMessageIter iter, value;
+       const char *key;
+       const char *signature = DBUS_TYPE_STRING_AS_STRING
+               DBUS_TYPE_VARIANT_AS_STRING;
+
+       if (dbus_message_has_signature(message, signature) == FALSE) {
+               connman_error("dundee signature does not match");
+               return TRUE;
+       }
+
+       info = g_hash_table_lookup(dundee_devices, path);
+       if (info == NULL)
+               return TRUE;
+
+       if (dbus_message_iter_init(message, &iter) == FALSE)
+               return TRUE;
+
+       dbus_message_iter_get_basic(&iter, &key);
+
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &value);
+
+       /*
+        * Dundee guarantees the ordering of Settings and
+        * Active. Settings will always be send before Active = True.
+        * That means we don't have to order here.
+        */
+       if (g_str_equal(key, "Active") == TRUE) {
+               dbus_message_iter_get_basic(&value, &info->active);
+
+               DBG("%s Active %d", info->path, info->active);
+
+               if (info->active == TRUE)
+                       set_connected(info);
+               else
+                       set_disconnected(info);
+       } else if (g_str_equal(key, "Settings") == TRUE) {
+               DBG("%s Settings", info->path);
+
+               extract_settings(&value, info);
+       } else if (g_str_equal(key, "Name") == TRUE) {
+               char *name;
+
+               dbus_message_iter_get_basic(&value, &name);
+
+               g_free(info->name);
+               info->name = g_strdup(name);
+
+               DBG("%s Name %s", info->path, info->name);
+
+               connman_network_set_name(info->network, info->name);
+               connman_network_update(info->network);
+       }
+
+       return TRUE;
+}
+
 static void add_device(const char *path, DBusMessageIter *properties)
 {
        struct dundee_data *info;
@@ -292,6 +600,12 @@ static void add_device(const char *path, DBusMessageIter *properties)
        }
 
        g_hash_table_insert(dundee_devices, g_strdup(path), info);
+
+       create_device(info);
+       create_network(info);
+
+       if (info->active == TRUE)
+               set_connected(info);
 }
 
 static gboolean device_added(DBusConnection *connection, DBusMessage *message,
@@ -462,6 +776,7 @@ static void dundee_disconnect(DBusConnection *connection, void *user_data)
 static guint watch;
 static guint added_watch;
 static guint removed_watch;
+static guint device_watch;
 
 static int dundee_init(void)
 {
@@ -484,7 +799,15 @@ static int dundee_init(void)
                                                DEVICE_REMOVED, device_removed,
                                                NULL, NULL);
 
-       if (watch == 0 || added_watch == 0 || removed_watch == 0) {
+       device_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+                                               DUNDEE_DEVICE_INTERFACE,
+                                               PROPERTY_CHANGED,
+                                               device_changed,
+                                               NULL, NULL);
+
+
+       if (watch == 0 || added_watch == 0 || removed_watch == 0 ||
+                       device_watch == 0) {
                err = -EIO;
                goto remove;
        }
@@ -505,6 +828,7 @@ remove:
        g_dbus_remove_watch(connection, watch);
        g_dbus_remove_watch(connection, added_watch);
        g_dbus_remove_watch(connection, removed_watch);
+       g_dbus_remove_watch(connection, device_watch);
 
        dbus_connection_unref(connection);
 
@@ -516,6 +840,7 @@ static void dundee_exit(void)
        g_dbus_remove_watch(connection, watch);
        g_dbus_remove_watch(connection, added_watch);
        g_dbus_remove_watch(connection, removed_watch);
+       g_dbus_remove_watch(connection, device_watch);
 
        connman_device_driver_unregister(&dundee_driver);
        connman_network_driver_unregister(&network_driver);