From 9072fc5e24e8d9be58f704a43390787030043d91 Mon Sep 17 00:00:00 2001 From: arno Date: Sat, 27 Mar 2010 17:47:18 +0100 Subject: [PATCH 2/2] Bug #26777 diff --git a/.gitignore b/.gitignore index 3b25067..bf81b51 100644 --- a/.gitignore +++ b/.gitignore @@ -42,6 +42,7 @@ providers/geonames/geoclue-geonames providers/gsmloc/geoclue-gsmloc providers/hostip/geoclue-hostip providers/localnet/geoclue-localnet +providers/userset/geoclue-userset providers/manual/geoclue-manual providers/nominatim/geoclue-nominatim providers/plazes/geoclue-plazes diff --git a/configure.ac b/configure.ac index 5d2d192..b361022 100644 --- a/configure.ac +++ b/configure.ac @@ -143,7 +143,7 @@ fi AC_SUBST(CONNECTIVITY_LIBS) AC_SUBST(CONNECTIVITY_CFLAGS) -PROVIDER_SUBDIRS="example hostip geonames nominatim manual plazes localnet yahoo gsmloc" +PROVIDER_SUBDIRS="example hostip geonames nominatim manual plazes localnet yahoo gsmloc userset" # ----------------------------------------------------------- # gypsy / gpsd / skyhook @@ -249,6 +249,7 @@ providers/localnet/Makefile providers/yahoo/Makefile providers/gsmloc/Makefile providers/skyhook/Makefile +providers/userset/Makefile src/Makefile ]) diff --git a/example/Makefile.am b/example/Makefile.am index 16d3fec..5039441 100644 --- a/example/Makefile.am +++ b/example/Makefile.am @@ -5,6 +5,8 @@ noinst_PROGRAMS = \ master-pos-example \ address-example \ geocode-example \ + userset-address-example \ + userset-position-example \ revgeocode-example \ common-example @@ -13,6 +15,8 @@ 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 +userset_address_example_SOURCES = userset-address-example.c +userset_position_example_SOURCES = userset-position-example.c revgeocode_example_SOURCES = revgeocode-example.c master_example_SOURCES = master-example.c master_pos_example_SOURCES = master-pos-example.c diff --git a/example/userset-address-example.c b/example/userset-address-example.c new file mode 100644 index 0000000..71eee14 --- /dev/null +++ b/example/userset-address-example.c @@ -0,0 +1,208 @@ +/* + * Geoclue + * userset-address-example.c - Example using userset 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 USERSET_SERVICE "org.freedesktop.Geoclue.Providers.Userset" +#define USERSET_PATH "/org/freedesktop/Geoclue/Providers/Userset" + +/* 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_userset_address (char *countrycode, + 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.Userset", + "/org/freedesktop/Geoclue/Providers/Userset", + "org.freedesktop.Geoclue.Userset"); + if (proxy == NULL) { + g_printerr ("Failed to create proxy object\n"); + dbus_g_connection_unref (connection); + return FALSE; + } + + address = geoclue_address_details_new (); + if (countrycode && (strlen (countrycode) > 0)) { + geoclue_address_details_insert (address, + GEOCLUE_ADDRESS_KEY_COUNTRYCODE, + countrycode); + } + 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_userset_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 (USERSET_SERVICE, USERSET_PATH); + /* create new GeoclueAddress */ + if (provider == NULL) { + g_printerr ("Error while creating GeoclueAddress object.\n"); + return 1; + } + + /* get and display address */ + initial_details = get_userset_address(provider); + + /* change address */ + if (set_userset_address("", "Finland", "", "Helsinki", "", "", "Solnantie 24") == FALSE) { + g_printerr ("Error setting address.\n"); + g_object_unref(provider); + if (initial_details) { + g_hash_table_destroy(initial_details); + } + return 1; + } + + /* show newly added address */ + get_userset_address(provider); + + /* reset initial user address */ + if (initial_details) { + char *countrycode = g_hash_table_lookup(initial_details, "countrycode"); + 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_userset_address(countrycode, + country, + region, + locality, + area, + postalcode, + street); + + g_hash_table_destroy(initial_details); + } + + g_object_unref(provider); + + return 0; +} diff --git a/example/userset-position-example.c b/example/userset-position-example.c new file mode 100644 index 0000000..d0b2913 --- /dev/null +++ b/example/userset-position-example.c @@ -0,0 +1,171 @@ +/* + * Geoclue + * userset-position-example.c - Example using userset 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 USERSET_SERVICE "org.freedesktop.Geoclue.Providers.Userset" +#define USERSET_PATH "/org/freedesktop/Geoclue/Providers/Userset" + +static gboolean +set_userset_position(double latitude, + double longitude, + double altitude, + GeocluePositionFields fields, + 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.Userset", + "/org/freedesktop/Geoclue/Providers/Userset", + "org.freedesktop.Geoclue.Userset"); + + 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_INT, fields, + 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_userset_position(GeocluePosition *provider, + double *latitude, + double *longitude, + double *altitude, + GeocluePositionFields *fields, + double *horizontal_accuracy, + double *vertical_accuracy) +{ + GError *error = NULL; + 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; + } + + GeoclueAccuracyLevel level; + geoclue_accuracy_get_details (accuracy, &level, + horizontal_accuracy, vertical_accuracy); + + /* Print out coordinates if they are valid */ + if (*fields & GEOCLUE_POSITION_FIELDS_LATITUDE && + *fields & GEOCLUE_POSITION_FIELDS_LONGITUDE) { + + 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; + GeocluePositionFields initial_fields; + + double latitude, longitude, altitude = 0.0; + double horiz_acc, vert_acc = 0.0; + GeocluePositionFields fields = GEOCLUE_POSITION_FIELDS_NONE; + GeocluePositionFields fields_all = GEOCLUE_POSITION_FIELDS_LATITUDE | + GEOCLUE_POSITION_FIELDS_LONGITUDE | + GEOCLUE_POSITION_FIELDS_ALTITUDE; + + g_type_init(); + + provider = geoclue_position_new (USERSET_SERVICE, USERSET_PATH); + /* create new GeocluePosition */ + if (provider == NULL) { + g_printerr ("Error while creating GeocluePosition object.\n"); + return 1; + } + + /* get and display position */ + get_userset_position(provider, + &initial_latitude, &initial_longitude, &initial_altitude, + &initial_fields, &initial_horiz_acc, &initial_vert_acc); + + /* change position */ + if (set_userset_position(60.2, 24.9, 117, fields_all, 500, 0) == FALSE) { + g_printerr ("Error setting position.\n"); + g_object_unref(provider); + return 1; + } + + /* show newly added position */ + get_userset_position(provider, + &latitude, &longitude, &altitude, + &fields, &horiz_acc, &vert_acc); + + /* reset initial user position */ + set_userset_position(initial_latitude, initial_longitude, initial_altitude, + initial_fields, initial_horiz_acc, initial_vert_acc); + + g_object_unref(provider); + + return 0; +} diff --git a/providers/userset/Makefile.am b/providers/userset/Makefile.am new file mode 100644 index 0000000..63c0639 --- /dev/null +++ b/providers/userset/Makefile.am @@ -0,0 +1,54 @@ +libexec_PROGRAMS = geoclue-userset + +noinst_DATA = \ + geoclue-userset.xml + +nodist_geoclue_userset_SOURCES = \ + geoclue-userset-glue.h + +BUILT_SOURCES = \ + $(nodist_geoclue_userset_SOURCES) + +geoclue_userset_SOURCES = \ + geoclue-userset.c + +geoclue_userset_CFLAGS = \ + -I$(top_srcdir) \ + -I$(top_builddir) \ + $(GEOCLUE_CFLAGS) + +geoclue_userset_LDADD = \ + $(GEOCLUE_LIBS) \ + $(top_builddir)/geoclue/libgeoclue.la + + +providersdir = $(datadir)/geoclue-providers +providers_DATA = geoclue-userset.provider + +servicedir = $(DBUS_SERVICES_DIR) +service_in_files = org.freedesktop.Geoclue.Providers.Userset.service.in +service_DATA = $(service_in_files:.service.in=.service) + +$(service_DATA): $(service_in_files) Makefile + @sed -e "s|\@libexecdir\@|$(libexecdir)|" $< > $@ + +CLEANFILES = \ + stamp-geoclue-userset-glue.h + +EXTRA_DIST = \ + $(service_in_files) \ + $(providers_DATA) \ + $(noinst_DATA) + +DISTCLEANFILES = \ + $(service_DATA) \ + $(nodist_geoclue_userset_SOURCES) + +%-glue.h: stamp-%-glue.h + @true + +stamp-geoclue-userset-glue.h: geoclue-userset.xml + $(DBUS_BINDING_TOOL) --prefix=geoclue_userset --mode=glib-server $< > xgen-$(@F) \ + && (cmp -s xgen-$(@F) $(@F:stamp-%=%) || cp xgen-$(@F) $(@F:stamp-%=%)) \ + && rm -f xgen-$(@F) \ + && echo timestamp > $(@F) diff --git a/providers/userset/geoclue-userset.c b/providers/userset/geoclue-userset.c new file mode 100644 index 0000000..636f7e0 --- /dev/null +++ b/providers/userset/geoclue-userset.c @@ -0,0 +1,538 @@ +/** + * + * Expects to find a keyfile in user config dir + * (~/.config/geoclue-userset). + * + * The keyfile should contain entries like this: + * + * [userset] + * country=Finland + * street=Solnantie 24 + * locality=Helsinki + * latitude=60.2 + * longitude=24.9 + * altitude=170 + * horizontal_accuracy=500 + * + * Position and address interfaces are supported so far. + * + * Any application can submit an address or a position to userset provider + * through the D-Bus API -- + * org.freedesktop.Geoclue.Geoclue.SetPosition + * org.freedesktop.Geoclue.Userset.SetAddress + * + * Those informations will then be available to geoclue clients. + * + * 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 + +#include +#include +#include +#include + +#define KEYFILE_NAME "geoclue-userset" + +typedef struct { + GcProvider parent; + + GMainLoop *loop; + + char *keyfile_name; + + GHashTable *address; + double latitude; + double longitude; + double altitude; + GeocluePositionFields geofields; + GeoclueAccuracy *accuracy; +} GeoclueUserset; + +typedef struct { + GcProviderClass parent_class; +} GeoclueUsersetClass; + +#define GEOCLUE_TYPE_USERSET (geoclue_userset_get_type ()) +#define GEOCLUE_USERSET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GEOCLUE_TYPE_USERSET, GeoclueUserset)) + +static void geoclue_userset_address_init (GcIfaceAddressClass *iface); +static void geoclue_userset_position_init (GcIfacePositionClass *iface); + +G_DEFINE_TYPE_WITH_CODE (GeoclueUserset, geoclue_userset, GC_TYPE_PROVIDER, + G_IMPLEMENT_INTERFACE (GC_TYPE_IFACE_ADDRESS, + geoclue_userset_address_init) + G_IMPLEMENT_INTERFACE (GC_TYPE_IFACE_POSITION, + geoclue_userset_position_init)) + +static gboolean geoclue_userset_set_address (GeoclueUserset *userset, GHashTable *address, GError **error); +static gboolean geoclue_userset_set_position (GeoclueUserset *userset, + double latitude, double longitude, double altitude, + GeocluePositionFields fields, + double horizontal_accuracy, double vertical_accuracy, + GError **error); + +#include "geoclue-userset-glue.h" + +static gboolean +get_status (GcIfaceGeoclue *gc, + GeoclueStatus *status, + GError **error) +{ + *status = GEOCLUE_STATUS_AVAILABLE; + return TRUE; +} + +static void +shutdown (GcProvider *provider) +{ + GeoclueUserset *userset; + + userset = GEOCLUE_USERSET (provider); + g_main_loop_quit (userset->loop); +} + +static void +finalize (GObject *object) +{ + GeoclueUserset *userset; + + userset = GEOCLUE_USERSET (object); + g_free (userset->keyfile_name); + if (userset->address) { + g_hash_table_destroy (userset->address); + } + if (userset->accuracy) { + geoclue_accuracy_free (userset->accuracy); + } + + G_OBJECT_CLASS (geoclue_userset_parent_class)->finalize (object); +} + +static void +geoclue_userset_class_init (GeoclueUsersetClass *klass) +{ + GcProviderClass *p_class = (GcProviderClass *) klass; + GObjectClass *o_class = (GObjectClass *) klass; + + o_class->finalize = finalize; + + p_class->get_status = get_status; + p_class->shutdown = shutdown; + + dbus_g_object_type_install_info (geoclue_userset_get_type (), + &dbus_glib_geoclue_userset_object_info); +} + +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_userset_load_from_keyfile (GeoclueUserset *userset, + GKeyFile *keyfile) +{ + GError *error = NULL; + char **keys; + char **k; + GeoclueAccuracyLevel level; + double horizontal_accuracy, vertical_accuracy; + + /* read address and positions from "userset" group */ + keys = g_key_file_get_keys (keyfile, "userset", + NULL, &error); + if (error) { + g_warning ("Could not load keys for group [userset] from %s: %s", + userset->keyfile_name, error->message); + g_error_free (error); + error = NULL; + } + + userset->address = geoclue_address_details_new(); + userset->geofields = GEOCLUE_POSITION_FIELDS_NONE; + horizontal_accuracy = 0.0; + vertical_accuracy = 0.0; + + k = keys; + while (*k) { + char *value; + value = g_key_file_get_string (keyfile, "userset", *k, NULL); + if (valid_key_for_address (*k)) { + geoclue_address_details_insert (userset->address, *k, value); + } else if (g_ascii_strcasecmp (*k, "latitude") == 0) { + userset->latitude = atof (value); + userset->geofields |= GEOCLUE_POSITION_FIELDS_LATITUDE; + } else if (g_ascii_strcasecmp (*k, "longitude") == 0) { + userset->longitude = atof (value); + userset->geofields |= GEOCLUE_POSITION_FIELDS_LONGITUDE; + } else if (g_ascii_strcasecmp (*k, "altitude") == 0) { + userset->altitude = atof (value); + userset->geofields |= GEOCLUE_POSITION_FIELDS_ALTITUDE; + } 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 (userset->address); + userset->accuracy = geoclue_accuracy_new (level, + horizontal_accuracy, + vertical_accuracy); +} + +static void +geoclue_userset_init (GeoclueUserset *userset) +{ + const char *dir; + GKeyFile *keyfile; + GError *error = NULL; + + gc_provider_set_details (GC_PROVIDER (userset), + "org.freedesktop.Geoclue.Providers.Userset", + "/org/freedesktop/Geoclue/Providers/Userset", + "Userset", "provides Address and Position as defined by user in a local file (which can be updated through D-Bus)"); + + /* load known addresses from keyfile */ + dir = g_get_user_config_dir (); + g_mkdir_with_parents (dir, 0755); + userset->address = NULL; + userset->accuracy = NULL; + userset->keyfile_name = g_build_filename (dir, KEYFILE_NAME, NULL); + + keyfile = g_key_file_new (); + if (!g_key_file_load_from_file (keyfile, userset->keyfile_name, + G_KEY_FILE_NONE, &error)) { + g_error_free (error); + } else { + geoclue_userset_load_from_keyfile (userset, keyfile); + g_key_file_free (keyfile); + } +} + +static void +add_address_detail_to_keyfile (char *key, char *value, + GKeyFile *keyfile) +{ + g_key_file_set_string (keyfile, "userset", 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_userset_save_infos (GeoclueUserset *userset, + double latitude, + double longitude, + double altitude, + GeocluePositionFields geofields, + GeoclueAccuracy *accuracy, + GHashTable *address_details, + GError **error) +{ + GError *int_err = NULL; + char value[30]; + double hor_acc, vert_acc; + GKeyFile *keyfile; + char *file_content; + + if (address_details != NULL) { + address_details = filter_address_details (address_details); + } + + keyfile = g_key_file_new (); + if (!g_key_file_load_from_file (keyfile, userset->keyfile_name, + G_KEY_FILE_NONE, &int_err)) { + g_error_free (int_err); + int_err = NULL; + } + + /* remove old group (if exists) and add new to GKeyFile */ + g_key_file_remove_group (keyfile, "userset", NULL); + + g_hash_table_foreach (address_details, (GHFunc) add_address_detail_to_keyfile, keyfile); + + if (geofields & GEOCLUE_POSITION_FIELDS_LATITUDE) { + if (!g_snprintf (value, 29, "%f", latitude)) { + g_warning ("memory error when setting address"); + return FALSE; + } + g_key_file_set_string (keyfile, "userset", "latitude", (value)); + } + + if (geofields & GEOCLUE_POSITION_FIELDS_LONGITUDE) { + if (!g_snprintf (value, 29, "%f", longitude)) { + g_warning ("memory error when setting address"); + return FALSE; + } + g_key_file_set_string (keyfile, "userset", "longitude", (value)); + } + + if (geofields & GEOCLUE_POSITION_FIELDS_ALTITUDE) { + if (!g_snprintf (value, 29, "%f", altitude)) { + g_warning ("memory error when setting address"); + return FALSE; + } + g_key_file_set_string (keyfile, "userset", "altitude", (value)); + } + + if (accuracy) { + geoclue_accuracy_get_details(accuracy, NULL, &hor_acc, &vert_acc); + if (hor_acc && + (geofields & GEOCLUE_POSITION_FIELDS_LONGITUDE) && + (geofields & GEOCLUE_POSITION_FIELDS_LATITUDE)) { + if (!g_snprintf (value, 29, "%f", hor_acc)) { + g_warning ("memory error when setting address"); + return FALSE; + } + g_key_file_set_string (keyfile, "userset", "horizontal_accuracy", (value)); + } + if (vert_acc && (geofields & GEOCLUE_POSITION_FIELDS_ALTITUDE)) { + if (!g_snprintf (value, 29, "%f", vert_acc)) { + g_warning ("memory error when setting address"); + return FALSE; + } + g_key_file_set_string (keyfile, "userset", "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); + /* TODO set error */ + return FALSE; + } + + g_file_set_contents (userset->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); + /* TODO set error */ + return FALSE; + } + + /* re-parse keyfile */ + if (userset->address) { + g_hash_table_destroy (userset->address); + } + if (userset->accuracy) { + geoclue_accuracy_free (userset->accuracy); + } + geoclue_userset_load_from_keyfile (userset, keyfile); + g_key_file_free (keyfile); + + return TRUE; +} + +static gboolean +geoclue_userset_set_position (GeoclueUserset *userset, + double latitude, double longitude, double altitude, + GeocluePositionFields geofields, + double horizontal_accuracy, double vertical_accuracy, + GError **error) +{ + GeoclueAccuracy* accuracy; + + accuracy = geoclue_accuracy_new (GEOCLUE_ACCURACY_LEVEL_NONE, horizontal_accuracy, vertical_accuracy); + if (!geoclue_userset_save_infos(userset, + latitude, + longitude, + altitude, + geofields, + accuracy, + userset->address, + error)) { + return FALSE; + } + + gc_iface_position_emit_position_changed (GC_IFACE_POSITION (userset), + userset->geofields, + time (NULL), + userset->latitude, userset->longitude, userset->altitude, + userset->accuracy); + return TRUE; +} + +static gboolean +geoclue_userset_set_address (GeoclueUserset *userset, + GHashTable *details, + GError **error) +{ + if (!details) { + /* TODO set error */ + return FALSE; + } + if (!geoclue_userset_save_infos(userset, + userset->latitude, + userset->longitude, + userset->altitude, + userset->geofields, + userset->accuracy, + details, + error)) { + return FALSE; + } + gc_iface_address_emit_address_changed (GC_IFACE_ADDRESS (userset), + time (NULL), userset->address, userset->accuracy); + return TRUE; +} + +static gboolean +get_position (GcIfacePosition *gc, + GeocluePositionFields *fields, + int *timestamp, + double *latitude, + double *longitude, + double *altitude, + GeoclueAccuracy **accuracy, + GError **error) +{ + GeoclueUserset *userset; + + userset = GEOCLUE_USERSET (gc); + + if (timestamp) { + *timestamp = time(NULL); + } + + *fields = userset->geofields; + if (userset->geofields & GEOCLUE_POSITION_FIELDS_LATITUDE) { + *latitude = userset->latitude; + } + if (userset->geofields & GEOCLUE_POSITION_FIELDS_LONGITUDE) { + *longitude = userset->longitude; + } + if (userset->geofields & GEOCLUE_POSITION_FIELDS_LATITUDE) { + *latitude = userset->latitude; + } + + if (accuracy) { + if (userset->accuracy) { + *accuracy = geoclue_accuracy_copy (userset->accuracy); + } else { + *accuracy = geoclue_accuracy_new (GEOCLUE_ACCURACY_LEVEL_NONE, 0, 0); + } + } + + return TRUE; +} + +static gboolean +get_address (GcIfaceAddress *gc, + int *timestamp, + GHashTable **address, + GeoclueAccuracy **accuracy, + GError **error) +{ + GeoclueUserset *userset; + userset = GEOCLUE_USERSET (gc); + + if (timestamp) { + *timestamp = time(NULL); + } + if (address) { + if (userset->address) { + *address = geoclue_address_details_copy (userset->address); + } else { + g_set_error (error, GEOCLUE_ERROR, + GEOCLUE_ERROR_NOT_AVAILABLE, + "Address not available from provider"); + return FALSE; + } + } + if (accuracy) { + if (userset->accuracy) { + *accuracy = geoclue_accuracy_copy (userset->accuracy); + } else { + g_set_error (error, GEOCLUE_ERROR, + GEOCLUE_ERROR_NOT_AVAILABLE, + "Accuracy not available from provider"); + return FALSE; + } + } + return TRUE; +} + +static void +geoclue_userset_position_init (GcIfacePositionClass *iface) +{ + iface->get_position = get_position; +} + +static void +geoclue_userset_address_init (GcIfaceAddressClass *iface) +{ + iface->get_address = get_address; +} + +int +main (int argc, + char **argv) +{ + GeoclueUserset *userset; + + g_type_init (); + + userset = g_object_new (GEOCLUE_TYPE_USERSET, NULL); + userset->loop = g_main_loop_new (NULL, TRUE); + + g_main_loop_run (userset->loop); + + g_main_loop_unref (userset->loop); + g_object_unref (userset); + + return 0; +} diff --git a/providers/userset/geoclue-userset.provider b/providers/userset/geoclue-userset.provider new file mode 100644 index 0000000..912582c --- /dev/null +++ b/providers/userset/geoclue-userset.provider @@ -0,0 +1,7 @@ +[Geoclue Provider] +Name=Userset +Service=org.freedesktop.Geoclue.Providers.Userset +Path=/org/freedesktop/Geoclue/Providers/Userset +Interfaces=org.freedesktop.Geoclue.Address,org.freedesktop.Geoclue.Position +Accuracy=Detailed +Provides=ProvidesCacheableOnConnection diff --git a/providers/userset/geoclue-userset.xml b/providers/userset/geoclue-userset.xml new file mode 100644 index 0000000..9cad8a8 --- /dev/null +++ b/providers/userset/geoclue-userset.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/providers/userset/org.freedesktop.Geoclue.Providers.Userset.service.in b/providers/userset/org.freedesktop.Geoclue.Providers.Userset.service.in new file mode 100644 index 0000000..f63c7a2 --- /dev/null +++ b/providers/userset/org.freedesktop.Geoclue.Providers.Userset.service.in @@ -0,0 +1,3 @@ +[D-BUS Service] +Name=org.freedesktop.Geoclue.Providers.Userset +Exec=@libexecdir@/geoclue-userset -- 1.7.0.3