From 63fced3833f6e5f9dd17182f0087034db3cb1f11 Mon Sep 17 00:00:00 2001 From: arno Date: Thu, 25 Feb 2010 23:29:45 +0100 Subject: [PATCH] localnet improvements --- example/Makefile.am | 4 + example/localnet-address-example.c | 194 +++++++ example/localnet-position-example.c | 161 ++++++ providers/localnet/geoclue-localnet.c | 721 +++++++++++++++++++------- providers/localnet/geoclue-localnet.provider | 2 +- providers/localnet/geoclue-localnet.xml | 26 +- 6 files changed, 902 insertions(+), 206 deletions(-) create mode 100644 example/localnet-address-example.c create mode 100644 example/localnet-position-example.c diff --git a/example/Makefile.am b/example/Makefile.am index 16d3fec..43a43b8 100644 --- a/example/Makefile.am +++ b/example/Makefile.am @@ -1,23 +1,27 @@ noinst_PROGRAMS = \ position-example \ position-async-example \ master-example \ master-pos-example \ address-example \ geocode-example \ + localnet-address-example \ + localnet-position-example \ revgeocode-example \ common-example common_example_SOURCES = common-example.c position_example_SOURCES = position-example.c position_async_example_SOURCES = position-async-example.c address_example_SOURCES = address-example.c geocode_example_SOURCES = geocode-example.c revgeocode_example_SOURCES = revgeocode-example.c +localnet_address_example_SOURCES = localnet-address-example.c +localnet_position_example_SOURCES = localnet-position-example.c master_example_SOURCES = master-example.c master_pos_example_SOURCES = master-pos-example.c AM_CFLAGS = $(GEOCLUE_CFLAGS) LDADD = \ $(GEOCLUE_LIBS) \ $(top_builddir)/geoclue/libgeoclue.la diff --git a/example/localnet-address-example.c b/example/localnet-address-example.c new file mode 100644 index 0000000..d70865d --- /dev/null +++ b/example/localnet-address-example.c @@ -0,0 +1,194 @@ +/* + * Geoclue + * localnet-address-example.c - Example using localnet provider for + * getting/setting addresses + * + * Author: Arno Renevier + * Copyright 2010 Arno Renevier + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include +#include + +#define LOCALNET_SERVICE "org.freedesktop.Geoclue.Providers.Localnet" +#define LOCALNET_PATH "/org/freedesktop/Geoclue/Providers/Localnet" + +/* GHFunc, use with g_hash_table_foreach */ +static void +print_address_key_and_value (char *key, char *value, gpointer user_data) +{ + g_print (" %s: %s\n", key, value); +} + +static gboolean +set_localnet_address (char *country_code, + char *country, + char *region, + char *locality, + char *area, + char *postalcode, + char *street) +{ + GError *error = NULL; + DBusGConnection *connection = NULL; + DBusGProxy *proxy; + GHashTable *address; + + connection = dbus_g_bus_get (DBUS_BUS_SESSION, + &error); + if (connection == NULL) { + g_printerr ("Failed to open connection to bus: %s\n", error->message); + g_error_free (error); + return FALSE; + } + + proxy = dbus_g_proxy_new_for_name (connection, + "org.freedesktop.Geoclue.Providers.Localnet", + "/org/freedesktop/Geoclue/Providers/Localnet", + "org.freedesktop.Geoclue.Localnet"); + if (proxy == NULL) { + g_printerr ("Failed to create proxy object\n"); + dbus_g_connection_unref (connection); + return FALSE; + } + + address = geoclue_address_details_new (); + if (country_code && (strlen (country_code) > 0)) { + geoclue_address_details_insert (address, GEOCLUE_ADDRESS_KEY_COUNTRYCODE, country_code); + } + if (country && (strlen (country) > 0)) { + geoclue_address_details_insert (address, + (GEOCLUE_ADDRESS_KEY_COUNTRY), + (country)); + } + if (region && (strlen (region) > 0)) { + geoclue_address_details_insert (address, GEOCLUE_ADDRESS_KEY_REGION, region); + } + if (locality && (strlen (locality) > 0)) { + geoclue_address_details_insert (address, GEOCLUE_ADDRESS_KEY_LOCALITY, locality); + } + if (area && (strlen (area) > 0)) { + geoclue_address_details_insert (address, GEOCLUE_ADDRESS_KEY_AREA, area); + } + if (postalcode && (strlen (postalcode) > 0)) { + geoclue_address_details_insert (address, GEOCLUE_ADDRESS_KEY_POSTALCODE, postalcode); + } + if (street && (strlen (street) > 0)) { + geoclue_address_details_insert (address, GEOCLUE_ADDRESS_KEY_STREET, street); + } + + if (!dbus_g_proxy_call (proxy, "SetAddress", &error, + dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_STRING), + address, G_TYPE_INVALID, G_TYPE_INVALID)) { + g_printerr ("Failed to call SetAddress: %s\n", error->message); + g_error_free (error); + g_hash_table_destroy (address); + dbus_g_connection_unref (connection); + g_object_unref (proxy); + return FALSE; + } + + g_hash_table_destroy (address); + dbus_g_connection_unref (connection); + g_object_unref (proxy); + return TRUE; +} + +static GHashTable* +get_localnet_address (GeoclueAddress* provider) +{ + GError *error = NULL; + int timestamp; + GHashTable *details = NULL; + GeoclueAccuracy *accuracy = NULL; + GeoclueAccuracyLevel level; + + /* Query current address */ + if (!geoclue_address_get_address (provider, ×tamp, + &details, &accuracy, + &error)) { + g_printerr ("Error getting address: %s\n", error->message); + g_error_free (error); + return NULL; + } + geoclue_accuracy_get_details (accuracy, &level, NULL, NULL); + + /* address data is in GHashTable details, need to turn that into a string */ + g_print ("Current address: (accuracy level %d)\n", level); + g_hash_table_foreach (details, (GHFunc)print_address_key_and_value, NULL); + + geoclue_accuracy_free (accuracy); + + return details; +} + +int main (int argc, char **argv) +{ + GHashTable* initial_details = NULL; + GeoclueAddress* provider = NULL; + + g_type_init(); + + provider = geoclue_address_new (LOCALNET_SERVICE, LOCALNET_PATH); + /* create new GeoclueAddress */ + if (provider == NULL) { + g_printerr ("Error while creating GeoclueAddress object.\n"); + return 1; + } + + /* get and display address */ + initial_details = get_localnet_address(provider); + + /* change address */ + if (set_localnet_address("", "Finland", "", "Helsinki", "", "", "Solnantie 24") == FALSE) { + g_printerr ("Error setting address.\n"); + g_object_unref(provider); + g_hash_table_destroy(initial_details); + return 1; + } + + /* show newly added address */ + get_localnet_address(provider); + + /* reset initial user address */ + if (initial_details) { + char *country_code = g_hash_table_lookup(initial_details, "country_code"); + char *country = g_hash_table_lookup(initial_details, "country"); + char *region = g_hash_table_lookup(initial_details, "region"); + char *locality = g_hash_table_lookup(initial_details, "locality"); + char *area = g_hash_table_lookup(initial_details, "area"); + char *postalcode = g_hash_table_lookup(initial_details, "postalcode"); + char *street = g_hash_table_lookup(initial_details, "street"); + set_localnet_address(country_code, + country, + region, + locality, + area, + postalcode, + street); + + g_hash_table_destroy(initial_details); + } + + g_object_unref(provider); + + return 0; +} diff --git a/example/localnet-position-example.c b/example/localnet-position-example.c new file mode 100644 index 0000000..c63cc2a --- /dev/null +++ b/example/localnet-position-example.c @@ -0,0 +1,161 @@ +/* + * Geoclue + * localnet-position-example.c - Example using localnet provider for + * getting/setting positions + * + * Author: Arno Renevier + * Copyright 2010 Arno Renevier + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include +#include + +#define LOCALNET_SERVICE "org.freedesktop.Geoclue.Providers.Localnet" +#define LOCALNET_PATH "/org/freedesktop/Geoclue/Providers/Localnet" + +static gboolean +set_localnet_position(double latitude, + double longitude, + double altitude, + double horizontal_accuracy, + double vertical_accuracy) +{ + GError *error = NULL; + DBusGConnection *connection = NULL; + DBusGProxy *proxy; + connection = dbus_g_bus_get (DBUS_BUS_SESSION, + &error); + if (connection == NULL) { + g_printerr ("Failed to open connection to bus: %s\n", error->message); + g_error_free (error); + return FALSE; + } + + proxy = dbus_g_proxy_new_for_name (connection, + "org.freedesktop.Geoclue.Providers.Localnet", + "/org/freedesktop/Geoclue/Providers/Localnet", + "org.freedesktop.Geoclue.Localnet"); + + if (proxy == NULL) { + g_printerr ("Failed to create proxy object\n"); + dbus_g_connection_unref (connection); + return FALSE; + } + if (!dbus_g_proxy_call (proxy, "SetPosition", &error, G_TYPE_DOUBLE, latitude, + G_TYPE_DOUBLE, longitude, G_TYPE_DOUBLE, altitude, G_TYPE_DOUBLE, horizontal_accuracy, + G_TYPE_DOUBLE, vertical_accuracy, G_TYPE_INVALID, G_TYPE_INVALID)) { + g_printerr ("Failed to call SetPosition: %s\n", error->message); + g_error_free (error); + dbus_g_connection_unref (connection); + g_object_unref (proxy); + return FALSE; + } + + dbus_g_connection_unref (connection); + g_object_unref (proxy); + return TRUE; +} + +static gboolean +get_localnet_position(GeocluePosition *provider, + double *latitude, + double *longitude, + double *altitude, + double *horizontal_accuracy, + double *vertical_accuracy) +{ + GError *error = NULL; + GeocluePositionFields fields; + GeoclueAccuracy *accuracy = NULL; + + fields = geoclue_position_get_position (provider, NULL, + latitude, longitude, + altitude, &accuracy, + &error); + if (error) { + g_printerr ("Error getting position: %s\n", error->message); + g_error_free (error); + return FALSE; + } + + /* Print out coordinates if they are valid */ + if (fields & GEOCLUE_POSITION_FIELDS_LATITUDE && + fields & GEOCLUE_POSITION_FIELDS_LONGITUDE) { + + GeoclueAccuracyLevel level; + geoclue_accuracy_get_details (accuracy, &level, horizontal_accuracy, vertical_accuracy); + + g_print ("Current position:\n"); + g_print ("\t%f, %f\n", *latitude, *longitude); + g_print ("\tAccuracy level %d (%.0f meters horizontal accuracy)\n", + level, *horizontal_accuracy); + + geoclue_accuracy_free (accuracy); + return TRUE; + } else { + g_print ("Latitude and longitude not available.\n"); + geoclue_accuracy_free (accuracy); + return FALSE; + } +} + +int main (int argc, char **argv) +{ + GeocluePosition* provider = NULL; + double initial_latitude, initial_longitude, initial_altitude = 0.0; + double initial_horiz_acc, initial_vert_acc = 0.0; + double latitude, longitude, altitude = 0.0; + double horiz_acc, vert_acc = 0.0; + + g_type_init(); + + provider = geoclue_position_new (LOCALNET_SERVICE, LOCALNET_PATH); + /* create new GeocluePosition */ + if (provider == NULL) { + g_printerr ("Error while creating GeocluePosition object.\n"); + return 1; + } + + /* get and display position */ + get_localnet_position(provider, + &initial_latitude, &initial_longitude, &initial_altitude, + &initial_horiz_acc, &initial_vert_acc); + + /* change position */ + if (set_localnet_position(60.2, 24.9, 117, 500, 0) == FALSE) { + g_printerr ("Error setting position.\n"); + g_object_unref(provider); + return 1; + } + + /* show newly added position */ + get_localnet_position(provider, + &latitude, &longitude, &altitude, + &horiz_acc, &vert_acc); + + /* reset initial user position */ + set_localnet_position(initial_latitude, initial_longitude, initial_altitude, + initial_horiz_acc, initial_vert_acc); + + g_object_unref(provider); + + return 0; +} diff --git a/providers/localnet/geoclue-localnet.c b/providers/localnet/geoclue-localnet.c index 80501cb..1afaae7 100644 --- a/providers/localnet/geoclue-localnet.c +++ b/providers/localnet/geoclue-localnet.c @@ -4,44 +4,38 @@ * (~/.config/geoclue-localnet-gateways). * * The keyfile should contain entries like this: * * [00:1D:7E:55:8D:80] * country=Finland * street=Solnantie 24 * locality=Helsinki + * latitude=60.2 + * longitude=24.9 + * horizontal_accuracy=500 * - * Only address interface is supported so far. + * Position and address interfaces are supported so far. * - * Any application that can obtain a reliable address can submit it - * to localnet provider through the D-Bus API -- it will then be provided - * whenever connected to the same router: + * Any application that can obtain a reliable address can submit it as a + * GeoclueAddress to localnet provider through the D-Bus API -- it will then be + * provided whenever connected to the same router: * org.freedesktop.Geoclue.Localnet.SetAddress - * org.freedesktop.Geoclue.Localnet.SetAddressFields + * + * It's also possible to set a default address, that will be used if router + * informations cannot be determined or if no address is provided for found + * router. + * org.freedesktop.Geoclue.Localnet.SetAddressDefault * * SetAddress allows setting the current address as a GeoclueAddress, - * while SetAddressFields is a convenience version with separate - * address fields. Shell example using SetAddressFields: * - dbus-send --print-reply --type=method_call \ - --dest=org.freedesktop.Geoclue.Providers.Localnet \ - /org/freedesktop/Geoclue/Providers/Localnet \ - org.freedesktop.Geoclue.Localnet.SetAddressFields \ - string: \ - string:"Finland" \ - string: \ - string:"Helsinki" \ - string: \ - string: \ - string:"Solnantie 24" - - * This would make the provider save the specified address with current - * router mac address. It will provide the address to clients whenever - * the computer is connected to the same router again. + * For position, message names are + * org.freedesktop.Geoclue.Localnet.SetPosition + * and + * org.freedesktop.Geoclue.Localnet.SetPositionDefault * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -53,33 +47,44 @@ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * */ #include #include +#include #include #include #include #include #include #include #include #include +#include #define KEYFILE_NAME "geoclue-localnet-gateways" +#define GATEWAY_DEFAULT "default" + +#define LATITUDE_INVALID -200.0 +#define LONGITUDE_INVALID -200.0 +#define ALTITUDE_INVALID -100000.0 + typedef struct { char *mac; GHashTable *address; - GeoclueAccuracy *accuracy; + double latitude; + double longitude; + double altitude; + GeoclueAccuracy *accuracy; } Gateway; typedef struct { GcProvider parent; GMainLoop *loop; @@ -90,23 +95,34 @@ typedef struct { typedef struct { GcProviderClass parent_class; } GeoclueLocalnetClass; #define GEOCLUE_TYPE_LOCALNET (geoclue_localnet_get_type ()) #define GEOCLUE_LOCALNET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GEOCLUE_TYPE_LOCALNET, GeoclueLocalnet)) static void geoclue_localnet_address_init (GcIfaceAddressClass *iface); +static void geoclue_localnet_position_init (GcIfacePositionClass *iface); G_DEFINE_TYPE_WITH_CODE (GeoclueLocalnet, geoclue_localnet, GC_TYPE_PROVIDER, G_IMPLEMENT_INTERFACE (GC_TYPE_IFACE_ADDRESS, - geoclue_localnet_address_init)) + geoclue_localnet_address_init) + G_IMPLEMENT_INTERFACE (GC_TYPE_IFACE_POSITION, + geoclue_localnet_position_init)) static gboolean geoclue_localnet_set_address (GeoclueLocalnet *localnet, GHashTable *address, GError **error); -static gboolean geoclue_localnet_set_address_fields (GeoclueLocalnet *localnet, char *country_code, char *country, char *region, char *locality, char *area, char *postalcode, char *street, GError **error); +static gboolean geoclue_localnet_set_address_default (GeoclueLocalnet *localnet, GHashTable *address, GError **error); +static gboolean geoclue_localnet_set_position (GeoclueLocalnet *localnet, + double latitude, double longitude, double altitude, + double horizontal_accuracy, double vertical_accuracy, + GError **error); +static gboolean geoclue_localnet_set_position_default (GeoclueLocalnet *localnet, + double latitude, double longitude, double altitude, + double horizontal_accuracy, double vertical_accuracy, + GError **error); #include "geoclue-localnet-glue.h" static gboolean get_status (GcIfaceGeoclue *gc, GeoclueStatus *status, GError **error) { @@ -255,58 +271,112 @@ get_mac_address (char **mac) entry++; } g_free (route_gateway); g_strfreev (lines); return *mac ? 1 : 0; } +static gboolean +valid_latitude (double latitude) +{ + return (latitude >= -90.0 && latitude <= 90.0); +} + +static gboolean +valid_longitude (double longitude) +{ + return (longitude >= -180.0 && longitude <= 180.0); +} + +static gboolean +valid_altitude (double altitude) +{ + return (altitude > ALTITUDE_INVALID); +} + +static gboolean +valid_key_for_address(char *key) +{ + if ((g_ascii_strcasecmp (key, GEOCLUE_ADDRESS_KEY_COUNTRYCODE) == 0) || + (g_ascii_strcasecmp (key, GEOCLUE_ADDRESS_KEY_COUNTRY) == 0) || + (g_ascii_strcasecmp (key, GEOCLUE_ADDRESS_KEY_REGION) == 0) || + (g_ascii_strcasecmp (key, GEOCLUE_ADDRESS_KEY_LOCALITY) == 0) || + (g_ascii_strcasecmp (key, GEOCLUE_ADDRESS_KEY_AREA) == 0) || + (g_ascii_strcasecmp (key, GEOCLUE_ADDRESS_KEY_POSTALCODE) == 0) || + (g_ascii_strcasecmp (key, GEOCLUE_ADDRESS_KEY_STREET) == 0)) { + return TRUE; + } else { + return FALSE; + } +} + static void geoclue_localnet_load_gateways_from_keyfile (GeoclueLocalnet *localnet, GKeyFile *keyfile) { char **groups; char **g; GError *error = NULL; + double horizontal_accuracy, vertical_accuracy; groups = g_key_file_get_groups (keyfile, NULL); g = groups; while (*g) { GeoclueAccuracyLevel level; char **keys; char **k; Gateway *gateway = g_new0 (Gateway, 1); gateway->mac = g_ascii_strdown (*g, -1); gateway->address = geoclue_address_details_new (); + + gateway->latitude = LATITUDE_INVALID; + gateway->longitude = LONGITUDE_INVALID; + gateway->altitude = ALTITUDE_INVALID; + horizontal_accuracy = 0.0; + vertical_accuracy = 0.0; /* read all keys in the group as address fields */ keys = g_key_file_get_keys (keyfile, *g, NULL, &error); if (error) { g_warning ("Could not load keys for group [%s] from %s: %s", *g, localnet->keyfile_name, error->message); g_error_free (error); error = NULL; } k = keys; while (*k) { char *value; - value = g_key_file_get_string (keyfile, *g, *k, NULL); - g_hash_table_insert (gateway->address, - *k, value); + value = g_key_file_get_string (keyfile, *g, *k, NULL); + if (valid_key_for_address (*k)) { + geoclue_address_details_insert (gateway->address, *k, value); + } else if (g_ascii_strcasecmp (*k, "latitude") == 0) { + gateway->latitude = atof (value); + } else if (g_ascii_strcasecmp (*k, "longitude") == 0) { + gateway->longitude = atof (value); + } else if (g_ascii_strcasecmp (*k, "altitude") == 0) { + gateway->altitude = atof (value); + } else if (g_ascii_strcasecmp (*k, "accuracy") == 0 || + g_ascii_strcasecmp (*k, "horizontal_accuracy") == 0) { + horizontal_accuracy = atof (value); + } else if (g_ascii_strcasecmp (*k, "vertical_accuracy") == 0) { + vertical_accuracy = atof (value); + } + g_free (value); k++; } g_free (keys); level = geoclue_address_details_get_accuracy_level (gateway->address); - gateway->accuracy = geoclue_accuracy_new (level, 0, 0); + gateway->accuracy = geoclue_accuracy_new (level, horizontal_accuracy, vertical_accuracy); localnet->gateways = g_slist_prepend (localnet->gateways, gateway); g++; } g_strfreev (groups); } @@ -348,18 +418,17 @@ geoclue_localnet_init (GeoclueLocalnet *localnet) /* load known addresses from keyfile */ dir = g_get_user_config_dir (); g_mkdir_with_parents (dir, 0755); localnet->keyfile_name = g_build_filename (dir, KEYFILE_NAME, NULL); keyfile = g_key_file_new (); if (!g_key_file_load_from_file (keyfile, localnet->keyfile_name, G_KEY_FILE_NONE, &error)) { - g_warning ("Could not load keyfile %s: %s", - localnet->keyfile_name, error->message); + /* no warning; it's valid to have a not yet existing config file */ g_error_free (error); } geoclue_localnet_load_gateways_from_keyfile (localnet, keyfile); g_key_file_free (keyfile); } @@ -371,218 +440,478 @@ typedef struct { static void add_address_detail_to_keyfile (char *key, char *value, localnet_keyfile_group *group) { g_key_file_set_string (group->keyfile, group->group_name, key, value); } +/* filters *details to keep only valid keys */ +GHashTable * +filter_address_details (GHashTable *details) +{ + GList *keys = g_hash_table_get_keys (details); + GList *li; + for(li = keys; li != NULL; li = li->next) { + if (!valid_key_for_address (li->data)) { + g_hash_table_remove (details, li->data); + } + } + g_list_free (keys); + return details; +} + +static gboolean +geoclue_localnet_save_infos_for_gateway(GeoclueLocalnet *localnet, + gchar *mac, + double latitude, + double longitude, + double altitude, + GeoclueAccuracy *accuracy, + GHashTable *address_details, + GError **error) +{ + GKeyFile *keyfile; + GError *int_err = NULL; + char value[30]; + char *file_content; + double hor_acc, vert_acc; + localnet_keyfile_group *keyfile_group; + + if (address_details != NULL) { + address_details = filter_address_details (address_details); + } + + keyfile = g_key_file_new (); + /* reload keyfile just in case it's changed */ + if (!g_key_file_load_from_file (keyfile, localnet->keyfile_name, + G_KEY_FILE_NONE, &int_err)) { + /* no warning; it's valid to have a not yet existing config file */ + g_error_free (int_err); + int_err = NULL; + } + + /* remove old group (if exists) and add new to GKeyFile */ + g_key_file_remove_group (keyfile, mac, NULL); + + keyfile_group = g_new0 (localnet_keyfile_group, 1); + keyfile_group->keyfile = keyfile; + keyfile_group->group_name = mac; + if (address_details != NULL) { + g_hash_table_foreach (address_details, (GHFunc) add_address_detail_to_keyfile, keyfile_group); + } + g_free (keyfile_group); + + if (valid_latitude (latitude)) { + if (!g_snprintf (value, 29, "%f", latitude)) { + g_warning ("memory error when setting address"); + return FALSE; + } + g_key_file_set_string (keyfile, mac, + "latitude", (value)); + } + + if (valid_longitude (longitude)) { + if (!g_snprintf (value, 29, "%f", longitude)) { + g_warning ("memory error when setting address"); + return FALSE; + } + g_key_file_set_string (keyfile, mac, + "longitude", (value)); + } + + if (valid_altitude (altitude)) { + if (!g_snprintf (value, 29, "%f", altitude)) { + g_warning ("memory error when setting address"); + return FALSE; + } + g_key_file_set_string (keyfile, mac, + "altitude", (value)); + } + + if (accuracy) { + geoclue_accuracy_get_details(accuracy, NULL, &hor_acc, &vert_acc); + if (hor_acc) { + if (!g_snprintf (value, 29, "%f", hor_acc)) { + g_warning ("memory error when setting address"); + return FALSE; + } + g_key_file_set_string (keyfile, mac, + "horizontal_accuracy", (value)); + } + if (vert_acc) { + if (!g_snprintf (value, 29, "%f", vert_acc)) { + g_warning ("memory error when setting address"); + return FALSE; + } + g_key_file_set_string (keyfile, mac, + "vertical_accuracy", (value)); + } + } + + /* save keyfile*/ + file_content = g_key_file_to_data (keyfile, NULL, &int_err); + if (int_err) { + g_warning ("Failed to get keyfile data as string: %s", int_err->message); + g_error_free (int_err); + g_key_file_free (keyfile); + g_free (mac); + /* TODO set error */ + return FALSE; + } + + g_file_set_contents (localnet->keyfile_name, file_content, -1, &int_err); + g_free (file_content); + if (int_err) { + g_warning ("Failed to save keyfile: %s", int_err->message); + g_error_free (int_err); + g_key_file_free (keyfile); + g_free (mac); + /* TODO set error */ + return FALSE; + } + + /* re-parse keyfile */ + free_gateway_list (localnet->gateways); + localnet->gateways = NULL; + geoclue_localnet_load_gateways_from_keyfile (localnet, keyfile); + g_key_file_free (keyfile); + + return TRUE; +} + +static gboolean +geoclue_localnet_set_address_for_gateway (GeoclueLocalnet *localnet, + gchar *mac, + GHashTable *details, + GError **error) +{ + Gateway* gw; + double latitude = LATITUDE_INVALID; + double longitude = LONGITUDE_INVALID; + double altitude = ALTITUDE_INVALID; + GeoclueAccuracy *accuracy = NULL; + + gw = geoclue_localnet_find_gateway (localnet, mac); + if (gw) { + latitude = gw->latitude; + longitude = gw->longitude; + altitude = gw->altitude; + accuracy = gw->accuracy; + } + return geoclue_localnet_save_infos_for_gateway(localnet, mac, + latitude, longitude, altitude, + accuracy, + details, + error); +} + +static gboolean +geoclue_localnet_set_address_default (GeoclueLocalnet *localnet, + GHashTable *details, + GError **error) +{ + return geoclue_localnet_set_address_for_gateway(localnet, + GATEWAY_DEFAULT, + details, + error); +} + static gboolean geoclue_localnet_set_address (GeoclueLocalnet *localnet, GHashTable *details, GError **error) { - char *str, *mac = NULL; - GKeyFile *keyfile; - GError *int_err = NULL; - localnet_keyfile_group *keyfile_group; - Gateway *gw; - - if (!details) { - /* TODO set error */ - return FALSE; - } - - if (get_mac_address (&mac) < 0) - return FALSE; - - if (!mac) { - g_warning ("Couldn't get current gateway mac address"); - /* TODO set error */ - return FALSE; - } - /* reload keyfile just in case it's changed */ - keyfile = g_key_file_new (); - if (!g_key_file_load_from_file (keyfile, localnet->keyfile_name, - G_KEY_FILE_NONE, &int_err)) { - g_warning ("Could not load keyfile %s: %s", - localnet->keyfile_name, int_err->message); - g_error_free (int_err); - int_err = NULL; - } - - /* remove old group (if exists) and add new to GKeyFile */ - g_key_file_remove_group (keyfile, mac, NULL); - - keyfile_group = g_new0 (localnet_keyfile_group, 1); - keyfile_group->keyfile = keyfile; - keyfile_group->group_name = mac; - g_hash_table_foreach (details, (GHFunc) add_address_detail_to_keyfile, keyfile_group); - g_free (keyfile_group); - - /* save keyfile*/ - str = g_key_file_to_data (keyfile, NULL, &int_err); - if (int_err) { - g_warning ("Failed to get keyfile data as string: %s", int_err->message); - g_error_free (int_err); - g_key_file_free (keyfile); - g_free (mac); - /* TODO set error */ - return FALSE; - } - - g_file_set_contents (localnet->keyfile_name, str, -1, &int_err); - g_free (str); - if (int_err) { - g_warning ("Failed to save keyfile: %s", int_err->message); - g_error_free (int_err); - g_key_file_free (keyfile); - g_free (mac); - /* TODO set error */ - return FALSE; - } - - /* re-parse keyfile */ - free_gateway_list (localnet->gateways); - localnet->gateways = NULL; - geoclue_localnet_load_gateways_from_keyfile (localnet, keyfile); - g_key_file_free (keyfile); - - gw = geoclue_localnet_find_gateway (localnet, mac); - g_free (mac); - - if (gw) { - gc_iface_address_emit_address_changed (GC_IFACE_ADDRESS (localnet), - time (NULL), gw->address, gw->accuracy); - } else { - /* empty address -- should emit anyway? */ + char *mac = NULL; + int i; + Gateway *gw; + + if (!details) { + /* TODO set error */ + return FALSE; + } + + /* we may be trying to read /proc/net/arp right after network connection. + * It's sometimes not up yet, try a couple of times */ + for (i = 0; i < 5; i++) { + if (get_mac_address (&mac) == 1) + break; + usleep (200); } - return TRUE; + + if (mac == NULL) { + mac = g_strdup (GATEWAY_DEFAULT); + if (!mac) { + g_warning ("memory error when setting address"); + return FALSE; + } + } + + if (!geoclue_localnet_set_address_for_gateway(localnet, + mac, + details, + error)) { + g_free (mac); + return FALSE; + } + + gw = geoclue_localnet_find_gateway (localnet, mac); + g_free (mac); + + if (gw) { + gc_iface_address_emit_address_changed (GC_IFACE_ADDRESS (localnet), + time (NULL), gw->address, gw->accuracy); + } else { + /* empty address -- should emit anyway? */ + } + return TRUE; } static gboolean -geoclue_localnet_set_address_fields (GeoclueLocalnet *localnet, - char *country_code, - char *country, - char *region, - char *locality, - char *area, - char *postalcode, - char *street, - GError **error) +geoclue_localnet_set_position_for_gateway(GeoclueLocalnet *localnet, + gchar *mac, + double latitude, double longitude, double altitude, + double horizontal_accuracy, double vertical_accuracy, + GError **error) +{ + Gateway* gw; + GHashTable *details = NULL; + GeoclueAccuracy* accuracy; + + gw = geoclue_localnet_find_gateway (localnet, mac); + if (gw) { + details = gw->address; + } + + accuracy = geoclue_accuracy_new (GEOCLUE_ACCURACY_LEVEL_NONE, horizontal_accuracy, vertical_accuracy); + + return geoclue_localnet_save_infos_for_gateway(localnet, mac, + latitude, longitude, altitude, + accuracy, + details, + error); +} + +static gboolean +geoclue_localnet_set_position (GeoclueLocalnet *localnet, + double latitude, double longitude, double altitude, + double horizontal_accuracy, double vertical_accuracy, + GError **error) { - GHashTable *address; - gboolean ret; - - address = geoclue_address_details_new (); - if (country_code && (strlen (country_code) > 0)) { - g_hash_table_insert (address, - g_strdup (GEOCLUE_ADDRESS_KEY_COUNTRYCODE), - g_strdup (country_code)); - if (!country) { - geoclue_address_details_set_country_from_code (address); - } - } - if (country && (strlen (country) > 0)) { - g_hash_table_insert (address, - g_strdup (GEOCLUE_ADDRESS_KEY_COUNTRY), - g_strdup (country)); - } - if (region && (strlen (region) > 0)) { - g_hash_table_insert (address, - g_strdup (GEOCLUE_ADDRESS_KEY_REGION), - g_strdup (region)); - } - if (locality && (strlen (locality) > 0)) { - g_hash_table_insert (address, - g_strdup (GEOCLUE_ADDRESS_KEY_LOCALITY), - g_strdup (locality)); - } - if (area && (strlen (area) > 0)) { - g_hash_table_insert (address, - g_strdup (GEOCLUE_ADDRESS_KEY_AREA), - g_strdup (area)); - } - if (postalcode && (strlen (postalcode) > 0)) { - g_hash_table_insert (address, - g_strdup (GEOCLUE_ADDRESS_KEY_POSTALCODE), - g_strdup (postalcode)); + char *mac = NULL; + int i; + Gateway *gw; + + /* we may be trying to read /proc/net/arp right after network connection. + * It's sometimes not up yet, try a couple of times */ + for (i = 0; i < 5; i++) { + if (get_mac_address (&mac) == 1) + break; + usleep (200); } - if (street && (strlen (street) > 0)) { - g_hash_table_insert (address, - g_strdup (GEOCLUE_ADDRESS_KEY_STREET), - g_strdup (street)); + + if (mac == NULL) { + mac = g_strdup (GATEWAY_DEFAULT); + if (!mac) { + g_warning ("memory error when setting address"); + return FALSE; + } + } + + if (!geoclue_localnet_set_position_for_gateway(localnet, + mac, + latitude, + longitude, + altitude, + horizontal_accuracy, + vertical_accuracy, + error)) { + g_free (mac); + return FALSE; + } + + gw = geoclue_localnet_find_gateway (localnet, mac); + g_free (mac); + + if (gw) { + GeocluePositionFields fields = GEOCLUE_POSITION_FIELDS_NONE; + if (gw->latitude) { + fields |= GEOCLUE_POSITION_FIELDS_LATITUDE; + } + if (gw->longitude) { + fields |= GEOCLUE_POSITION_FIELDS_LONGITUDE; + } + if (gw->altitude) { + fields |= GEOCLUE_POSITION_FIELDS_ALTITUDE; + } + gc_iface_position_emit_position_changed (GC_IFACE_POSITION (localnet), + fields, + time (NULL), + gw->latitude, gw->longitude, gw->altitude, + gw->accuracy); + } else { + /* empty position -- should emit anyway? */ + } + return TRUE; + +} + +static gboolean +geoclue_localnet_set_position_default (GeoclueLocalnet *localnet, + double latitude, double longitude, double altitude, + double horizontal_accuracy, double vertical_accuracy, + GError **error) +{ + return geoclue_localnet_set_position_for_gateway(localnet, + GATEWAY_DEFAULT, + latitude, + longitude, + altitude, + horizontal_accuracy, + vertical_accuracy, + error); +} + +static Gateway* +get_gateway_best_choice(GeoclueLocalnet *localnet) +{ + char *mac = NULL; + int i; + Gateway *gw = NULL; + + /* we may be trying to read /proc/net/arp right after network connection. + * It's sometimes not up yet, try a couple of times */ + for (i = 0; i < 5; i++) { + if (get_mac_address (&mac) == 1) + break; + usleep (200); } - - ret = geoclue_localnet_set_address (localnet, - address, - error); - g_hash_table_destroy (address); - return ret; + + if (mac != NULL) { + gw = geoclue_localnet_find_gateway (localnet, mac); + g_free (mac); + } else { + g_warning ("Couldn't get current gateway mac address"); + } + + if (!gw) { + gw = geoclue_localnet_find_gateway (localnet, GATEWAY_DEFAULT); + } + return gw; +} + +static gboolean +get_position (GcIfacePosition *gc, + GeocluePositionFields *fields, + int *timestamp, + double *latitude, + double *longitude, + double *altitude, + GeoclueAccuracy **accuracy, + GError **error) +{ + GeoclueLocalnet *localnet; + Gateway *gw; + + localnet = GEOCLUE_LOCALNET (gc); + + gw = get_gateway_best_choice(localnet); + + if (gw == NULL) { + g_set_error (error, GEOCLUE_ERROR, + GEOCLUE_ERROR_NOT_AVAILABLE, + "No position available"); + return FALSE; + } + + if (timestamp) { + *timestamp = time(NULL); + } + + *fields = GEOCLUE_POSITION_FIELDS_NONE; + if (!valid_latitude(gw->latitude) || !valid_longitude(gw->longitude)) { + g_set_error (error, GEOCLUE_ERROR, + GEOCLUE_ERROR_NOT_AVAILABLE, + "No position available"); + return FALSE; + } + + if (latitude) { + if (valid_latitude(gw->latitude)) { + *fields |= GEOCLUE_POSITION_FIELDS_LATITUDE; + *latitude = gw->latitude; + } + } + if (longitude) { + if (valid_longitude(gw->longitude)) { + *fields |= GEOCLUE_POSITION_FIELDS_LONGITUDE; + *longitude = gw->longitude; + } + } + if (altitude) { + if (valid_altitude(gw->longitude)) { + *fields |= GEOCLUE_POSITION_FIELDS_ALTITUDE; + *altitude = gw->altitude; + } + } + if (accuracy) { + *accuracy = geoclue_accuracy_copy (gw->accuracy); + } + + return TRUE; } static gboolean get_address (GcIfaceAddress *gc, int *timestamp, GHashTable **address, GeoclueAccuracy **accuracy, GError **error) { GeoclueLocalnet *localnet; - int i, ret_val; - char *mac = NULL; Gateway *gw; localnet = GEOCLUE_LOCALNET (gc); - /* we may be trying to read /proc/net/arp right after network connection. - * It's sometimes not up yet, try a couple of times */ - for (i = 0; i < 5; i++) { - ret_val = get_mac_address (&mac); - if (ret_val < 0) - return FALSE; - else if (ret_val == 1) - break; - usleep (200); - } - - if (!mac) { - g_warning ("Couldn't get current gateway mac address"); - if (error) { - g_set_error (error, GEOCLUE_ERROR, - GEOCLUE_ERROR_NOT_AVAILABLE, "Could not get current gateway mac address"); - } - return FALSE; - } - - gw = geoclue_localnet_find_gateway (localnet, mac); - g_free (mac); - + gw = get_gateway_best_choice(localnet); + + if (gw == NULL) { + g_set_error (error, GEOCLUE_ERROR, + GEOCLUE_ERROR_NOT_AVAILABLE, + "No address available"); + return FALSE; + } + + if (g_hash_table_size (gw->address) == 0) { + g_set_error (error, GEOCLUE_ERROR, + GEOCLUE_ERROR_NOT_AVAILABLE, + "No address available"); + return FALSE; + } + if (timestamp) { *timestamp = time(NULL); } if (address) { - if (gw) { - *address = geoclue_address_details_copy (gw->address); - } else { - *address = geoclue_address_details_new (); - } + *address = geoclue_address_details_copy (gw->address); } if (accuracy) { - if (gw) { - *accuracy = geoclue_accuracy_copy (gw->accuracy); - } else { - *accuracy = geoclue_accuracy_new (GEOCLUE_ACCURACY_LEVEL_NONE, 0, 0); - } + *accuracy = geoclue_accuracy_copy (gw->accuracy); } return TRUE; } static void +geoclue_localnet_position_init (GcIfacePositionClass *iface) +{ + iface->get_position = get_position; +} + +static void geoclue_localnet_address_init (GcIfaceAddressClass *iface) { iface->get_address = get_address; } int main (int argc, char **argv) diff --git a/providers/localnet/geoclue-localnet.provider b/providers/localnet/geoclue-localnet.provider index e306ea6..8ba3053 100644 --- a/providers/localnet/geoclue-localnet.provider +++ b/providers/localnet/geoclue-localnet.provider @@ -1,7 +1,7 @@ [Geoclue Provider] Name=Localnet Service=org.freedesktop.Geoclue.Providers.Localnet Path=/org/freedesktop/Geoclue/Providers/Localnet -Interfaces=org.freedesktop.Geoclue.Address +Interfaces=org.freedesktop.Geoclue.Address;org.freedesktop.Geoclue.Position Accuracy=Street Provides=ProvidesCacheableOnConnection diff --git a/providers/localnet/geoclue-localnet.xml b/providers/localnet/geoclue-localnet.xml index 0154f99..5183f01 100644 --- a/providers/localnet/geoclue-localnet.xml +++ b/providers/localnet/geoclue-localnet.xml @@ -1,18 +1,26 @@ - - - - - - - - - + + + + + + + + + + + + + + + + + -- 1.7.0