Go to:
Gentoo Home
Documentation
Forums
Lists
Bugs
Planet
Store
Wiki
Get Gentoo!
Gentoo's Bugzilla – Attachment 830687 Details for
Bug 880847
app-misc/geoclue + net-libs/libsoup-3.x
Home
|
New
–
[Ex]
|
Browse
|
Search
|
Privacy Policy
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Required patch for libsoup-3.x
geoclue-2.6.0-libsoup-3.2.patch (text/plain), 174.24 KB, created by
Vladislav Mikhailikov
on 2022-11-10 20:48:24 UTC
(
hide
)
Description:
Required patch for libsoup-3.x
Filename:
MIME Type:
Creator:
Vladislav Mikhailikov
Created:
2022-11-10 20:48:24 UTC
Size:
174.24 KB
patch
obsolete
>diff -Nurw geoclue-2.6.0/.gitlab-ci.yml geoclue-2.6.0-soup-3.2/.gitlab-ci.yml >--- geoclue-2.6.0/.gitlab-ci.yml 2022-02-10 21:03:30.000000000 +0300 >+++ geoclue-2.6.0-soup-3.2/.gitlab-ci.yml 1970-01-01 03:00:00.000000000 +0300 >@@ -1,28 +0,0 @@ >-before_script: >- - sed -i '/^#\sdeb-src /s/^#//' '/etc/apt/sources.list' >- - apt-get --allow-unauthenticated update && apt-get build-dep --yes geoclue-2.0 >- - apt-get install --yes git gobject-introspection libmm-glib-dev wget valac >- - apt-get install --yes libnotify-dev xsltproc gtk-doc-tools python3-pip >- - apt-get install --yes ninja-build gettext modemmanager-dev >- - pip3 install meson==0.53.2 >- >-# Ubuntu 14.04 is not supported, see README for details >-# >- >-ubuntu-18.04: >- image: ubuntu:bionic >- artifacts: >- when: always >- name: "bionic-${CI_COMMIT_REF_NAME}" >- paths: >- - "${CI_PROJECT_DIR}/build" >- script: meson build && ninja -C build && ninja -C build test && ninja -C build install >- >-ubuntu-18.04-no-backend: >- image: ubuntu:bionic >- artifacts: >- when: always >- name: "bionic-no-backend-${CI_COMMIT_REF_NAME}" >- paths: >- - "${CI_PROJECT_DIR}/build" >- script: meson -Denable-backend=false build && ninja -C build && ninja -C build test && ninja -C build install >diff -Nurw geoclue-2.6.0/data/geoclue.conf.in geoclue-2.6.0-soup-3.2/data/geoclue.conf.in >--- geoclue-2.6.0/data/geoclue.conf.in 2022-02-10 21:03:30.000000000 +0300 >+++ geoclue-2.6.0-soup-3.2/data/geoclue.conf.in 2022-11-10 23:30:31.817463792 +0300 >@@ -64,6 +64,12 @@ > # If set to true, geoclue will automatically submit network data to Mozilla > # each time it gets a GPS lock. > # >+# Currently, only Modem-GPS or Network NMEA sources are supported as providers >+# of a location to submit (one at a time). >+# If Modem-GPS source is enabled above it will be the exclusive provider >+# (regardless whether the system is actually equipped with such modem), >+# otherwise Network NMEA source will be considered. >+# > submit-data=false > > # URL to submission API of Mozilla Location Service. If not set, defaults to >diff -Nurw geoclue-2.6.0/data/geoclue.service.in geoclue-2.6.0-soup-3.2/data/geoclue.service.in >--- geoclue-2.6.0/data/geoclue.service.in 2022-02-10 21:03:30.000000000 +0300 >+++ geoclue-2.6.0-soup-3.2/data/geoclue.service.in 2022-11-10 23:30:31.817463792 +0300 >@@ -5,6 +5,7 @@ > Type=dbus > BusName=org.freedesktop.GeoClue2 > User=@dbus_srv_user@ >+Environment="GSETTINGS_BACKEND=memory" > ExecStart=@libexecdir@/geoclue > > # Filesystem lockdown >diff -Nurw geoclue-2.6.0/src/gclue-3g-tower.h geoclue-2.6.0-soup-3.2/src/gclue-3g-tower.h >--- geoclue-2.6.0/src/gclue-3g-tower.h 2022-02-10 21:03:30.000000000 +0300 >+++ geoclue-2.6.0-soup-3.2/src/gclue-3g-tower.h 2022-11-10 23:30:31.818463784 +0300 >@@ -26,9 +26,12 @@ > > typedef enum { > GCLUE_TOWER_TEC_UNKNOWN = 0, >- GCLUE_TOWER_TEC_3G = 1, >- GCLUE_TOWER_TEC_4G = 2, >+ GCLUE_TOWER_TEC_2G = 1, >+ GCLUE_TOWER_TEC_3G = 2, >+ GCLUE_TOWER_TEC_4G = 3, >+ GCLUE_TOWER_TEC_NO_FIX = 99, > } GClueTowerTec; >+# define GCLUE_TOWER_TEC_MAX_VALID GCLUE_TOWER_TEC_4G > > typedef struct _GClue3GTower GClue3GTower; > >diff -Nurw geoclue-2.6.0/src/gclue-3g.c geoclue-2.6.0-soup-3.2/src/gclue-3g.c >--- geoclue-2.6.0/src/gclue-3g.c 2022-02-10 21:03:30.000000000 +0300 >+++ geoclue-2.6.0-soup-3.2/src/gclue-3g.c 2022-11-10 23:30:31.819463777 +0300 >@@ -24,9 +24,11 @@ > #include <libsoup/soup.h> > #include <string.h> > #include "gclue-3g.h" >+#include "gclue-3g-tower.h" > #include "gclue-modem-manager.h" > #include "gclue-location.h" > #include "gclue-mozilla.h" >+#include "gclue-wifi.h" > > /** > * SECTION:gclue-3g >@@ -35,14 +37,24 @@ > * Contains functions to get the geolocation based on 3GPP cell towers. > **/ > >+/* Should be slightly less than MAX_LOCATION_AGE in gclue-locator.c, so we don't >+ * get replaced by a less accurate WiFi location while still connected to a tower. >+ * Technically, this can only happen on the NEIGHBORHOOD accuracy level (since at >+ * this level WiFi does scrambling), but it won't hurt on higher ones, too. >+ * In seconds. >+ */ >+#define LOCATION_3GPP_TIMEOUT (25 * 60) >+ >+static unsigned int gclue_3g_running; >+ > struct _GClue3GPrivate { >+ GClueMozilla *mozilla; > GClueModem *modem; > > GCancellable *cancellable; > > gulong threeg_notify_id; >- >- GClue3GTower *tower; >+ guint location_3gpp_timeout_id; > }; > > G_DEFINE_TYPE_WITH_CODE (GClue3G, >@@ -56,6 +68,7 @@ > gclue_3g_stop (GClueLocationSource *source); > static SoupMessage * > gclue_3g_create_query (GClueWebSource *web, >+ const char **query_data_description, > GError **error); > static SoupMessage * > gclue_3g_create_submit_query (GClueWebSource *web, >@@ -64,24 +77,21 @@ > static GClueAccuracyLevel > gclue_3g_get_available_accuracy_level (GClueWebSource *web, > gboolean available); >-static GClueLocation * >-gclue_3g_parse_response (GClueWebSource *web, >- const char *xml, >- GError **error); > > static void > on_3g_enabled (GObject *source_object, > GAsyncResult *result, > gpointer user_data) > { >- GClue3G *source = GCLUE_3G (user_data); >- GError *error = NULL; >+ g_autoptr(GError) error = NULL; > >- if (!gclue_modem_enable_3g_finish (source->priv->modem, >+ if (!gclue_modem_enable_3g_finish (GCLUE_MODEM (source_object), > result, > &error)) { >+ if (error && !g_error_matches (error, G_IO_ERROR, >+ G_IO_ERROR_CANCELLED)) { > g_warning ("Failed to enable 3GPP: %s", error->message); >- g_error_free (error); >+ } > } > } > >@@ -92,23 +102,30 @@ > { > GClue3G *source = GCLUE_3G (user_data); > GClue3GPrivate *priv = source->priv; >+ gboolean available_3g; >+ >+ available_3g = gclue_modem_get_is_3g_available (priv->modem); >+ g_debug ("3G available notify %d", (int)available_3g); > > gclue_web_source_refresh (GCLUE_WEB_SOURCE (source)); > > if (gclue_location_source_get_active (GCLUE_LOCATION_SOURCE (source)) && >- gclue_modem_get_is_3g_available (priv->modem)) >+ available_3g) > gclue_modem_enable_3g (priv->modem, > priv->cancellable, > on_3g_enabled, > source); > } > >-static GClueLocation * >-gclue_3g_parse_response (GClueWebSource *web, >- const char *content, >- GError **error) >+static void cancel_location_3gpp_timeout (GClue3G *g3g) > { >- return gclue_mozilla_parse_response (content, error); >+ GClue3GPrivate *priv = g3g->priv; >+ >+ if (!priv->location_3gpp_timeout_id) >+ return; >+ >+ g_source_remove (priv->location_3gpp_timeout_id); >+ priv->location_3gpp_timeout_id = 0; > } > > static void >@@ -125,7 +142,10 @@ > priv->threeg_notify_id); > priv->threeg_notify_id = 0; > >+ cancel_location_3gpp_timeout (source); >+ > g_clear_object (&priv->modem); >+ g_clear_object (&priv->mozilla); > g_clear_object (&priv->cancellable); > } > >@@ -142,7 +162,6 @@ > source_class->stop = gclue_3g_stop; > web_class->create_query = gclue_3g_create_query; > web_class->create_submit_query = gclue_3g_create_submit_query; >- web_class->parse_response = gclue_3g_parse_response; > web_class->get_available_accuracy_level = > gclue_3g_get_available_accuracy_level; > } >@@ -151,18 +170,26 @@ > gclue_3g_init (GClue3G *source) > { > GClue3GPrivate *priv; >+ GClueWebSource *web_source = GCLUE_WEB_SOURCE (source); > > source->priv = gclue_3g_get_instance_private (source); > priv = source->priv; > > priv->cancellable = g_cancellable_new (); > >+ priv->mozilla = gclue_mozilla_get_singleton (); >+ gclue_web_source_set_locate_url (web_source, >+ gclue_mozilla_get_locate_url (priv->mozilla)); >+ gclue_web_source_set_submit_url (web_source, >+ gclue_mozilla_get_submit_url (priv->mozilla)); >+ > priv->modem = gclue_modem_manager_get_singleton (); > priv->threeg_notify_id = > g_signal_connect (priv->modem, > "notify::is-3g-available", > G_CALLBACK (on_is_3g_available_notify), > source); >+ priv->location_3gpp_timeout_id = 0; > } > > static void >@@ -177,36 +204,53 @@ > /** > * gclue_3g_new: > * >- * Get the #GClue3G singleton. >+ * Get the #GClue3G singleton, for the specified max accuracy level @level. > * > * Returns: (transfer full): a new ref to #GClue3G. Use g_object_unref() > * when done. > **/ > GClue3G * >-gclue_3g_get_singleton (void) >+gclue_3g_get_singleton (GClueAccuracyLevel level) > { >- static GClue3G *source = NULL; >+ static GClue3G *source[] = { NULL, NULL }; >+ int i; >+ >+ g_return_val_if_fail (level >= GCLUE_ACCURACY_LEVEL_CITY, NULL); > >- if (source == NULL) { >- source = g_object_new (GCLUE_TYPE_3G, >+ i = gclue_wifi_should_skip_bsss (level) ? 0 : 1; >+ if (source[i] == NULL) { >+ source[i] = g_object_new (GCLUE_TYPE_3G, >+ "accuracy-level", level, > "compute-movement", FALSE, > NULL); >- g_object_weak_ref (G_OBJECT (source), >+ g_object_weak_ref (G_OBJECT (source[i]), > on_3g_destroyed, >- &source); >+ &source[i]); > } else >- g_object_ref (source); >+ g_object_ref (source[i]); > >- return source; >+ return source[i]; >+} >+ >+static gboolean >+g3g_should_skip_bsss (GClue3G *g3g) >+{ >+ GClueAccuracyLevel level; >+ >+ g_object_get (G_OBJECT (g3g), "accuracy-level", &level, NULL); >+ return gclue_wifi_should_skip_bsss (level); > } > > static SoupMessage * > gclue_3g_create_query (GClueWebSource *web, >+ const char **query_data_description, > GError **error) > { >- GClue3GPrivate *priv = GCLUE_3G (web)->priv; >+ GClue3G *g3g = GCLUE_3G (web); >+ GClue3GPrivate *priv = g3g->priv; >+ gboolean skip_bss; > >- if (priv->tower == NULL) { >+ if (!gclue_mozilla_has_tower (priv->mozilla)) { > g_set_error_literal (error, > G_IO_ERROR, > G_IO_ERROR_NOT_INITIALIZED, >@@ -214,7 +258,13 @@ > return NULL; /* Not initialized yet */ > } > >- return gclue_mozilla_create_query (NULL, priv->tower, error); >+ skip_bss = g3g_should_skip_bsss (g3g); >+ if (skip_bss) { >+ g_debug ("Will skip BSSs in query due to our accuracy level"); >+ } >+ >+ return gclue_mozilla_create_query (priv->mozilla, FALSE, skip_bss, >+ query_data_description, error); > } > > static SoupMessage * >@@ -224,7 +274,7 @@ > { > GClue3GPrivate *priv = GCLUE_3G (web)->priv; > >- if (priv->tower == NULL) { >+ if (!gclue_mozilla_has_tower (priv->mozilla)) { > g_set_error_literal (error, > G_IO_ERROR, > G_IO_ERROR_NOT_INITIALIZED, >@@ -232,9 +282,8 @@ > return NULL; /* Not initialized yet */ > } > >- return gclue_mozilla_create_submit_query (location, >- NULL, >- priv->tower, >+ return gclue_mozilla_create_submit_query (priv->mozilla, >+ location, > error); > } > >@@ -249,6 +298,41 @@ > return GCLUE_ACCURACY_LEVEL_NONE; > } > >+gboolean gclue_3g_should_skip_tower (GClueAccuracyLevel level) >+{ >+ return level < GCLUE_ACCURACY_LEVEL_NEIGHBORHOOD; >+} >+ >+static gboolean >+on_location_3gpp_timeout (gpointer user_data) >+{ >+ GClue3G *g3g = GCLUE_3G (user_data); >+ GClue3GPrivate *priv = g3g->priv; >+ >+ if (!gclue_mozilla_has_tower (priv->mozilla)) { >+ g_debug ("3GPP location timeout, but no tower"); >+ priv->location_3gpp_timeout_id = 0; >+ return G_SOURCE_REMOVE; >+ } >+ >+ g_debug ("3GPP location timeout, re-sending existing location"); >+ gclue_web_source_refresh (GCLUE_WEB_SOURCE (g3g)); >+ >+ return G_SOURCE_CONTINUE; >+} >+ >+static void set_location_3gpp_timeout (GClue3G *g3g) >+{ >+ GClue3GPrivate *priv = g3g->priv; >+ >+ g_debug ("Scheduling new 3GPP location timeout"); >+ >+ cancel_location_3gpp_timeout (g3g); >+ priv->location_3gpp_timeout_id = g_timeout_add_seconds (LOCATION_3GPP_TIMEOUT, >+ on_location_3gpp_timeout, >+ g3g); >+} >+ > static void > on_fix_3g (GClueModem *modem, > const gchar *opc, >@@ -257,15 +341,26 @@ > GClueTowerTec tec, > gpointer user_data) > { >- GClue3GPrivate *priv = GCLUE_3G (user_data)->priv; >+ GClue3G *g3g = GCLUE_3G (user_data); >+ GClue3GPrivate *priv = g3g->priv; >+ >+ g_debug ("3GPP %s fix available", >+ tec == GCLUE_TOWER_TEC_NO_FIX ? "no" : "new"); > >- if (priv->tower == NULL) >- priv->tower = g_slice_new0 (GClue3GTower); >- g_strlcpy (priv->tower->opc, opc, >+ if (tec != GCLUE_TOWER_TEC_NO_FIX) { >+ GClue3GTower tower; >+ >+ g_strlcpy (tower.opc, opc, > GCLUE_3G_TOWER_OPERATOR_CODE_STR_LEN + 1); >- priv->tower->lac = lac; >- priv->tower->cell_id = cell_id; >- priv->tower->tec = tec; >+ tower.lac = lac; >+ tower.cell_id = cell_id; >+ tower.tec = tec; >+ set_location_3gpp_timeout (g3g); >+ gclue_mozilla_set_tower (priv->mozilla, &tower); >+ } else { >+ cancel_location_3gpp_timeout (g3g); >+ gclue_mozilla_set_tower (priv->mozilla, NULL); >+ } > > gclue_web_source_refresh (GCLUE_WEB_SOURCE (user_data)); > } >@@ -286,16 +381,17 @@ > if (base_result != GCLUE_LOCATION_SOURCE_START_RESULT_OK) > return base_result; > >- if (priv->tower != NULL) { >- g_slice_free (GClue3GTower, priv->tower); >- priv->tower = NULL; >+ if (gclue_3g_running == 0) { >+ g_debug ("First 3GPP source starting up"); > } >+ gclue_3g_running++; > > g_signal_connect (priv->modem, > "fix-3g", > G_CALLBACK (on_fix_3g), > source); > >+ /* Emits fix-3g signal even if the location hasn't actually changed to prime us */ > if (gclue_modem_get_is_3g_available (priv->modem)) > gclue_modem_enable_3g (priv->modem, > priv->cancellable, >@@ -307,7 +403,8 @@ > static GClueLocationSourceStopResult > gclue_3g_stop (GClueLocationSource *source) > { >- GClue3GPrivate *priv = GCLUE_3G (source)->priv; >+ GClue3G *g3g = GCLUE_3G (source); >+ GClue3GPrivate *priv = g3g->priv; > GClueLocationSourceClass *base_class; > GError *error = NULL; > GClueLocationSourceStopResult base_result; >@@ -316,13 +413,23 @@ > > base_class = GCLUE_LOCATION_SOURCE_CLASS (gclue_3g_parent_class); > base_result = base_class->stop (source); >- if (base_result == GCLUE_LOCATION_SOURCE_STOP_RESULT_STILL_USED) >+ if (base_result != GCLUE_LOCATION_SOURCE_STOP_RESULT_OK) > return base_result; > > g_signal_handlers_disconnect_by_func (G_OBJECT (priv->modem), > G_CALLBACK (on_fix_3g), > source); > >+ cancel_location_3gpp_timeout (g3g); >+ >+ g_assert (gclue_3g_running > 0); >+ gclue_3g_running--; >+ if (gclue_3g_running > 0) { >+ return base_result; >+ } >+ >+ g_debug ("Last 3GPP source stopping, disabling location gathering and invalidating existing tower"); >+ > if (gclue_modem_get_is_3g_available (priv->modem)) > if (!gclue_modem_disable_3g (priv->modem, > priv->cancellable, >@@ -332,5 +439,7 @@ > g_error_free (error); > } > >+ gclue_mozilla_set_tower (priv->mozilla, NULL); >+ > return base_result; > } >diff -Nurw geoclue-2.6.0/src/gclue-3g.h geoclue-2.6.0-soup-3.2/src/gclue-3g.h >--- geoclue-2.6.0/src/gclue-3g.h 2022-02-10 21:03:30.000000000 +0300 >+++ geoclue-2.6.0-soup-3.2/src/gclue-3g.h 2022-11-10 23:30:31.819463777 +0300 >@@ -22,6 +22,8 @@ > #ifndef GCLUE_3G_H > #define GCLUE_3G_H > >+#include "config.h" >+ > #include <glib.h> > #include <gio/gio.h> > #include "gclue-web-source.h" >@@ -62,7 +64,13 @@ > GClueWebSourceClass parent_class; > }; > >-GClue3G * gclue_3g_get_singleton (void); >+GClue3G * gclue_3g_get_singleton (GClueAccuracyLevel level); >+ >+#if GCLUE_USE_3G_SOURCE >+gboolean gclue_3g_should_skip_tower (GClueAccuracyLevel level); >+#else >+static inline gboolean gclue_3g_should_skip_tower (GClueAccuracyLevel level) { return TRUE; } >+#endif > > G_END_DECLS > >diff -Nurw geoclue-2.6.0/src/gclue-cdma.c geoclue-2.6.0-soup-3.2/src/gclue-cdma.c >--- geoclue-2.6.0/src/gclue-cdma.c 2022-02-10 21:03:30.000000000 +0300 >+++ geoclue-2.6.0-soup-3.2/src/gclue-cdma.c 2022-11-10 23:30:31.819463777 +0300 >@@ -79,14 +79,15 @@ > GAsyncResult *result, > gpointer user_data) > { >- GClueCDMA *source = GCLUE_CDMA (user_data); >- GError *error = NULL; >+ g_autoptr(GError) error = NULL; > >- if (!gclue_modem_enable_cdma_finish (source->priv->modem, >+ if (!gclue_modem_enable_cdma_finish (GCLUE_MODEM (source_object), > result, > &error)) { >+ if (error && !g_error_matches (error, G_IO_ERROR, >+ G_IO_ERROR_CANCELLED)) { > g_warning ("Failed to enable CDMA: %s", error->message); >- g_error_free (error); >+ } > } > } > >@@ -199,7 +200,8 @@ > > location = gclue_location_new (latitude, > longitude, >- 1000); /* Assume 1 km accuracy */ >+ 1000, /* Assume 1 km accuracy */ >+ "CDMA"); > > gclue_location_source_set_location (GCLUE_LOCATION_SOURCE (user_data), > location); >diff -Nurw geoclue-2.6.0/src/gclue-client-info.c geoclue-2.6.0-soup-3.2/src/gclue-client-info.c >--- geoclue-2.6.0/src/gclue-client-info.c 2022-02-10 21:03:30.000000000 +0300 >+++ geoclue-2.6.0-soup-3.2/src/gclue-client-info.c 2022-11-10 23:30:31.820463769 +0300 >@@ -356,15 +356,18 @@ > GTask *task = G_TASK (user_data); > gpointer *info = g_task_get_source_object (task); > GClueClientInfoPrivate *priv = GCLUE_CLIENT_INFO (info)->priv; >+ GDBusProxy *dbus_proxy; > GError *error = NULL; > >- priv->dbus_proxy = g_dbus_proxy_new_for_bus_finish (res, &error); >- if (priv->dbus_proxy == NULL) { >+ dbus_proxy = g_dbus_proxy_new_for_bus_finish (res, &error); >+ if (dbus_proxy == NULL) { > g_task_return_error (task, error); > g_object_unref (task); > return; > } > >+ priv->dbus_proxy = dbus_proxy; >+ > g_dbus_proxy_call (priv->dbus_proxy, > "GetConnectionUnixUser", > g_variant_new ("(s)", priv->bus_name), >diff -Nurw geoclue-2.6.0/src/gclue-compass.c geoclue-2.6.0-soup-3.2/src/gclue-compass.c >--- geoclue-2.6.0/src/gclue-compass.c 2022-02-10 21:03:30.000000000 +0300 >+++ geoclue-2.6.0-soup-3.2/src/gclue-compass.c 2022-11-10 23:30:31.820463769 +0300 >@@ -141,15 +141,16 @@ > { > GClueCompass *compass; > Compass *proxy = COMPASS (source_object); >- GError *error = NULL; >+ g_autoptr(GError) error = NULL; > > if (!compass_call_claim_compass_finish (proxy, res, &error)) { >- if (error->code != G_IO_ERROR_CANCELLED) >+ if (error && !g_error_matches (error, G_IO_ERROR, >+ G_IO_ERROR_CANCELLED)) { > g_debug ("Failed to claim IIO proxy compass: %s", > error->message); >- g_error_free (error); >- g_object_unref (proxy); >+ } > >+ g_object_unref (proxy); > return; > } > g_debug ("IIO compass claimed"); >@@ -173,14 +174,15 @@ > { > GClueCompass *compass; > Compass *proxy; >- GError *error = NULL; >+ g_autoptr(GError) error = NULL; > > proxy = compass_proxy_new_for_bus_finish (res, &error); > if (proxy == NULL) { >- if (error->code != G_IO_ERROR_CANCELLED) >+ if (error && !g_error_matches (error, G_IO_ERROR, >+ G_IO_ERROR_CANCELLED)) { > g_debug ("Failed to connect to IIO compass proxy: %s", > error->message); >- g_error_free (error); >+ } > > return; > } >diff -Nurw geoclue-2.6.0/src/gclue-config.c geoclue-2.6.0-soup-3.2/src/gclue-config.c >--- geoclue-2.6.0/src/gclue-config.c 2022-02-10 21:03:30.000000000 +0300 >+++ geoclue-2.6.0-soup-3.2/src/gclue-config.c 2022-11-10 23:30:31.820463769 +0300 >@@ -236,7 +236,7 @@ > "url", > &error); > if (error != NULL) { >- g_debug ("Failed to get config \"wifi/url\": %s", >+ g_debug ("Using the default locate URL: %s", > error->message); > g_clear_error (&error); > priv->wifi_url = g_strdup (DEFAULT_WIFI_URL); >@@ -259,7 +259,7 @@ > "submission-url", > &error); > if (error != NULL) { >- g_debug ("Failed to get config \"wifi/submission-url\": %s", >+ g_debug ("Using the default submit URL: %s", > error->message); > g_clear_error (&error); > priv->wifi_submit_url = g_strdup (DEFAULT_WIFI_SUBMIT_URL); >@@ -270,7 +270,7 @@ > "submission-nick", > &error); > if (error != NULL) { >- g_debug ("Failed to get config \"wifi/submission-nick\": %s", >+ g_debug ("Using the default submission nick: %s", > error->message); > g_error_free (error); > priv->wifi_submit_nick = g_strdup (DEFAULT_WIFI_SUBMIT_NICK); >diff -Nurw geoclue-2.6.0/src/gclue-location-source.c geoclue-2.6.0-soup-3.2/src/gclue-location-source.c >--- geoclue-2.6.0/src/gclue-location-source.c 2022-02-10 21:03:30.000000000 +0300 >+++ geoclue-2.6.0-soup-3.2/src/gclue-location-source.c 2022-11-10 23:30:31.820463769 +0300 >@@ -419,27 +419,36 @@ > priv->location = gclue_location_duplicate (location); > > if (priv->scramble_location) { >- gdouble latitude, distance, accuracy; >+ gdouble latitude, distance, accuracy, scramble_range; > > latitude = gclue_location_get_latitude (priv->location); > accuracy = gclue_location_get_accuracy (priv->location); > >+ scramble_range = GCLUE_LOCATION_ACCURACY_NEIGHBORHOOD; >+ if (accuracy >= scramble_range) { >+ /* If the location source is already pretty inaccurate >+ * do just a limited range scrambling to be sure. >+ */ >+ scramble_range /= 3; >+ } >+ > /* Randomization is needed to stop apps from calculationg the > * actual location. > */ >- distance = (gdouble) g_random_int_range (1, 3); >+ distance = g_random_double_range (0, scramble_range); >+ distance /= 1000; > > if (g_random_boolean ()) > latitude += distance * LATITUDE_IN_KM; > else > latitude -= distance * LATITUDE_IN_KM; >- accuracy += 3000; >+ accuracy += scramble_range; > > g_object_set (G_OBJECT (priv->location), > "latitude", latitude, > "accuracy", accuracy, > NULL); >- g_debug ("location scrambled"); >+ g_debug ("%s location scrambled", G_OBJECT_TYPE_NAME (source)); > } > > speed = gclue_location_get_speed (location); >diff -Nurw geoclue-2.6.0/src/gclue-location.c geoclue-2.6.0-soup-3.2/src/gclue-location.c >--- geoclue-2.6.0/src/gclue-location.c 2022-02-10 21:03:30.000000000 +0300 >+++ geoclue-2.6.0-soup-3.2/src/gclue-location.c 2022-11-10 23:30:31.820463769 +0300 >@@ -546,6 +546,7 @@ > * @latitude: a valid latitude > * @longitude: a valid longitude > * @accuracy: accuracy of location in meters >+ * @description: a description for the location > * > * Creates a new #GClueLocation object. > * >@@ -554,12 +555,14 @@ > GClueLocation * > gclue_location_new (gdouble latitude, > gdouble longitude, >- gdouble accuracy) >+ gdouble accuracy, >+ const char *description) > { > return g_object_new (GCLUE_TYPE_LOCATION, > "latitude", latitude, > "longitude", longitude, > "accuracy", accuracy, >+ "description", description, > NULL); > } > >@@ -642,6 +645,7 @@ > "longitude", longitude, > "accuracy", accuracy, > "timestamp", timestamp, >+ "description", "GPS GGA", > NULL); > if (altitude != GCLUE_LOCATION_ALTITUDE_UNKNOWN) > g_object_set (location, "altitude", altitude, NULL); >@@ -689,6 +693,7 @@ > "timestamp", timestamp, > "speed", speed, > "heading", heading, >+ "description", "GPS RMC", > NULL); > > if (prev_location != NULL) { >@@ -770,7 +775,7 @@ > * gclue_location_duplicate: > * @location: the #GClueLocation instance to duplicate. > * >- * Creates a new copy of @location object. >+ * Creates a new copy of @location object (with the same timestamp). > * > * Returns: a new #GClueLocation object. Use g_object_unref() when done. > **/ >@@ -788,6 +793,32 @@ > "timestamp", location->priv->timestamp, > "speed", location->priv->speed, > "heading", location->priv->heading, >+ "description", location->priv->description, >+ NULL); >+} >+ >+/** >+ * gclue_location_duplicate_fresh: >+ * @location: the #GClueLocation instance to duplicate. >+ * >+ * Creates a new copy of @location object with a refreshed timestamp. >+ * >+ * Returns: a new #GClueLocation object. Use g_object_unref() when done. >+ **/ >+GClueLocation * >+gclue_location_duplicate_fresh (GClueLocation *location) >+{ >+ g_return_val_if_fail (GCLUE_IS_LOCATION (location), NULL); >+ >+ return g_object_new >+ (GCLUE_TYPE_LOCATION, >+ "latitude", location->priv->latitude, >+ "longitude", location->priv->longitude, >+ "accuracy", location->priv->accuracy, >+ "altitude", location->priv->altitude, >+ "speed", location->priv->speed, >+ "heading", location->priv->heading, >+ "description", location->priv->description, > NULL); > } > >diff -Nurw geoclue-2.6.0/src/gclue-location.h geoclue-2.6.0-soup-3.2/src/gclue-location.h >--- geoclue-2.6.0/src/gclue-location.h 2022-02-10 21:03:30.000000000 +0300 >+++ geoclue-2.6.0-soup-3.2/src/gclue-location.h 2022-11-10 23:30:31.820463769 +0300 >@@ -77,6 +77,13 @@ > #define GCLUE_LOCATION_ACCURACY_UNKNOWN -1 > > /** >+ * GCLUE_LOCATION_ACCURACY_EXACT: >+ * >+ * Constant representing exact-level accuracy. >+ */ >+#define GCLUE_LOCATION_ACCURACY_EXACT 50 /* 50 m */ >+ >+/** > * GCLUE_LOCATION_ACCURACY_STREET: > * > * Constant representing street-level accuracy. >@@ -84,6 +91,13 @@ > #define GCLUE_LOCATION_ACCURACY_STREET 1000 /* 1 km */ > > /** >+ * GCLUE_LOCATION_ACCURACY_NEIGHBORHOOD: >+ * >+ * Constant representing neighborhood-level accuracy. >+ */ >+#define GCLUE_LOCATION_ACCURACY_NEIGHBORHOOD 3000 /* 3 km */ >+ >+/** > * GCLUE_LOCATION_ACCURACY_CITY: > * > * Constant representing city-level accuracy. >@@ -94,6 +108,8 @@ > * GCLUE_LOCATION_ACCURACY_REGION: > * > * Constant representing region-level accuracy. >+ * >+ * Currently unused. > */ > #define GCLUE_LOCATION_ACCURACY_REGION 50000 /* 50 km */ > >@@ -108,6 +124,8 @@ > * GCLUE_LOCATION_ACCURACY_CONTINENT: > * > * Constant representing continent-level accuracy. >+ * >+ * Currently unused. > */ > #define GCLUE_LOCATION_ACCURACY_CONTINENT 3000000 /* 3000 km */ > >@@ -127,7 +145,8 @@ > > GClueLocation *gclue_location_new (gdouble latitude, > gdouble longitude, >- gdouble accuracy); >+ gdouble accuracy, >+ const char *description); > > GClueLocation *gclue_location_new_full > (gdouble latitude, >@@ -146,6 +165,8 @@ > > GClueLocation *gclue_location_duplicate > (GClueLocation *location); >+GClueLocation *gclue_location_duplicate_fresh >+ (GClueLocation *location); > > void gclue_location_set_description > (GClueLocation *loc, >diff -Nurw geoclue-2.6.0/src/gclue-locator.c geoclue-2.6.0-soup-3.2/src/gclue-locator.c >--- geoclue-2.6.0/src/gclue-locator.c 2022-02-10 21:03:30.000000000 +0300 >+++ geoclue-2.6.0-soup-3.2/src/gclue-locator.c 2022-11-10 23:30:31.820463769 +0300 >@@ -81,15 +81,14 @@ > > static void > set_location (GClueLocator *locator, >- GClueLocation *location) >+ GClueLocation *location, >+ const char *src_name) > { > GClueLocation *cur_location; > > cur_location = gclue_location_source_get_location > (GCLUE_LOCATION_SOURCE (locator)); > >- g_debug ("New location available"); >- > if (cur_location != NULL) { > guint64 cur_timestamp, new_timestamp; > double dist, speed; >@@ -97,7 +96,8 @@ > cur_timestamp = gclue_location_get_timestamp (cur_location); > new_timestamp = gclue_location_get_timestamp (location); > if (new_timestamp < cur_timestamp) { >- g_debug ("New location older than current, ignoring."); >+ g_debug ("New %s location older than current, ignoring.", >+ src_name); > return; > } > >@@ -126,11 +126,12 @@ > * a reasonable speed, OR it is more or as accurate as > * the previous one. > */ >- g_debug ("Ignoring less accurate new location"); >+ g_debug ("Ignoring less accurate new %s location", src_name); > return; > } > } > >+ g_debug ("New location available from %s", src_name); > gclue_location_source_set_location (GCLUE_LOCATION_SOURCE (locator), > location); > } >@@ -183,7 +184,7 @@ > GClueLocation *location; > > location = gclue_location_source_get_location (source); >- set_location (locator, location); >+ set_location (locator, location, G_OBJECT_TYPE_NAME (source)); > } > > static gboolean >@@ -206,7 +207,7 @@ > > location = gclue_location_source_get_location (src); > if (gclue_location_source_get_active (src) && location != NULL) >- set_location (locator, location); >+ set_location (locator, location, G_OBJECT_TYPE_NAME (src)); > > gclue_location_source_start (src); > } >@@ -364,7 +365,7 @@ > > #if GCLUE_USE_3G_SOURCE > if (gclue_config_get_enable_3g_source (gconfig)) { >- GClue3G *source = gclue_3g_get_singleton (); >+ GClue3G *source = gclue_3g_get_singleton (locator->priv->accuracy_level); > locator->priv->sources = g_list_append (locator->priv->sources, > source); > } >@@ -387,14 +388,20 @@ > GClueModemGPS *gps = gclue_modem_gps_get_singleton (); > locator->priv->sources = g_list_append (locator->priv->sources, > gps); >+ if (!submit_source) { > submit_source = GCLUE_LOCATION_SOURCE (gps); > } >+ } > #endif > #if GCLUE_USE_NMEA_SOURCE > if (gclue_config_get_enable_nmea_source (gconfig)) { > GClueNMEASource *nmea = gclue_nmea_source_get_singleton (); > locator->priv->sources = g_list_append (locator->priv->sources, > nmea); >+ if (!submit_source) { >+ submit_source = GCLUE_LOCATION_SOURCE (nmea); >+ } >+ > } > #endif > >diff -Nurw geoclue-2.6.0/src/gclue-modem-gps.c geoclue-2.6.0-soup-3.2/src/gclue-modem-gps.c >--- geoclue-2.6.0/src/gclue-modem-gps.c 2022-02-10 21:03:30.000000000 +0300 >+++ geoclue-2.6.0-soup-3.2/src/gclue-modem-gps.c 2022-11-10 23:30:31.821463761 +0300 >@@ -80,14 +80,15 @@ > GAsyncResult *result, > gpointer user_data) > { >- GClueModemGPS *source = GCLUE_MODEM_GPS (user_data); >- GError *error = NULL; >+ g_autoptr(GError) error = NULL; > >- if (!gclue_modem_enable_gps_finish (source->priv->modem, >+ if (!gclue_modem_enable_gps_finish (GCLUE_MODEM (source_object), > result, > &error)) { >+ if (error && !g_error_matches (error, G_IO_ERROR, >+ G_IO_ERROR_CANCELLED)) { > g_warning ("Failed to enable GPS: %s", error->message); >- g_error_free (error); >+ } > } > } > >@@ -168,10 +169,10 @@ > source); > threshold = gclue_location_source_get_time_threshold > (GCLUE_LOCATION_SOURCE (source)); >- g_signal_connect (threshold, >+ g_signal_connect_object (threshold, > "notify::value", > G_CALLBACK (on_time_threshold_changed), >- source); >+ source, 0); > } > > static void >diff -Nurw geoclue-2.6.0/src/gclue-modem-manager.c geoclue-2.6.0-soup-3.2/src/gclue-modem-manager.c >--- geoclue-2.6.0/src/gclue-modem-manager.c 2022-02-10 21:03:30.000000000 +0300 >+++ geoclue-2.6.0-soup-3.2/src/gclue-modem-manager.c 2022-11-10 23:30:31.821463761 +0300 >@@ -45,11 +45,13 @@ > MMModem *modem; > MMModemLocation *modem_location; > MMLocation3gpp *location_3gpp; >+ gboolean location_3gpp_ignore_previous; > MMLocationGpsNmea *location_nmea; > > GCancellable *cancellable; > > MMModemLocationSource caps; /* Caps we set or are going to set */ >+ GClueTowerTec tec; > > guint time_threshold; > }; >@@ -286,7 +288,8 @@ > is_location_3gpp_same (GClueModemManager *manager, > const gchar *new_opc, > gulong new_lac, >- gulong new_cell_id) >+ gulong new_cell_id, >+ GClueTowerTec new_tec) > { > GClueModemManagerPrivate *priv = manager->priv; > const gchar *opc; >@@ -295,7 +298,7 @@ > gchar opc_buf[GCLUE_3G_TOWER_OPERATOR_CODE_STR_LEN + 1]; > #endif > >- if (priv->location_3gpp == NULL) >+ if (priv->location_3gpp == NULL || priv->location_3gpp_ignore_previous) > return FALSE; > > #if MM_CHECK_VERSION(1, 18, 0) >@@ -306,11 +309,10 @@ > #endif > lac = mm_location_3gpp_get_location_area_code (priv->location_3gpp); > >- // Most likely this is an LTE connection and with the mozilla >- // services they use the tracking area code in place of the >- // location area code in this case. >+ // Use the tracking area code in place of the >+ // location area code for LTE. > // https://ichnaea.readthedocs.io/en/latest/api/geolocate.html#cell-tower-fields >- if (lac == 0x0 || lac == 0xFFFE) { >+ if (priv->tec == GCLUE_TOWER_TEC_4G) { > lac = mm_location_3gpp_get_tracking_area_code(priv->location_3gpp); > } > >@@ -318,7 +320,20 @@ > > return (g_strcmp0 (opc, new_opc) == 0 && > lac == new_lac && >- cell_id == new_cell_id); >+ cell_id == new_cell_id && >+ priv->tec == new_tec); >+} >+ >+static void clear_3gpp_location (GClueModemManager *manager) >+{ >+ GClueModemManagerPrivate *priv = manager->priv; >+ >+ if (!priv->location_3gpp && !priv->location_3gpp_ignore_previous) { >+ return; >+ } >+ >+ g_clear_object (&priv->location_3gpp); >+ g_signal_emit (manager, signals[FIX_3G], 0, NULL, 0, 0, GCLUE_TOWER_TEC_NO_FIX); > } > > static void >@@ -326,30 +341,40 @@ > GAsyncResult *res, > gpointer user_data) > { >- GClueModemManager *manager = GCLUE_MODEM_MANAGER (user_data); >- GClueModemManagerPrivate *priv = manager->priv; >+ GClueModemManager *manager; >+ GClueModemManagerPrivate *priv; > MMModemLocation *modem_location = MM_MODEM_LOCATION (source_object); >+ MMModemAccessTechnology modem_access_tec; > g_autoptr(MMLocation3gpp) location_3gpp = NULL; >- GError *error = NULL; > const gchar *opc; > gulong lac, cell_id; >- GClueTowerTec tec = GCLUE_TOWER_TEC_3G; >+ GClueTowerTec tec; > #if !MM_CHECK_VERSION(1, 18, 0) >+ g_autoptr(GError) error = NULL; > gchar opc_buf[GCLUE_3G_TOWER_OPERATOR_CODE_STR_LEN + 1]; >-#endif > > location_3gpp = mm_modem_location_get_3gpp_finish (modem_location, > res, > &error); > if (error != NULL) { >+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { > g_warning ("Failed to get location from 3GPP: %s", > error->message); >- g_error_free (error); >+ } >+ > return; > } >+#else >+ location_3gpp = mm_modem_location_get_signaled_3gpp (modem_location); >+#endif >+ >+ manager = GCLUE_MODEM_MANAGER (user_data); >+ priv = manager->priv; > > if (location_3gpp == NULL) { > g_debug ("No 3GPP"); >+ clear_3gpp_location (manager); >+ priv->location_3gpp_ignore_previous = FALSE; > return; > } > >@@ -364,46 +389,80 @@ > > lac = mm_location_3gpp_get_location_area_code (location_3gpp); > >- // Most likely this is an LTE connection and with the mozilla >- // services they use the tracking area code in place of the >- // location area code in this case. >- // https://ichnaea.readthedocs.io/en/latest/api/geolocate.html#cell-tower-fields >- if (lac == 0x0 || lac == 0xFFFE) { >+ cell_id = mm_location_3gpp_get_cell_id (location_3gpp); >+ >+ modem_access_tec = mm_modem_get_access_technologies(priv->modem); >+ >+ if (modem_access_tec == MM_MODEM_ACCESS_TECHNOLOGY_GSM || >+ modem_access_tec == MM_MODEM_ACCESS_TECHNOLOGY_GPRS || >+ modem_access_tec == MM_MODEM_ACCESS_TECHNOLOGY_EDGE) { >+ tec = GCLUE_TOWER_TEC_2G; >+ } else if (modem_access_tec == MM_MODEM_ACCESS_TECHNOLOGY_UMTS || >+ modem_access_tec == MM_MODEM_ACCESS_TECHNOLOGY_HSDPA || >+ modem_access_tec == MM_MODEM_ACCESS_TECHNOLOGY_HSUPA || >+ modem_access_tec == MM_MODEM_ACCESS_TECHNOLOGY_HSPA || >+ modem_access_tec == MM_MODEM_ACCESS_TECHNOLOGY_HSPA_PLUS) { >+ tec = GCLUE_TOWER_TEC_3G; >+ } else if (modem_access_tec == MM_MODEM_ACCESS_TECHNOLOGY_LTE) { > lac = mm_location_3gpp_get_tracking_area_code(location_3gpp); > tec = GCLUE_TOWER_TEC_4G; >+ } else { >+ tec = GCLUE_TOWER_TEC_UNKNOWN; > } > >- cell_id = mm_location_3gpp_get_cell_id (location_3gpp); >- >- if (is_location_3gpp_same (manager, opc, lac, cell_id)) { >+ if (is_location_3gpp_same (manager, opc, lac, cell_id, tec)) { > g_debug ("New 3GPP location is same as last one"); > return; > } > g_clear_object (&priv->location_3gpp); > priv->location_3gpp = g_steal_pointer (&location_3gpp); >+ priv->location_3gpp_ignore_previous = FALSE; >+ priv->tec = tec; > > g_signal_emit (manager, signals[FIX_3G], 0, opc, lac, cell_id, tec); > } > > static void >+on_location_changed_get_3gpp (GObject *modem_object, >+ GClueModemManager *manager) >+{ >+#if MM_CHECK_VERSION(1, 18, 0) >+ on_get_3gpp_ready(modem_object, NULL, manager); >+#else >+ mm_modem_location_get_3gpp (MM_MODEM_LOCATION (modem_object), >+ manager->priv->cancellable, >+ on_get_3gpp_ready, >+ manager); >+#endif >+} >+ >+static void > on_get_cdma_ready (GObject *source_object, > GAsyncResult *res, > gpointer user_data) > { >- GClueModemManager *manager = GCLUE_MODEM_MANAGER (user_data); >+ GClueModemManager *manager; > MMModemLocation *modem_location = MM_MODEM_LOCATION (source_object); > g_autoptr(MMLocationCdmaBs) location_cdma = NULL; >- GError *error = NULL; >+#if !MM_CHECK_VERSION(1, 18, 0) >+ g_autoptr(GError) error = NULL; > > location_cdma = mm_modem_location_get_cdma_bs_finish (modem_location, > res, > &error); > if (error != NULL) { >- g_warning ("Failed to get location from 3GPP: %s", >+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { >+ g_warning ("Failed to get location from CDMA: %s", > error->message); >- g_error_free (error); >+ } >+ > return; > } >+#else >+ location_cdma = mm_modem_location_get_signaled_cdma_bs (modem_location); >+#endif >+ >+ manager = GCLUE_MODEM_MANAGER (user_data); > > if (location_cdma == NULL) { > g_debug ("No CDMA"); >@@ -417,6 +476,20 @@ > mm_location_cdma_bs_get_longitude (location_cdma)); > } > >+static void >+on_location_changed_get_cdma (GObject *modem_object, >+ GClueModemManager *manager) >+{ >+#if MM_CHECK_VERSION(1, 18, 0) >+ on_get_cdma_ready(modem_object, NULL, manager); >+#else >+ mm_modem_location_get_cdma_bs (MM_MODEM_LOCATION (modem_object), >+ manager->priv->cancellable, >+ on_get_cdma_ready, >+ manager); >+#endif >+} >+ > static gboolean > is_location_gga_same (GClueModemManager *manager, > const char *new_gga) >@@ -436,24 +509,33 @@ > GAsyncResult *res, > gpointer user_data) > { >- GClueModemManager *manager = GCLUE_MODEM_MANAGER (user_data); >- GClueModemManagerPrivate *priv = manager->priv; >+ GClueModemManager *manager; >+ GClueModemManagerPrivate *priv; > MMModemLocation *modem_location = MM_MODEM_LOCATION (source_object); > g_autoptr(MMLocationGpsNmea) location_nmea = NULL; > static const gchar *sentences[3]; > const gchar *gga, *rmc; > gint i = 0; >- GError *error = NULL; >+#if !MM_CHECK_VERSION(1, 18, 0) >+ g_autoptr(GError) error = NULL; > > location_nmea = mm_modem_location_get_gps_nmea_finish (modem_location, > res, > &error); > if (error != NULL) { >+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { > g_warning ("Failed to get location from NMEA information: %s", > error->message); >- g_error_free (error); >+ } >+ > return; > } >+#else >+ location_nmea = mm_modem_location_get_signaled_gps_nmea (modem_location); >+#endif >+ >+ manager = GCLUE_MODEM_MANAGER (user_data); >+ priv = manager->priv; > > if (location_nmea == NULL) { > g_debug ("No NMEA"); >@@ -486,28 +568,32 @@ > } > > static void >+on_location_changed_get_gps_nmea (GObject *modem_object, >+ GClueModemManager *manager) >+{ >+#if MM_CHECK_VERSION(1, 18, 0) >+ on_get_gps_nmea_ready(modem_object, NULL, manager); >+#else >+ mm_modem_location_get_gps_nmea (MM_MODEM_LOCATION (modem_object), >+ manager->priv->cancellable, >+ on_get_gps_nmea_ready, >+ manager); >+#endif >+} >+ >+static void > on_location_changed (GObject *modem_object, > GParamSpec *pspec, > gpointer user_data) > { >- MMModemLocation *modem_location = MM_MODEM_LOCATION (modem_object); > GClueModemManager *manager = GCLUE_MODEM_MANAGER (user_data); > > if ((manager->priv->caps & MM_MODEM_LOCATION_SOURCE_3GPP_LAC_CI) != 0) >- mm_modem_location_get_3gpp (modem_location, >- manager->priv->cancellable, >- on_get_3gpp_ready, >- manager); >+ on_location_changed_get_3gpp (modem_object, manager); > if ((manager->priv->caps & MM_MODEM_LOCATION_SOURCE_CDMA_BS) != 0) >- mm_modem_location_get_cdma_bs (modem_location, >- manager->priv->cancellable, >- on_get_cdma_ready, >- manager); >+ on_location_changed_get_cdma (modem_object, manager); > if ((manager->priv->caps & MM_MODEM_LOCATION_SOURCE_GPS_NMEA) != 0) >- mm_modem_location_get_gps_nmea (modem_location, >- manager->priv->cancellable, >- on_get_gps_nmea_ready, >- manager); >+ on_location_changed_get_gps_nmea (modem_object, manager); > } > > static void >@@ -516,8 +602,9 @@ > gpointer user_data) > { > GTask *task = G_TASK (user_data); >- GClueModemManager *manager; >- GClueModemManagerPrivate *priv; >+ GClueModemManager *manager = GCLUE_MODEM_MANAGER >+ (g_task_get_source_object (task)); >+ GClueModemManagerPrivate *priv = manager->priv; > GError *error = NULL; > > if (!mm_modem_location_setup_finish (MM_MODEM_LOCATION (modem_object), >@@ -527,10 +614,11 @@ > > goto out; > } >- manager = GCLUE_MODEM_MANAGER (g_task_get_source_object (task)); >- priv = manager->priv; >+ > g_debug ("Modem '%s' setup.", mm_object_get_path (priv->mm_object)); > >+ /* Make sure that we actually emit that signal */ >+ priv->location_3gpp_ignore_previous = TRUE; > on_location_changed (modem_object, NULL, manager); > > g_task_return_boolean (task, TRUE); >@@ -551,8 +639,6 @@ > priv->caps |= caps; > task = g_task_new (manager, cancellable, callback, user_data); > >- priv = GCLUE_MODEM_MANAGER (g_task_get_source_object (task))->priv; >- > caps = mm_modem_location_get_enabled (priv->modem_location) | priv->caps; > mm_modem_location_setup (priv->modem_location, > caps, >@@ -656,15 +742,13 @@ > GAsyncResult *res, > gpointer user_data) > { >- gboolean ret; >- GError *error = NULL; >+ g_autoptr(GError) error = NULL; > >- ret = mm_modem_location_set_gps_refresh_rate_finish >+ mm_modem_location_set_gps_refresh_rate_finish > (MM_MODEM_LOCATION (source_object), res, &error); >- if (!ret) { >+ if (error && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { > g_warning ("Failed to set GPS refresh rate: %s", > error->message); >- g_error_free (error); > } > } > >@@ -736,6 +820,8 @@ > return; > g_debug ("Modem '%s' removed.", mm_object_get_path (priv->mm_object)); > >+ clear_3gpp_location (manager); >+ > g_signal_handlers_disconnect_by_func (G_OBJECT (priv->modem_location), > G_CALLBACK (on_location_changed), > user_data); >@@ -753,19 +839,27 @@ > GAsyncResult *res, > gpointer user_data) > { >- GClueModemManagerPrivate *priv = GCLUE_MODEM_MANAGER (user_data)->priv; >+ MMManager *mmmanager; >+ GClueModemManager *manager; >+ GClueModemManagerPrivate *priv; > GList *objects, *node; >- GError *error = NULL; >+ g_autoptr(GError) error = NULL; > >- priv->manager = mm_manager_new_finish (res, &error); >- if (priv->manager == NULL) { >+ mmmanager = mm_manager_new_finish (res, &error); >+ if (mmmanager == NULL) { >+ if (error && !g_error_matches (error, G_IO_ERROR, >+ G_IO_ERROR_CANCELLED)) { > g_warning ("Failed to connect to ModemManager: %s", > error->message); >- g_error_free (error); >+ } > > return; > } > >+ manager = GCLUE_MODEM_MANAGER (user_data); >+ priv = manager->priv; >+ priv->manager = mmmanager; >+ > objects = g_dbus_object_manager_get_objects > (G_DBUS_OBJECT_MANAGER (priv->manager)); > for (node = objects; node != NULL; node = node->next) { >@@ -779,15 +873,15 @@ > } > g_list_free_full (objects, g_object_unref); > >- g_signal_connect (G_OBJECT (priv->manager), >+ g_signal_connect_object (G_OBJECT (priv->manager), > "object-added", > G_CALLBACK (on_mm_object_added), >- user_data); >+ manager, 0); > >- g_signal_connect (G_OBJECT (priv->manager), >+ g_signal_connect_object (G_OBJECT (priv->manager), > "object-removed", > G_CALLBACK (on_mm_object_removed), >- user_data); >+ manager, 0); > } > > static void >@@ -795,19 +889,22 @@ > GAsyncResult *res, > gpointer user_data) > { >- GClueModemManagerPrivate *priv = GCLUE_MODEM_MANAGER (user_data)->priv; >- GDBusConnection *connection; >- GError *error = NULL; >+ GClueModemManagerPrivate *priv; >+ g_autoptr(GDBusConnection) connection = NULL; >+ g_autoptr(GError) error = NULL; > > connection = g_bus_get_finish (res, &error); > if (connection == NULL) { >+ if (error && !g_error_matches (error, G_IO_ERROR, >+ G_IO_ERROR_CANCELLED)) { > g_warning ("Failed to connect to system D-Bus: %s", > error->message); >- g_error_free (error); >+ } > > return; > } > >+ priv = GCLUE_MODEM_MANAGER (user_data)->priv; > mm_manager_new (connection, > 0, > priv->cancellable, >@@ -1043,7 +1140,7 @@ > g_return_val_if_fail (gclue_modem_manager_get_is_3g_available (modem), FALSE); > manager = GCLUE_MODEM_MANAGER (modem); > >- g_clear_object (&manager->priv->location_3gpp); >+ clear_3gpp_location (manager); > g_debug ("Clearing 3GPP location caps from modem"); > return clear_caps (manager, > MM_MODEM_LOCATION_SOURCE_3GPP_LAC_CI, >@@ -1062,7 +1159,7 @@ > g_return_val_if_fail (gclue_modem_manager_get_is_cdma_available (modem), FALSE); > manager = GCLUE_MODEM_MANAGER (modem); > >- g_clear_object (&manager->priv->location_3gpp); >+ clear_3gpp_location (manager); > g_debug ("Clearing CDMA location caps from modem"); > return clear_caps (manager, > MM_MODEM_LOCATION_SOURCE_CDMA_BS, >diff -Nurw geoclue-2.6.0/src/gclue-mozilla.c geoclue-2.6.0-soup-3.2/src/gclue-mozilla.c >--- geoclue-2.6.0/src/gclue-mozilla.c 2022-02-10 21:03:30.000000000 +0300 >+++ geoclue-2.6.0-soup-3.2/src/gclue-mozilla.c 2022-11-10 23:30:31.821463761 +0300 >@@ -25,8 +25,10 @@ > #include <string.h> > #include <config.h> > #include "gclue-mozilla.h" >+#include "gclue-3g-tower.h" > #include "gclue-config.h" > #include "gclue-error.h" >+#include "gclue-wifi.h" > > /** > * SECTION:gclue-mozilla >@@ -40,6 +42,22 @@ > * its easy to switch to Google's API. > **/ > >+struct _GClueMozillaPrivate >+{ >+ GClueWifi *wifi; >+ >+ GClue3GTower tower; >+ gboolean tower_valid; >+ gboolean tower_submitted; >+ >+ gboolean bss_submitted; >+}; >+ >+G_DEFINE_TYPE_WITH_CODE (GClueMozilla, >+ gclue_mozilla, >+ G_TYPE_OBJECT, >+ G_ADD_PRIVATE (GClueMozilla)) >+ > #define BSSID_LEN 6 > #define BSSID_STR_LEN 17 > #define MAX_SSID_LEN 32 >@@ -102,12 +120,10 @@ > return TRUE; > } > >-static const char * >-get_url (void) >+const char * >+gclue_mozilla_get_locate_url (GClueMozilla *mozilla) > { >- GClueConfig *config; >- >- config = gclue_config_get_singleton (); >+ GClueConfig *config = gclue_config_get_singleton (); > > return gclue_config_get_wifi_url (config); > } >@@ -136,25 +152,55 @@ > return FALSE; > } > >+static gboolean >+towertec_to_radiotype (GClueTowerTec tec, >+ const char **radiotype_p) >+{ >+ switch (tec) { >+ case GCLUE_TOWER_TEC_2G: >+ *radiotype_p = "gsm"; >+ break; >+ case GCLUE_TOWER_TEC_3G: >+ *radiotype_p = "wcdma"; >+ break; >+ case GCLUE_TOWER_TEC_4G: >+ *radiotype_p = "lte"; >+ break; >+ default: >+ *radiotype_p = NULL; >+ return FALSE; >+ } >+ >+ return TRUE; >+} >+ > SoupMessage * >-gclue_mozilla_create_query (GList *bss_list, /* As in Access Points */ >- GClue3GTower *tower, >+gclue_mozilla_create_query (GClueMozilla *mozilla, >+ gboolean skip_tower, >+ gboolean skip_bss, >+ const char **query_data_description, > GError **error) > { >+ gboolean has_tower = FALSE, has_bss = FALSE; > SoupMessage *ret = NULL; > JsonBuilder *builder; >+ g_autoptr(GList) bss_list = NULL; > JsonGenerator *generator; > JsonNode *root_node; > char *data; > gsize data_len; >- const char *uri; >+ const char *uri, *radiotype; > guint n_non_ignored_bsss; > GList *iter; > gint64 mcc, mnc; >+ g_autoptr(GBytes) body = NULL; > > builder = json_builder_new (); > json_builder_begin_object (builder); > >+ if (mozilla->priv->wifi && !skip_bss) { >+ bss_list = gclue_wifi_get_bss_list (mozilla->priv->wifi); >+ } > /* We send pure geoip query using empty object if both bss_list and > * tower are NULL. > * >@@ -172,11 +218,11 @@ > n_non_ignored_bsss++; > } > >- if (tower != NULL && >- operator_code_to_mcc_mnc (tower->opc, &mcc, &mnc)) { >- >+ if (mozilla->priv->tower_valid && !skip_tower && >+ towertec_to_radiotype (mozilla->priv->tower.tec, &radiotype) && >+ operator_code_to_mcc_mnc (mozilla->priv->tower.opc, &mcc, &mnc)) { > json_builder_set_member_name (builder, "radioType"); >- json_builder_add_string_value (builder, "gsm"); >+ json_builder_add_string_value (builder, radiotype); > > json_builder_set_member_name (builder, "cellTowers"); > json_builder_begin_array (builder); >@@ -184,21 +230,21 @@ > json_builder_begin_object (builder); > > json_builder_set_member_name (builder, "cellId"); >- json_builder_add_int_value (builder, tower->cell_id); >+ json_builder_add_int_value (builder, mozilla->priv->tower.cell_id); > json_builder_set_member_name (builder, "mobileCountryCode"); > json_builder_add_int_value (builder, mcc); > json_builder_set_member_name (builder, "mobileNetworkCode"); > json_builder_add_int_value (builder, mnc); > json_builder_set_member_name (builder, "locationAreaCode"); >- json_builder_add_int_value (builder, tower->lac); >- if (tower->tec == GCLUE_TOWER_TEC_4G) { >+ json_builder_add_int_value (builder, mozilla->priv->tower.lac); > json_builder_set_member_name (builder, "radioType"); >- json_builder_add_string_value (builder, "lte"); >- } >+ json_builder_add_string_value (builder, radiotype); > > json_builder_end_object (builder); > > json_builder_end_array (builder); >+ >+ has_tower = TRUE; > } > > if (n_non_ignored_bsss >= 2) { >@@ -222,6 +268,8 @@ > strength_dbm = wpa_bss_get_signal (bss); > json_builder_add_int_value (builder, strength_dbm); > json_builder_end_object (builder); >+ >+ has_bss = TRUE; > } > json_builder_end_array (builder); > } >@@ -236,15 +284,24 @@ > g_object_unref (builder); > g_object_unref (generator); > >- uri = get_url (); >+ uri = gclue_mozilla_get_locate_url (mozilla); > ret = soup_message_new ("POST", uri); >- soup_message_set_request (ret, >- "application/json", >- SOUP_MEMORY_TAKE, >- data, >- data_len); >+ body = g_bytes_new_take (data, data_len); >+ soup_message_set_request_body_from_bytes (ret, "application/json", body); > g_debug ("Sending following request to '%s':\n%s", uri, data); > >+ if (query_data_description) { >+ if (has_tower && has_bss) { >+ *query_data_description = "3GPP + WiFi"; >+ } else if (has_tower) { >+ *query_data_description = "3GPP"; >+ } else if (has_bss) { >+ *query_data_description = "WiFi"; >+ } else { >+ *query_data_description = "GeoIP"; >+ } >+ } >+ > return ret; > } > >@@ -252,28 +309,32 @@ > parse_server_error (JsonObject *object, GError **error) > { > JsonObject *error_obj; >- int code; > const char *message; > > if (!json_object_has_member (object, "error")) > return FALSE; > > error_obj = json_object_get_object_member (object, "error"); >- code = json_object_get_int_member (error_obj, "code"); >+ if (json_object_has_member (error_obj, "message")) { > message = json_object_get_string_member (error_obj, "message"); >+ } else { >+ message = "Unknown error"; >+ } > >- g_set_error_literal (error, G_IO_ERROR, code, message); >+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, message); > > return TRUE; > } > > GClueLocation * > gclue_mozilla_parse_response (const char *json, >+ const char *location_description, > GError **error) > { >- JsonParser *parser; >+ g_autoptr(JsonParser) parser = NULL; > JsonNode *node; > JsonObject *object, *loc_object; >+ g_autofree char *desc_new = NULL; > GClueLocation *location; > gdouble latitude, longitude, accuracy; > >@@ -288,54 +349,78 @@ > if (parse_server_error (object, error)) > return NULL; > >+ if (json_object_has_member (object, "fallback")) { >+ const char *fallback; >+ >+ fallback = json_object_get_string_member (object, "fallback"); >+ if (fallback && strlen (fallback)) { >+ desc_new = g_strdup_printf ("%s fallback (from %s data)", >+ fallback, location_description); >+ location_description = desc_new; >+ } >+ } >+ > loc_object = json_object_get_object_member (object, "location"); > latitude = json_object_get_double_member (loc_object, "lat"); > longitude = json_object_get_double_member (loc_object, "lng"); > > accuracy = json_object_get_double_member (object, "accuracy"); > >- location = gclue_location_new (latitude, longitude, accuracy); >- >- g_object_unref (parser); >+ location = gclue_location_new (latitude, longitude, accuracy, >+ location_description); > > return location; > } > >-static const char * >-get_submit_config (const char **nick) >+const char * >+gclue_mozilla_get_submit_url (GClueMozilla *mozilla) > { >- GClueConfig *config; >- >- config = gclue_config_get_singleton (); >- if (!gclue_config_get_wifi_submit_data (config)) >- return NULL; >- >- *nick = gclue_config_get_wifi_submit_nick (config); >+ GClueConfig *config = gclue_config_get_singleton (); > >+ if (gclue_config_get_wifi_submit_data (config)) > return gclue_config_get_wifi_submit_url (config); >+ else >+ return NULL; > } > > SoupMessage * >-gclue_mozilla_create_submit_query (GClueLocation *location, >- GList *bss_list, /* As in Access Points */ >- GClue3GTower *tower, >+gclue_mozilla_create_submit_query (GClueMozilla *mozilla, >+ GClueLocation *location, > GError **error) > { > SoupMessage *ret = NULL; >+ SoupMessageHeaders *request_headers; > JsonBuilder *builder; > JsonGenerator *generator; > JsonNode *root_node; > char *data, *timestr; >+ g_autoptr(GList) bss_list = NULL; > const char *url, *nick; > gsize data_len; > GList *iter; > gdouble lat, lon, accuracy, altitude; > GDateTime *datetime; > gint64 mcc, mnc; >+ GClueConfig *config; >+ g_autoptr(GBytes) body = NULL; >+ >+ if (mozilla->priv->bss_submitted && >+ (!mozilla->priv->tower_valid || >+ mozilla->priv->tower_submitted)) >+ { >+ g_debug ("Already created submit req for this data (bss submitted %d; tower: valid %d submitted %d)", >+ (int)mozilla->priv->bss_submitted, >+ (int)mozilla->priv->tower_valid, >+ (int)mozilla->priv->tower_submitted); >+ goto out; >+ } > >- url = get_submit_config (&nick); >+ >+ url = gclue_mozilla_get_submit_url (mozilla); > if (url == NULL) > goto out; >+ config = gclue_config_get_singleton (); >+ nick = gclue_config_get_wifi_submit_nick (config); > > builder = json_builder_new (); > json_builder_begin_object (builder); >@@ -379,6 +464,9 @@ > json_builder_set_member_name (builder, "radioType"); > json_builder_add_string_value (builder, "gsm"); > >+ if (mozilla->priv->wifi) { >+ bss_list = gclue_wifi_get_bss_list (mozilla->priv->wifi); >+ } > if (bss_list != NULL) { > json_builder_set_member_name (builder, "wifi"); > json_builder_begin_array (builder); >@@ -410,24 +498,32 @@ > json_builder_end_array (builder); /* wifi */ > } > >- if (tower != NULL && >- operator_code_to_mcc_mnc (tower->opc, &mcc, &mnc)) { >- >+ if (mozilla->priv->tower_valid && >+ operator_code_to_mcc_mnc (mozilla->priv->tower.opc, &mcc, &mnc)) { > json_builder_set_member_name (builder, "cell"); > json_builder_begin_array (builder); > > json_builder_begin_object (builder); > >+ if (mozilla->priv->tower.tec == GCLUE_TOWER_TEC_4G) { >+ json_builder_set_member_name (builder, "radio"); >+ json_builder_add_string_value (builder, "lte"); >+ } else if (mozilla->priv->tower.tec == GCLUE_TOWER_TEC_3G) { >+ json_builder_set_member_name (builder, "radio"); >+ json_builder_add_string_value (builder, "umts"); >+ } else if (mozilla->priv->tower.tec == GCLUE_TOWER_TEC_2G) { > json_builder_set_member_name (builder, "radio"); > json_builder_add_string_value (builder, "gsm"); >+ } >+ > json_builder_set_member_name (builder, "cid"); >- json_builder_add_int_value (builder, tower->cell_id); >+ json_builder_add_int_value (builder, mozilla->priv->tower.cell_id); > json_builder_set_member_name (builder, "mcc"); > json_builder_add_int_value (builder, mcc); > json_builder_set_member_name (builder, "mnc"); > json_builder_add_int_value (builder, mnc); > json_builder_set_member_name (builder, "lac"); >- json_builder_add_int_value (builder, tower->lac); >+ json_builder_add_int_value (builder, mozilla->priv->tower.lac); > > json_builder_end_object (builder); > >@@ -448,17 +544,18 @@ > g_object_unref (generator); > > ret = soup_message_new ("POST", url); >+ request_headers = soup_message_get_request_headers (ret); > if (nick != NULL && nick[0] != '\0') >- soup_message_headers_append (ret->request_headers, >+ soup_message_headers_append (request_headers, > "X-Nickname", > nick); >- soup_message_set_request (ret, >- "application/json", >- SOUP_MEMORY_TAKE, >- data, >- data_len); >+ body = g_bytes_new_take (data, data_len); >+ soup_message_set_request_body_from_bytes (ret, "application/json", body); > g_debug ("Sending following request to '%s':\n%s", url, data); > >+ mozilla->priv->bss_submitted = TRUE; >+ mozilla->priv->tower_submitted = TRUE; >+ > out: > return ret; > } >@@ -485,3 +582,134 @@ > > return FALSE; > } >+ >+static void >+gclue_mozilla_finalize (GObject *object) >+{ >+ GClueMozilla *mozilla = GCLUE_MOZILLA (object); >+ >+ g_clear_weak_pointer (&mozilla->priv->wifi); >+ >+ G_OBJECT_CLASS (gclue_mozilla_parent_class)->finalize (object); >+} >+ >+static void >+gclue_mozilla_init (GClueMozilla *mozilla) >+{ >+ mozilla->priv = gclue_mozilla_get_instance_private (mozilla); >+ mozilla->priv->wifi = NULL; >+ mozilla->priv->tower_valid = FALSE; >+ mozilla->priv->bss_submitted = FALSE; >+} >+ >+static void >+gclue_mozilla_class_init (GClueMozillaClass *klass) >+{ >+ GObjectClass *object_class = G_OBJECT_CLASS (klass); >+ >+ object_class->finalize = gclue_mozilla_finalize; >+} >+ >+GClueMozilla * >+gclue_mozilla_get_singleton (void) >+{ >+ static GClueMozilla *mozilla = NULL; >+ >+ if (!mozilla) { >+ mozilla = g_object_new (GCLUE_TYPE_MOZILLA, NULL); >+ g_object_add_weak_pointer (G_OBJECT (mozilla), (gpointer) &mozilla); >+ } else >+ g_object_ref (mozilla); >+ >+ return mozilla; >+} >+ >+void >+gclue_mozilla_set_wifi (GClueMozilla *mozilla, >+ GClueWifi *wifi) >+{ >+ g_return_if_fail (GCLUE_IS_MOZILLA (mozilla)); >+ >+ if (mozilla->priv->wifi == wifi) >+ return; >+ >+ g_clear_weak_pointer (&mozilla->priv->wifi); >+ >+ if (!wifi) { >+ return; >+ } >+ >+ mozilla->priv->wifi = wifi; >+ g_object_add_weak_pointer (G_OBJECT (mozilla->priv->wifi), >+ (gpointer) &mozilla->priv->wifi); >+} >+ >+gboolean >+gclue_mozilla_test_set_wifi (GClueMozilla *mozilla, >+ GClueWifi *old, GClueWifi *new) >+{ >+ if (mozilla->priv->wifi != old) >+ return FALSE; >+ >+ gclue_mozilla_set_wifi (mozilla, new); >+ return TRUE; >+} >+ >+void >+gclue_mozilla_set_bss_dirty (GClueMozilla *mozilla) >+{ >+ g_return_if_fail (GCLUE_IS_MOZILLA (mozilla)); >+ >+ mozilla->priv->bss_submitted = FALSE; >+} >+ >+static gboolean gclue_mozilla_tower_identical (const GClue3GTower *t1, >+ const GClue3GTower *t2) >+{ >+ return g_strcmp0 (t1->opc, t2->opc) == 0 && t1->lac == t2->lac && >+ t1->cell_id == t2->cell_id && t1->tec == t2->tec; >+} >+ >+void >+gclue_mozilla_set_tower (GClueMozilla *mozilla, >+ const GClue3GTower *tower) >+{ >+ g_return_if_fail (GCLUE_IS_MOZILLA (mozilla)); >+ >+ if (!tower || >+ !(tower->tec > GCLUE_TOWER_TEC_UNKNOWN >+ && tower->tec <= GCLUE_TOWER_TEC_MAX_VALID)) { >+ mozilla->priv->tower_valid = FALSE; >+ return; >+ } >+ >+ if (mozilla->priv->tower_valid && >+ mozilla->priv->tower_submitted) { >+ mozilla->priv->tower_submitted = >+ gclue_mozilla_tower_identical (&mozilla->priv->tower, >+ tower); >+ } else >+ mozilla->priv->tower_submitted = FALSE; >+ >+ mozilla->priv->tower = *tower; >+ mozilla->priv->tower_valid = TRUE; >+} >+ >+gboolean >+gclue_mozilla_has_tower (GClueMozilla *mozilla) >+{ >+ g_return_val_if_fail (GCLUE_IS_MOZILLA (mozilla), FALSE); >+ >+ return mozilla->priv->tower_valid; >+} >+ >+GClue3GTower * >+gclue_mozilla_get_tower (GClueMozilla *mozilla) >+{ >+ g_return_val_if_fail (GCLUE_IS_MOZILLA (mozilla), NULL); >+ >+ if (!mozilla->priv->tower_valid) >+ return NULL; >+ >+ return &mozilla->priv->tower; >+} >diff -Nurw geoclue-2.6.0/src/gclue-mozilla.h geoclue-2.6.0-soup-3.2/src/gclue-mozilla.h >--- geoclue-2.6.0/src/gclue-mozilla.h 2022-02-10 21:03:30.000000000 +0300 >+++ geoclue-2.6.0-soup-3.2/src/gclue-mozilla.h 2022-11-10 23:30:31.821463761 +0300 >@@ -30,21 +30,72 @@ > > G_BEGIN_DECLS > >+#define GCLUE_TYPE_MOZILLA (gclue_mozilla_get_type()) >+#define GCLUE_MOZILLA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCLUE_TYPE_MOZILLA, GClueMozilla)) >+#define GCLUE_MOZILLA_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCLUE_TYPE_MOZILLA, GClueMozilla const)) >+#define GCLUE_MOZILLA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GCLUE_TYPE_MOZILLA, GClueMozillaClass)) >+#define GCLUE_IS_MOZILLA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCLUE_TYPE_MOZILLA)) >+#define GCLUE_IS_MOZILLA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GCLUE_TYPE_MOZILLA)) >+#define GCLUE_MOZILLA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GCLUE_TYPE_MOZILLA, GClueMozillaClass)) >+ >+typedef struct _GClueMozilla GClueMozilla; >+typedef struct _GClueMozillaClass GClueMozillaClass; >+typedef struct _GClueMozillaPrivate GClueMozillaPrivate; >+ >+struct _GClueMozilla >+{ >+ GObject parent; >+ >+ /*< private >*/ >+ GClueMozillaPrivate *priv; >+}; >+ >+struct _GClueMozillaClass >+{ >+ GObjectClass parent_class; >+}; >+ >+struct GClueWifi; >+typedef struct _GClueWifi GClueWifi; >+ >+GType gclue_mozilla_get_type (void) G_GNUC_CONST; >+ >+GClueMozilla *gclue_mozilla_get_singleton (void); >+ >+void gclue_mozilla_set_wifi (GClueMozilla *mozilla, >+ GClueWifi *wifi); >+gboolean >+gclue_mozilla_test_set_wifi (GClueMozilla *mozilla, >+ GClueWifi *old, GClueWifi *new); >+void gclue_mozilla_set_bss_dirty (GClueMozilla *mozilla); >+ >+void gclue_mozilla_set_tower (GClueMozilla *mozilla, >+ const GClue3GTower *tower); >+gboolean >+gclue_mozilla_has_tower (GClueMozilla *mozilla); >+GClue3GTower * >+gclue_mozilla_get_tower (GClueMozilla *mozilla); >+ > SoupMessage * >-gclue_mozilla_create_query (GList *bss_list, /* As in Access Points */ >- GClue3GTower *tower, >+gclue_mozilla_create_query (GClueMozilla *mozilla, >+ gboolean skip_tower, >+ gboolean skip_bss, >+ const char **query_data_description, > GError **error); > GClueLocation * > gclue_mozilla_parse_response (const char *json, >+ const char *location_description, > GError **error); > SoupMessage * >-gclue_mozilla_create_submit_query (GClueLocation *location, >- GList *bss_list, /* As in Access Points */ >- GClue3GTower *tower, >+gclue_mozilla_create_submit_query (GClueMozilla *mozilla, >+ GClueLocation *location, > GError **error); > gboolean > gclue_mozilla_should_ignore_bss (WPABSS *bss); > >+const char *gclue_mozilla_get_locate_url (GClueMozilla *mozilla); >+const char *gclue_mozilla_get_submit_url (GClueMozilla *mozilla); >+ > G_END_DECLS > > #endif /* GCLUE_MOZILLA_H */ >diff -Nurw geoclue-2.6.0/src/gclue-nmea-source.c geoclue-2.6.0-soup-3.2/src/gclue-nmea-source.c >--- geoclue-2.6.0/src/gclue-nmea-source.c 2022-02-10 21:03:30.000000000 +0300 >+++ geoclue-2.6.0-soup-3.2/src/gclue-nmea-source.c 2022-11-10 23:30:31.821463761 +0300 >@@ -22,11 +22,13 @@ > */ > > #include <stdlib.h> >+#include <string.h> > #include <glib.h> > #include "gclue-config.h" >+#include "gclue-location.h" > #include "gclue-nmea-utils.h" > #include "gclue-nmea-source.h" >-#include "gclue-location.h" >+#include "gclue-utils.h" > #include "config.h" > #include "gclue-enum-types.h" > >@@ -37,21 +39,35 @@ > #include <avahi-glib/glib-watch.h> > #include <gio/gunixsocketaddress.h> > >+/* Once we run out of NMEA services to try how long to wait >+ * until retrying all of them. >+ * In seconds. >+ */ >+#define SERVICE_UNBREAK_TIME 5 >+ > typedef struct AvahiServiceInfo AvahiServiceInfo; > > struct _GClueNMEASourcePrivate { > GSocketConnection *connection; >+ GDataInputStream *input_stream; > > GSocketClient *client; > > GCancellable *cancellable; > >+ AvahiGLibPoll *glib_poll; >+ > AvahiClient *avahi_client; > > AvahiServiceInfo *active_service; > >- /* List of all services but only the most accurate one is used. */ >- GList *all_services; >+ /* List of services to try but only the most accurate one is used. */ >+ GList *try_services; >+ >+ /* List of known-broken services. */ >+ GList *broken_services; >+ >+ guint accuracy_refresh_source, unbreak_timer; > }; > > G_DEFINE_TYPE_WITH_CODE (GClueNMEASource, >@@ -65,16 +81,15 @@ > gclue_nmea_source_stop (GClueLocationSource *source); > > static void >-connect_to_service (GClueNMEASource *source); >-static void >-disconnect_from_service (GClueNMEASource *source); >+try_connect_to_service (GClueNMEASource *source); > > struct AvahiServiceInfo { > char *identifier; > char *host_name; >+ gboolean is_socket; > guint16 port; > GClueAccuracyLevel accuracy; >- guint64 timestamp; >+ gint64 timestamp_add; > }; > > static void >@@ -99,7 +114,7 @@ > service->host_name = g_strdup (host_name); > service->port = port; > service->accuracy = accuracy; >- service->timestamp = g_get_real_time () / G_USEC_PER_SEC; >+ service->timestamp_add = g_get_monotonic_time (); > > return service; > } >@@ -122,16 +137,41 @@ > { > AvahiServiceInfo *first, *second; > gint diff; >+ gint64 tdiff; > > first = (AvahiServiceInfo *) a; > second = (AvahiServiceInfo *) b; > > diff = second->accuracy - first->accuracy; >+ if (diff) >+ return diff; > >- if (diff == 0) >- return first->timestamp - second->timestamp; >+ g_assert (first->timestamp_add >= 0); >+ g_assert (second->timestamp_add >= 0); >+ tdiff = first->timestamp_add - second->timestamp_add; >+ if (tdiff < 0) >+ return -1; >+ else if (tdiff > 0) >+ return 1; >+ else >+ return 0; >+} > >- return diff; >+static void >+disconnect_from_service (GClueNMEASource *source) >+{ >+ GClueNMEASourcePrivate *priv = source->priv; >+ >+ if (!priv->active_service) >+ return; >+ >+ g_cancellable_cancel (priv->cancellable); >+ >+ g_clear_object (&priv->input_stream); >+ g_clear_object (&priv->connection); >+ g_clear_object (&priv->client); >+ g_clear_object (&priv->cancellable); >+ priv->active_service = NULL; > } > > static gboolean >@@ -145,9 +185,9 @@ > * 2. a more accurate service than one currently in use, is now > * available. > */ >- return (priv->active_service != NULL && >- (priv->all_services == NULL || >- priv->active_service != priv->all_services->data)); >+ return priv->active_service == NULL || >+ priv->try_services == NULL || >+ priv->active_service != priv->try_services->data; > } > > static void >@@ -157,25 +197,35 @@ > return; > > disconnect_from_service (source); >- connect_to_service (source); >+ try_connect_to_service (source); > } > >-static void >-refresh_accuracy_level (GClueNMEASource *source) >+static GClueAccuracyLevel get_head_accuracy (GList *list) >+{ >+ AvahiServiceInfo *service; >+ >+ if (!list) >+ return GCLUE_ACCURACY_LEVEL_NONE; >+ >+ service = (AvahiServiceInfo *) list->data; >+ return service->accuracy; >+} >+ >+static gboolean >+on_refresh_accuracy_level (gpointer user_data) > { >- GClueAccuracyLevel new, existing; >+ GClueNMEASource *source = GCLUE_NMEA_SOURCE (user_data); >+ GClueNMEASourcePrivate *priv = source->priv; >+ GClueAccuracyLevel new_try, new_broken, new, existing; >+ >+ priv->accuracy_refresh_source = 0; > > existing = gclue_location_source_get_available_accuracy_level > (GCLUE_LOCATION_SOURCE (source)); > >- if (source->priv->all_services != NULL) { >- AvahiServiceInfo *service; >- >- service = (AvahiServiceInfo *) source->priv->all_services->data; >- new = service->accuracy; >- } else { >- new = GCLUE_ACCURACY_LEVEL_NONE; >- } >+ new_try = get_head_accuracy (priv->try_services); >+ new_broken = get_head_accuracy (priv->broken_services); >+ new = MAX (new_try, new_broken); > > if (new != existing) { > g_debug ("Available accuracy level from %s: %u", >@@ -184,6 +234,109 @@ > "available-accuracy-level", new, > NULL); > } >+ >+ return G_SOURCE_REMOVE; >+} >+ >+static void >+refresh_accuracy_level (GClueNMEASource *source) >+{ >+ GClueNMEASourcePrivate *priv = source->priv; >+ >+ if (priv->accuracy_refresh_source) { >+ return; >+ } >+ >+ g_debug ("Scheduling NMEA accuracy level refresh"); >+ priv->accuracy_refresh_source = g_idle_add (on_refresh_accuracy_level, >+ source); >+} >+ >+static gboolean >+on_service_unbreak_time (gpointer source) >+{ >+ GClueNMEASourcePrivate *priv = GCLUE_NMEA_SOURCE (source)->priv; >+ >+ priv->unbreak_timer = 0; >+ >+ if (!priv->try_services && priv->broken_services) { >+ g_debug ("Unbreaking existing NMEA services"); >+ >+ priv->try_services = priv->broken_services; >+ priv->broken_services = NULL; >+ >+ reconnect_service (source); >+ } >+ >+ return G_SOURCE_REMOVE; >+} >+ >+static void >+check_unbreak_timer (GClueNMEASource *source) >+{ >+ GClueNMEASourcePrivate *priv = source->priv; >+ >+ if (priv->try_services || !priv->broken_services) { >+ if (priv->unbreak_timer) { >+ g_debug ("Removing unnecessary NMEA unbreaking timer"); >+ >+ g_source_remove (priv->unbreak_timer); >+ priv->unbreak_timer = 0; >+ } >+ >+ return; >+ } >+ >+ if (priv->unbreak_timer) { >+ return; >+ } >+ >+ g_debug ("Scheduling NMEA unbreaking timer"); >+ priv->unbreak_timer = g_timeout_add_seconds (SERVICE_UNBREAK_TIME, >+ on_service_unbreak_time, >+ source); >+} >+ >+static void >+service_lists_changed (GClueNMEASource *source) >+{ >+ check_unbreak_timer (source); >+ reconnect_service (source); >+ refresh_accuracy_level (source); >+} >+ >+static gboolean >+check_service_exists (GClueNMEASource *source, >+ const char *name) >+{ >+ GClueNMEASourcePrivate *priv = source->priv; >+ AvahiServiceInfo *service; >+ GList *item; >+ gboolean ret = FALSE; >+ >+ /* only `name` is required here */ >+ service = avahi_service_new (name, >+ NULL, >+ 0, >+ GCLUE_ACCURACY_LEVEL_NONE); >+ >+ item = g_list_find_custom (priv->try_services, >+ service, >+ compare_avahi_service_by_identifier); >+ if (item) { >+ ret = TRUE; >+ } else { >+ item = g_list_find_custom (priv->broken_services, >+ service, >+ compare_avahi_service_by_identifier); >+ if (item) { >+ ret = TRUE; >+ } >+ } >+ >+ g_clear_pointer (&service, avahi_service_free); >+ >+ return ret; > } > > static void >@@ -191,6 +344,7 @@ > const char *name, > const char *host_name, > uint16_t port, >+ gboolean is_socket, > AvahiStringList *txt) > { > GClueAccuracyLevel accuracy = GCLUE_ACCURACY_LEVEL_NONE; >@@ -201,7 +355,12 @@ > GEnumClass *enum_class; > GEnumValue *enum_value; > >- if (port == 0) { >+ if (check_service_exists (source, name)) { >+ g_debug ("NMEA service %s already exists", name); >+ return; >+ } >+ >+ if (!txt) { > accuracy = GCLUE_ACCURACY_LEVEL_EXACT; > > goto CREATE_SERVICE; >@@ -242,43 +401,72 @@ > > CREATE_SERVICE: > service = avahi_service_new (name, host_name, port, accuracy); >+ service->is_socket = is_socket; > >- source->priv->all_services = g_list_insert_sorted >- (source->priv->all_services, >+ source->priv->try_services = g_list_insert_sorted >+ (source->priv->try_services, > service, > compare_avahi_service_by_accuracy_n_time); > >- refresh_accuracy_level (source); >- reconnect_service (source); >+ n_services = g_list_length (source->priv->try_services); >+ g_debug ("No. of _nmea-0183._tcp services %u", n_services); > >- n_services = g_list_length (source->priv->all_services); >+ service_lists_changed (source); >+} > >- g_debug ("No. of _nmea-0183._tcp services %u", n_services); >+static void >+add_new_service_avahi (GClueNMEASource *source, >+ const char *name, >+ const char *host_name, >+ uint16_t port, >+ AvahiStringList *txt) >+{ >+ add_new_service (source, name, host_name, port, FALSE, txt); > } > > static void >-remove_service (GClueNMEASource *source, >- AvahiServiceInfo *service) >+add_new_service_socket (GClueNMEASource *source, >+ const char *name, >+ const char *socket_path) > { >- guint n_services = 0; >+ add_new_service (source, name, socket_path, 0, TRUE, NULL); >+} > >- avahi_service_free (service); >- source->priv->all_services = g_list_remove >- (source->priv->all_services, service); >+static void >+service_broken (GClueNMEASource *source) >+{ >+ GClueNMEASourcePrivate *priv = source->priv; >+ AvahiServiceInfo *service = priv->active_service; > >- n_services = g_list_length (source->priv->all_services); >+ g_assert (service); > >- g_debug ("No. of _nmea-0183._tcp services %u", >- n_services); >+ disconnect_from_service (source); > >- refresh_accuracy_level (source); >- reconnect_service (source); >+ priv->try_services = g_list_remove (priv->try_services, >+ service); >+ priv->broken_services = g_list_insert_sorted >+ (priv->broken_services, >+ service, >+ compare_avahi_service_by_accuracy_n_time); >+ >+ service_lists_changed (source); >+} >+ >+static void >+remove_service_from_list (GList **list, >+ GList *item) >+{ >+ AvahiServiceInfo *service = item->data; >+ >+ *list = g_list_delete_link (*list, item); >+ avahi_service_free (service); > } > > static void > remove_service_by_name (GClueNMEASource *source, > const char *name) > { >+ GClueNMEASourcePrivate *priv = source->priv; > AvahiServiceInfo *service; > GList *item; > >@@ -288,15 +476,31 @@ > 0, > GCLUE_ACCURACY_LEVEL_NONE); > >- item = g_list_find_custom (source->priv->all_services, >+ item = g_list_find_custom (priv->try_services, > service, > compare_avahi_service_by_identifier); >- avahi_service_free (service); >+ if (item) { >+ if (item->data == priv->active_service) { >+ g_debug ("Active NMEA service removed, disconnecting."); >+ disconnect_from_service (source); >+ } > >- if (item == NULL) >- return; >+ remove_service_from_list (&priv->try_services, >+ item); >+ } else { >+ item = g_list_find_custom (priv->broken_services, >+ service, >+ compare_avahi_service_by_identifier); >+ if (item) { >+ g_assert (item->data != priv->active_service); >+ remove_service_from_list (&priv->broken_services, >+ item); >+ } >+ } >+ >+ g_clear_pointer (&service, avahi_service_free); > >- remove_service (source, item->data); >+ service_lists_changed (source); > } > > static void >@@ -337,11 +541,14 @@ > } > > case AVAHI_RESOLVER_FOUND: >- g_debug ("Service %s:%u resolved", >+ g_debug ("Service '%s' of type '%s' in domain '%s' resolved to %s:%u", >+ name, >+ type, >+ domain, > host_name, >- port); >+ (unsigned int)port); > >- add_new_service (GCLUE_NMEA_SOURCE (user_data), >+ add_new_service_avahi (GCLUE_NMEA_SOURCE (user_data), > name, > host_name, > port, >@@ -358,12 +565,8 @@ > AvahiClientState state, > void *user_data) > { >- GClueNMEASourcePrivate *priv = GCLUE_NMEA_SOURCE (user_data)->priv; >- > g_return_if_fail (avahi_client != NULL); > >- priv->avahi_client = avahi_client; >- > if (state == AVAHI_CLIENT_FAILURE) { > const char *errorstr = avahi_strerror > (avahi_client_errno (avahi_client)); >@@ -448,52 +651,91 @@ > } > } > >+#define NMEA_LINE_END "\r\n" >+#define NMEA_LINE_END_CTR (sizeof (NMEA_LINE_END) - 1) >+ >+static void nmea_skip_delim (GBufferedInputStream *stream, >+ GCancellable *cancellable) >+{ >+ const char *buf; >+ gsize buf_size; >+ size_t delim_skip; >+ g_autoptr(GError) error = NULL; >+ >+ buf = (const char *) g_buffered_input_stream_peek_buffer (stream, >+ &buf_size); >+ >+ delim_skip = strnspn (buf, NMEA_LINE_END, buf_size); >+ for (size_t ctr = 0; ctr < delim_skip; ctr++) { >+ if (g_buffered_input_stream_read_byte (stream, cancellable, &error) < 0) { >+ if (error && !g_error_matches (error, G_IO_ERROR, >+ G_IO_ERROR_CANCELLED)) { >+ g_warning ("Failed to skip %zu / %zu NMEA delimiter: %s", >+ ctr, delim_skip, error->message); >+ } >+ break; >+ } >+ } >+} >+ >+static gboolean nmea_check_delim (GBufferedInputStream *stream) >+{ >+ const char *buf; >+ gsize buf_size; >+ >+ buf = (const char *) g_buffered_input_stream_peek_buffer (stream, >+ &buf_size); >+ >+ return strnpbrk (buf, NMEA_LINE_END, buf_size) != NULL; >+} >+ > #define NMEA_STR_LEN 128 > static void > on_read_nmea_sentence (GObject *object, > GAsyncResult *result, > gpointer user_data) > { >- GClueNMEASource *source = GCLUE_NMEA_SOURCE (user_data); >+ GClueNMEASource *source = NULL; > GDataInputStream *data_input_stream = G_DATA_INPUT_STREAM (object); >- GError *error = NULL; >+ g_autoptr(GError) error = NULL; > GClueLocation *prev_location; > g_autoptr(GClueLocation) location = NULL; > gsize data_size = 0 ; >- char *message; >+ g_autofree char *message = NULL; > gint i; >- static const gchar *sentences[3] = { 0 }; >- static gchar gga[NMEA_STR_LEN] = { 0 }; >- static gchar rmc[NMEA_STR_LEN] = { 0 }; >+ const gchar *sentences[3]; >+ gchar gga[NMEA_STR_LEN]; >+ gchar rmc[NMEA_STR_LEN]; > >- >- message = g_data_input_stream_read_line_finish (data_input_stream, >+ message = g_data_input_stream_read_upto_finish (data_input_stream, > result, > &data_size, > &error); > >+ gga[0] = '\0'; >+ rmc[0] = '\0'; >+ > do { >+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) >+ return; >+ >+ if (!source) >+ source = GCLUE_NMEA_SOURCE (user_data); >+ > if (message == NULL) { > if (error != NULL) { >- if (error->code == G_IO_ERROR_CLOSED) >- g_debug ("Socket closed."); >- else if (error->code != G_IO_ERROR_CANCELLED) >+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CLOSED)) { >+ g_debug ("NMEA socket closed."); >+ } else { > g_warning ("Error when receiving message: %s", > error->message); >- g_error_free (error); >+ } > } else { >- g_debug ("Nothing to read"); >+ g_debug ("NMEA nothing to read"); > } >- g_object_unref (data_input_stream); > >- if (source->priv->active_service != NULL) >- /* In case service did not advertise it exiting >- * or we failed to receive it's notification. >- */ >- remove_service (source, source->priv->active_service); >+ service_broken (source); > >- gga[0] = '\0'; >- rmc[0] = '\0'; > return; > } > g_debug ("Network source sent: \"%s\"", message); >@@ -506,12 +748,15 @@ > g_debug ("Ignoring NMEA sentence, as it's neither GGA or RMC: %s", message); > } > >- message = (char *) g_buffered_input_stream_peek_buffer >- (G_BUFFERED_INPUT_STREAM (data_input_stream), >- &data_size); >- if (g_strstr_len (message, data_size, "\n")) { >- message = g_data_input_stream_read_line >- (data_input_stream, &data_size, NULL, &error); >+ nmea_skip_delim (G_BUFFERED_INPUT_STREAM (data_input_stream), >+ source->priv->cancellable); >+ >+ if (nmea_check_delim (G_BUFFERED_INPUT_STREAM (data_input_stream))) { >+ g_clear_pointer (&message, g_free); >+ message = g_data_input_stream_read_upto >+ (data_input_stream, >+ NMEA_LINE_END, NMEA_LINE_END_CTR, >+ &data_size, NULL, &error); > } else { > break; > } >@@ -524,6 +769,7 @@ > sentences[i++] = rmc; > sentences[i] = NULL; > >+ if (i > 0) { > prev_location = gclue_location_source_get_location > (GCLUE_LOCATION_SOURCE (source)); > location = gclue_location_create_from_nmeas (sentences, >@@ -532,17 +778,15 @@ > > if (error != NULL) { > g_warning ("Error: %s", error->message); >- g_clear_error (&error); > } else { > gclue_location_source_set_location > (GCLUE_LOCATION_SOURCE (source), location); > } >+ } > >- gga[0] = '\0'; >- rmc[0] = '\0'; >- sentences[0] = NULL; >- >- g_data_input_stream_read_line_async (data_input_stream, >+ g_data_input_stream_read_upto_async (data_input_stream, >+ NMEA_LINE_END, >+ NMEA_LINE_END_CTR, > G_PRIORITY_DEFAULT, > source->priv->cancellable, > on_read_nmea_sentence, >@@ -554,30 +798,41 @@ > GAsyncResult *result, > gpointer user_data) > { >- GClueNMEASource *source = GCLUE_NMEA_SOURCE (user_data); > GSocketClient *client = G_SOCKET_CLIENT (object); >- GError *error = NULL; >- GDataInputStream *data_input_stream; >- GInputStream *input_stream; >+ GClueNMEASource *source; >+ g_autoptr(GSocketConnection) connection = NULL; >+ g_autoptr(GError) error = NULL; > >- source->priv->connection = g_socket_client_connect_to_host_finish >+ connection = g_socket_client_connect_to_host_finish > (client, > result, > &error); > >+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { >+ return; >+ } >+ >+ source = GCLUE_NMEA_SOURCE (user_data); >+ > if (error != NULL) { >- if (error->code != G_IO_ERROR_CANCELLED) > g_warning ("Failed to connect to NMEA service: %s", error->message); >- g_clear_error (&error); >- >+ service_broken (source); > return; > } > >- input_stream = g_io_stream_get_input_stream >- (G_IO_STREAM (source->priv->connection)); >- data_input_stream = g_data_input_stream_new (input_stream); >+ g_assert (connection); >+ g_debug ("NMEA service connected."); > >- g_data_input_stream_read_line_async (data_input_stream, >+ g_assert (!source->priv->connection); >+ source->priv->connection = g_steal_pointer (&connection); >+ >+ g_assert (!source->priv->input_stream); >+ source->priv->input_stream = g_data_input_stream_new >+ (g_io_stream_get_input_stream (G_IO_STREAM (source->priv->connection))); >+ >+ g_data_input_stream_read_upto_async (source->priv->input_stream, >+ NMEA_LINE_END, >+ NMEA_LINE_END_CTR, > G_PRIORITY_DEFAULT, > source->priv->cancellable, > on_read_nmea_sentence, >@@ -585,24 +840,39 @@ > } > > static void >-connect_to_service (GClueNMEASource *source) >+try_connect_to_service (GClueNMEASource *source) > { > GClueNMEASourcePrivate *priv = source->priv; >- GSocketAddress *addr; >- GSocketConnectable *connectable; > >- if (priv->all_services == NULL) >+ if (!gclue_location_source_get_active (GCLUE_LOCATION_SOURCE (source))) { >+ g_warn_if_fail (!priv->active_service); >+ >+ return; >+ } >+ >+ if (priv->active_service) >+ return; >+ >+ if (priv->try_services == NULL) > return; > >+ g_assert (!priv->cancellable); >+ priv->cancellable = g_cancellable_new (); >+ >+ g_assert (!priv->client); > priv->client = g_socket_client_new (); >- g_cancellable_reset (priv->cancellable); > > /* The service with the highest accuracy will be stored in the beginning > * of the list. > */ >- priv->active_service = (AvahiServiceInfo *) priv->all_services->data; >+ priv->active_service = (AvahiServiceInfo *) priv->try_services->data; > >- if ( priv->active_service->port != 0 ) >+ g_debug ("Trying to connect to NMEA %sservice %s:%u.", >+ priv->active_service->is_socket ? "socket " : "", >+ priv->active_service->host_name, >+ (unsigned int) priv->active_service->port); >+ >+ if (!priv->active_service->is_socket) { > g_socket_client_connect_to_host_async > (priv->client, > priv->active_service->host_name, >@@ -610,52 +880,86 @@ > priv->cancellable, > on_connection_to_location_server, > source); >- else { >+ } else { >+ g_autoptr(GSocketAddress) addr = NULL; >+ > addr = g_unix_socket_address_new(priv->active_service->host_name); >- connectable = G_SOCKET_CONNECTABLE (addr); > g_socket_client_connect_async (priv->client, >- connectable, >+ G_SOCKET_CONNECTABLE (addr), > priv->cancellable, > on_connection_to_location_server, > source); > } > } > >-static void >-disconnect_from_service (GClueNMEASource *source) >+static gboolean >+remove_avahi_services_from_list (GClueNMEASource *source, GList **list) > { > GClueNMEASourcePrivate *priv = source->priv; >+ gboolean removed_active = FALSE; >+ GList *l = *list; > >- g_cancellable_cancel (priv->cancellable); >+ while (l != NULL) { >+ GList *next = l->next; >+ AvahiServiceInfo *service = l->data; >+ >+ if (!service->is_socket) { >+ if (service == priv->active_service) { >+ g_debug ("Active NMEA service was Avahi-provided, disconnecting."); >+ disconnect_from_service (source); >+ removed_active = TRUE; >+ } > >- if (priv->connection != NULL) { >- GError *error = NULL; >+ remove_service_from_list (list, l); >+ } > >- g_io_stream_close (G_IO_STREAM (priv->connection), >- NULL, >- &error); >- if (error != NULL) >- g_warning ("Error in closing socket connection: %s", error->message); >+ l = next; > } > >- g_clear_object (&priv->connection); >- g_clear_object (&priv->client); >- priv->active_service = NULL; >+ return removed_active; >+} >+ >+static void >+disconnect_avahi_client (GClueNMEASource *source) >+{ >+ GClueNMEASourcePrivate *priv = source->priv; >+ >+ remove_avahi_services_from_list (source, &priv->try_services); >+ if (remove_avahi_services_from_list (source, &priv->broken_services)) { >+ g_warn_if_reached (); >+ } >+ >+ g_clear_pointer (&priv->avahi_client, avahi_client_free); >+ >+ service_lists_changed (source); > } > > static void > gclue_nmea_source_finalize (GObject *gnmea) > { >- GClueNMEASourcePrivate *priv = GCLUE_NMEA_SOURCE (gnmea)->priv; >+ GClueNMEASource *source = GCLUE_NMEA_SOURCE (gnmea); >+ GClueNMEASourcePrivate *priv = source->priv; > > G_OBJECT_CLASS (gclue_nmea_source_parent_class)->finalize (gnmea); > >- g_clear_object (&priv->connection); >- g_clear_object (&priv->client); >- g_clear_object (&priv->cancellable); >- if (priv->avahi_client) >- avahi_client_free (priv->avahi_client); >- g_list_free_full (priv->all_services, >+ disconnect_avahi_client (source); >+ disconnect_from_service (source); >+ >+ if (priv->accuracy_refresh_source) { >+ g_source_remove (priv->accuracy_refresh_source); >+ priv->accuracy_refresh_source = 0; >+ } >+ >+ if (priv->unbreak_timer) { >+ g_source_remove (priv->unbreak_timer); >+ priv->unbreak_timer = 0; >+ } >+ >+ g_clear_pointer (&priv->glib_poll, avahi_glib_poll_free); >+ >+ g_list_free_full (g_steal_pointer (&priv->try_services), >+ avahi_service_free); >+ g_list_free_full (g_steal_pointer (&priv->broken_services), > avahi_service_free); > } > >@@ -672,41 +976,33 @@ > } > > static void >-gclue_nmea_source_init (GClueNMEASource *source) >+try_connect_avahi_client (GClueNMEASource *source) > { >- GClueNMEASourcePrivate *priv; > AvahiServiceBrowser *service_browser; >+ GClueNMEASourcePrivate *priv = source->priv; > const AvahiPoll *poll_api; >- AvahiGLibPoll *glib_poll; >- const char *nmea_socket; >- GClueConfig *config; > int error; > >- source->priv = gclue_nmea_source_get_instance_private (source); >- priv = source->priv; >+ if (priv->avahi_client) { >+ AvahiClientState avahi_state; > >- glib_poll = avahi_glib_poll_new (NULL, G_PRIORITY_DEFAULT); >- poll_api = avahi_glib_poll_get (glib_poll); >- >- priv->cancellable = g_cancellable_new (); >- >- config = gclue_config_get_singleton (); >+ avahi_state = avahi_client_get_state (priv->avahi_client); >+ if (avahi_state != AVAHI_CLIENT_FAILURE) { >+ return; >+ } > >- nmea_socket = gclue_config_get_nmea_socket (config); >- if (nmea_socket != NULL) { >- add_new_service (source, >- "nmea-socket", >- nmea_socket, >- 0, >- NULL); >+ g_debug ("Avahi client in failure state, trying to reinit."); >+ disconnect_avahi_client (source); > } > >- avahi_client_new (poll_api, >+ g_assert (priv->glib_poll); >+ poll_api = avahi_glib_poll_get (priv->glib_poll); >+ >+ priv->avahi_client = avahi_client_new (poll_api, > 0, > client_callback, > source, > &error); >- > if (priv->avahi_client == NULL) { > g_warning ("Failed to connect to avahi service: %s", > avahi_strerror (error)); >@@ -722,15 +1018,43 @@ > 0, > browse_callback, > source); >- >- > if (service_browser == NULL) { > const char *errorstr; > > error = avahi_client_errno (priv->avahi_client); > errorstr = avahi_strerror (error); > g_warning ("Failed to browse avahi services: %s", errorstr); >+ goto fail_client; >+ } >+ >+ return; >+ >+fail_client: >+ disconnect_avahi_client (source); >+} >+ >+static void >+gclue_nmea_source_init (GClueNMEASource *source) >+{ >+ GClueNMEASourcePrivate *priv; >+ const char *nmea_socket; >+ GClueConfig *config; >+ >+ source->priv = gclue_nmea_source_get_instance_private (source); >+ priv = source->priv; >+ >+ priv->glib_poll = avahi_glib_poll_new (NULL, G_PRIORITY_DEFAULT); >+ >+ config = gclue_config_get_singleton (); >+ >+ nmea_socket = gclue_config_get_nmea_socket (config); >+ if (nmea_socket != NULL) { >+ add_new_service_socket (source, >+ "nmea-socket", >+ nmea_socket); > } >+ >+ try_connect_avahi_client (source); > } > > /** >@@ -750,8 +1074,10 @@ > source = g_object_new (GCLUE_TYPE_NMEA_SOURCE, NULL); > g_object_add_weak_pointer (G_OBJECT (source), > (gpointer) &source); >- } else >+ } else { > g_object_ref (source); >+ try_connect_avahi_client (source); >+ } > > return source; > } >@@ -767,10 +1093,11 @@ > > base_class = GCLUE_LOCATION_SOURCE_CLASS (gclue_nmea_source_parent_class); > base_result = base_class->start (source); >- if (base_result != GCLUE_LOCATION_SOURCE_START_RESULT_OK) >+ if (base_result == GCLUE_LOCATION_SOURCE_START_RESULT_FAILED) > return base_result; > >- connect_to_service (GCLUE_NMEA_SOURCE (source)); >+ try_connect_avahi_client (GCLUE_NMEA_SOURCE (source)); >+ reconnect_service (GCLUE_NMEA_SOURCE (source)); > > return base_result; > } >diff -Nurw geoclue-2.6.0/src/gclue-service-client.c geoclue-2.6.0-soup-3.2/src/gclue-service-client.c >--- geoclue-2.6.0/src/gclue-service-client.c 2022-02-10 21:03:30.000000000 +0300 >+++ geoclue-2.6.0-soup-3.2/src/gclue-service-client.c 2022-11-10 23:30:31.821463761 +0300 >@@ -262,10 +262,10 @@ > gclue_dbus_client_set_active (GCLUE_DBUS_CLIENT (client), TRUE); > priv->locator = gclue_locator_new (accuracy_level); > gclue_locator_set_time_threshold (priv->locator, priv->time_threshold); >- g_signal_connect (priv->locator, >+ g_signal_connect_object (priv->locator, > "notify::location", > G_CALLBACK (on_locator_location_changed), >- client); >+ client, 0); > > gclue_location_source_start (GCLUE_LOCATION_SOURCE (priv->locator)); > } >diff -Nurw geoclue-2.6.0/src/gclue-service-manager.c geoclue-2.6.0-soup-3.2/src/gclue-service-manager.c >--- geoclue-2.6.0/src/gclue-service-manager.c 2022-02-10 21:03:30.000000000 +0300 >+++ geoclue-2.6.0-soup-3.2/src/gclue-service-manager.c 2022-11-10 23:30:31.821463761 +0300 >@@ -228,15 +228,15 @@ > } > g_debug ("Number of connected clients: %u", priv->num_clients); > >- g_signal_connect (client, >+ g_signal_connect_object (client, > "notify::active", > G_CALLBACK (on_client_notify_active), >- data->manager); >+ data->manager, 0); > >- g_signal_connect (info, >+ g_signal_connect_object (info, > "peer-vanished", > G_CALLBACK (on_peer_vanished), >- data->manager); >+ data->manager, 0); > > client_created: > if (data->reuse_client) >@@ -434,6 +434,7 @@ > add_agent_data_free (AddAgentData *data) > { > g_clear_pointer (&data->desktop_id, g_free); >+ g_clear_object (&data->manager); > g_slice_free (AddAgentData, data); > } > >@@ -470,10 +471,10 @@ > g_debug ("New agent for user ID '%u'", user_id); > g_hash_table_replace (priv->agents, GINT_TO_POINTER (user_id), agent); > >- g_signal_connect (data->info, >+ g_signal_connect_object (data->info, > "peer-vanished", > G_CALLBACK (on_agent_vanished), >- data->manager); >+ data->manager, 0); > > gclue_dbus_manager_complete_add_agent (data->manager, data->invocation); > >@@ -564,7 +565,7 @@ > peer = g_dbus_method_invocation_get_sender (invocation); > > data = g_slice_new0 (AddAgentData); >- data->manager = manager; >+ data->manager = g_object_ref (manager); > data->invocation = invocation; > data->desktop_id = g_strdup (id); > gclue_client_info_new_async (peer, >@@ -659,10 +660,10 @@ > G_OBJECT_CLASS (gclue_service_manager_parent_class)->constructed (object); > > priv->locator = gclue_locator_new (GCLUE_ACCURACY_LEVEL_EXACT); >- g_signal_connect (G_OBJECT (priv->locator), >+ g_signal_connect_object (G_OBJECT (priv->locator), > "notify::available-accuracy-level", > G_CALLBACK (on_avail_accuracy_level_changed), >- object); >+ object, 0); > on_avail_accuracy_level_changed (G_OBJECT (priv->locator), > NULL, > object); >diff -Nurw geoclue-2.6.0/src/gclue-utils.h geoclue-2.6.0-soup-3.2/src/gclue-utils.h >--- geoclue-2.6.0/src/gclue-utils.h 1970-01-01 03:00:00.000000000 +0300 >+++ geoclue-2.6.0-soup-3.2/src/gclue-utils.h 2022-11-10 23:30:31.821463761 +0300 >@@ -0,0 +1,61 @@ >+/* vim: set et ts=8 sw=8: */ >+/* >+ * Geoclue is free software; you can redistribute it and/or modify it under >+ * the terms of the GNU General Public License as published by the Free >+ * Software Foundation; either version 2 of the License, or (at your option) >+ * any later version. >+ * >+ * Geoclue 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 General Public License for more >+ * details. >+ * >+ * You should have received a copy of the GNU General Public License along >+ * with Geoclue; if not, write to the Free Software Foundation, Inc., >+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA >+ * >+ */ >+ >+#ifndef GCLUE_UTILS_H >+#define GCLUE_UTILS_H >+ >+#include <glib.h> >+#include <string.h> >+ >+G_BEGIN_DECLS >+ >+#ifndef strnpbrk >+inline static const char * >+strnpbrk (const char *s, const char *accept, size_t n) >+{ >+ const char *end; >+ >+ for (end = s + n; s < end && *s != '\0'; s++) { >+ if (strchr (accept, *s)) { >+ return s; >+ } >+ } >+ >+ return NULL; >+} >+#endif >+ >+#ifndef strnspn >+inline static size_t >+strnspn (const char *s, const char *accept, size_t n) >+{ >+ const char *cur, *end; >+ >+ for (cur = s, end = s + n; cur < end && *cur != '\0'; cur++) { >+ if (!strchr (accept, *cur)) { >+ break; >+ } >+ } >+ >+ return cur - s; >+} >+#endif >+ >+G_END_DECLS >+ >+#endif /* GCLUE_UTILS_H */ >diff -Nurw geoclue-2.6.0/src/gclue-web-source.c geoclue-2.6.0-soup-3.2/src/gclue-web-source.c >--- geoclue-2.6.0/src/gclue-web-source.c 2022-02-10 21:03:30.000000000 +0300 >+++ geoclue-2.6.0-soup-3.2/src/gclue-web-source.c 2022-11-10 23:30:31.821463761 +0300 >@@ -27,6 +27,7 @@ > #include "gclue-web-source.h" > #include "gclue-error.h" > #include "gclue-location.h" >+#include "gclue-mozilla.h" > > /** > * SECTION:gclue-web-source >@@ -36,23 +37,37 @@ > * Baseclass for all sources that solely use a web resource for geolocation. > **/ > >-static gboolean >-get_internet_available (void); > static void > refresh_accuracy_level (GClueWebSource *web); > > struct _GClueWebSourcePrivate { >+ GCancellable *cancellable; >+ >+ GClueAccuracyLevel accuracy_level; >+ > SoupSession *soup_session; > > SoupMessage *query; >+ const char *query_data_description; > > gulong network_changed_id; > gulong connectivity_changed_id; > > guint64 last_submitted; > >- gboolean internet_available; >+ const char *locate_url; >+ const char *submit_url; >+ gboolean locate_url_reachable; >+ gboolean submit_url_reachable; >+}; >+ >+enum >+{ >+ PROP_0, >+ PROP_ACCURACY_LEVEL, >+ LAST_PROP > }; >+static GParamSpec *gParamSpecs[LAST_PROP]; > > G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GClueWebSource, > gclue_web_source, >@@ -60,7 +75,7 @@ > G_ADD_PRIVATE (GClueWebSource)) > > static void refresh_callback (SoupSession *session, >- SoupMessage *query, >+ GAsyncResult *result, > gpointer user_data); > > static void >@@ -83,12 +98,11 @@ > return; > } > >- if (!get_internet_available ()) { >+ if (!source->priv->locate_url_reachable) { > g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NETWORK_UNREACHABLE, >- "Network unavailable"); >+ "Cannot reach locate URL"); > return; > } >- g_debug ("Network available"); > > if (source->priv->query != NULL) { > g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_PENDING, >@@ -96,53 +110,59 @@ > return; > } > >- source->priv->query = GCLUE_WEB_SOURCE_GET_CLASS (source)->create_query (source, &local_error); >- >+ source->priv->query = GCLUE_WEB_SOURCE_GET_CLASS (source)->create_query >+ (source, &source->priv->query_data_description, &local_error); > if (source->priv->query == NULL) { > g_task_return_error (task, g_steal_pointer (&local_error)); > return; > } > >- /* TODO handle cancellation */ >- soup_session_queue_message (source->priv->soup_session, >+ soup_session_send_and_read_async (source->priv->soup_session, > source->priv->query, >- refresh_callback, >+ G_PRIORITY_DEFAULT, >+ cancellable, >+ (GAsyncReadyCallback)refresh_callback, > g_steal_pointer (&task)); > } > > static void > refresh_callback (SoupSession *session, >- SoupMessage *query, >+ GAsyncResult *result, > gpointer user_data) > { > g_autoptr(GTask) task = g_steal_pointer (&user_data); > GClueWebSource *web; >+ g_autoptr(SoupMessage) query = NULL; >+ g_autoptr(GBytes) body = NULL; > g_autoptr(GError) local_error = NULL; > g_autofree char *contents = NULL; > g_autofree char *str = NULL; > g_autoptr(GClueLocation) location = NULL; >- SoupURI *uri; >+ GUri *uri; >+ >+ web = GCLUE_WEB_SOURCE (g_task_get_source_object (task)); >+ query = g_steal_pointer (&web->priv->query); > >- if (query->status_code == SOUP_STATUS_CANCELLED) { >- g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_CANCELLED, >- "Operation cancelled"); >+ body = soup_session_send_and_read_finish (session, result, &local_error); >+ if (!body) { >+ g_task_return_error (task, g_steal_pointer (&local_error)); > return; > } > >- web = GCLUE_WEB_SOURCE (g_task_get_source_object (task)); >- web->priv->query = NULL; >- >- if (query->status_code != SOUP_STATUS_OK) { >+ if (soup_message_get_status (query) != SOUP_STATUS_OK) { > g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED, >- "Failed to query location: %s", query->reason_phrase); >+ "Query location SOUP error: %s", >+ soup_message_get_reason_phrase (query)); > return; > } > >- contents = g_strndup (query->response_body->data, query->response_body->length); >+ contents = g_strndup (g_bytes_get_data (body, NULL), g_bytes_get_size (body)); > uri = soup_message_get_uri (query); >- str = soup_uri_to_string (uri, FALSE); >+ str = g_uri_to_string (uri); > g_debug ("Got following response from '%s':\n%s", str, contents); >- location = GCLUE_WEB_SOURCE_GET_CLASS (web)->parse_response (web, contents, &local_error); >+ location = gclue_mozilla_parse_response (contents, >+ web->priv->query_data_description, >+ &local_error); > if (local_error != NULL) { > g_task_return_error (task, g_steal_pointer (&local_error)); > return; >@@ -177,22 +197,16 @@ > location = GCLUE_WEB_SOURCE_GET_CLASS (web)->refresh_finish (web, result, &local_error); > > if (local_error != NULL && >- !g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED)) { >- g_warning ("Failed to query location: %s", local_error->message); >- return; >+ !g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { >+ if (!g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED) && >+ !g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_PENDING)) { >+ g_warning ("Failed to query location: %s", >+ local_error->message); >+ } else { >+ g_debug ("Failed to query location: %s", >+ local_error->message); > } > } >- >-static gboolean >-get_internet_available (void) >-{ >- GNetworkMonitor *monitor = g_network_monitor_get_default (); >- gboolean available; >- >- available = (g_network_monitor_get_connectivity (monitor) == >- G_NETWORK_CONNECTIVITY_FULL); >- >- return available; > } > > static void >@@ -203,7 +217,7 @@ > existing = gclue_location_source_get_available_accuracy_level > (GCLUE_LOCATION_SOURCE (web)); > new = GCLUE_WEB_SOURCE_GET_CLASS (web)->get_available_accuracy_level >- (web, web->priv->internet_available); >+ (web, web->priv->locate_url_reachable); > if (new != existing) { > g_debug ("Available accuracy level from %s: %u", > G_OBJECT_TYPE_NAME (web), new); >@@ -214,18 +228,108 @@ > } > > static void >-on_network_changed (GNetworkMonitor *monitor G_GNUC_UNUSED, >- gboolean available G_GNUC_UNUSED, >+locate_url_checked_cb (GObject *source_object, >+ GAsyncResult *result, > gpointer user_data) > { >- GClueWebSource *web = GCLUE_WEB_SOURCE (user_data); >- gboolean last_available = web->priv->internet_available; >+ GNetworkMonitor *mon = G_NETWORK_MONITOR (source_object); >+ GClueWebSource *web; >+ gboolean reachable, last_reachable; >+ g_autoptr(GError) error = NULL; > >- web->priv->internet_available = get_internet_available (); >- if (last_available == web->priv->internet_available) >+ reachable = g_network_monitor_can_reach_finish (mon, result, &error); >+ if (error && g_error_matches (error, G_IO_ERROR, >+ G_IO_ERROR_CANCELLED)) { >+ return; /* WebSource instance is finalized */ >+ } >+ >+ web = GCLUE_WEB_SOURCE (user_data); >+ last_reachable = web->priv->locate_url_reachable; >+ web->priv->locate_url_reachable = reachable; >+ if (last_reachable == reachable) > return; /* We already reacted to network change */ > >- GCLUE_WEB_SOURCE_GET_CLASS (web)->refresh_async (web, NULL, query_callback, NULL); >+ g_debug ("Network changed: %s", >+ reachable ? "Enabling locate URL queries" : >+ "Disabling locate URL queries"); >+ if (reachable) { >+ GCLUE_WEB_SOURCE_GET_CLASS (web)->refresh_async >+ (web, NULL, query_callback, NULL); >+ } >+} >+ >+static void >+submit_url_checked_cb (GObject *source_object, >+ GAsyncResult *result, >+ gpointer user_data) >+{ >+ GNetworkMonitor *mon = G_NETWORK_MONITOR (source_object); >+ GClueWebSource *web; >+ gboolean reachable, last_reachable; >+ g_autoptr(GError) error = NULL; >+ >+ reachable = g_network_monitor_can_reach_finish (mon, result, &error); >+ if (error && g_error_matches (error, G_IO_ERROR, >+ G_IO_ERROR_CANCELLED)) { >+ return; /* WebSource instance is finalized */ >+ } >+ >+ web = GCLUE_WEB_SOURCE (user_data); >+ last_reachable = web->priv->submit_url_reachable; >+ web->priv->submit_url_reachable = reachable; >+ if (last_reachable == reachable) { >+ return; >+ } >+ g_debug ("Network changed: %s", >+ reachable ? "Enabling submit URL queries" : >+ "Disabling submit URL queries"); >+} >+ >+static void >+on_network_changed (GNetworkMonitor *unused_monitor G_GNUC_UNUSED, >+ gboolean available G_GNUC_UNUSED, >+ gpointer user_data) >+{ >+ GNetworkMonitor *monitor = g_network_monitor_get_default (); >+ GClueWebSource *web = GCLUE_WEB_SOURCE (user_data); >+ g_autoptr(GSocketConnectable) submit_addr = NULL; >+ g_autoptr(GSocketConnectable) locate_addr = NULL; >+ >+ if (web->priv->submit_url) { >+ submit_addr = g_network_address_parse_uri (web->priv->submit_url, >+ 80, NULL); >+ if (submit_addr) { >+ g_network_monitor_can_reach_async (monitor, >+ submit_addr, >+ web->priv->cancellable, >+ submit_url_checked_cb, >+ web); >+ } else { >+ g_warning ("Could not parse submit URL '%s'", >+ web->priv->submit_url); >+ web->priv->submit_url_reachable = FALSE; >+ } >+ } else { >+ web->priv->submit_url_reachable = FALSE; >+ } >+ >+ if (web->priv->locate_url) { >+ locate_addr = g_network_address_parse_uri (web->priv->locate_url, >+ 80, NULL); >+ if (locate_addr) { >+ g_network_monitor_can_reach_async (monitor, >+ locate_addr, >+ web->priv->cancellable, >+ locate_url_checked_cb, >+ web); >+ } else { >+ g_warning ("Could not parse locate URL '%s'", >+ web->priv->locate_url); >+ web->priv->locate_url_reachable = FALSE; >+ } >+ } else { >+ web->priv->locate_url_reachable = FALSE; >+ } > } > > static void >@@ -241,6 +345,8 @@ > { > GClueWebSourcePrivate *priv = GCLUE_WEB_SOURCE (gsource)->priv; > >+ g_cancellable_cancel (priv->cancellable); >+ > if (priv->network_changed_id) { > g_signal_handler_disconnect (g_network_monitor_get_default (), > priv->network_changed_id); >@@ -253,15 +359,13 @@ > priv->connectivity_changed_id = 0; > } > >- if (priv->query != NULL) { >- g_debug ("Cancelling query"); >- soup_session_cancel_message (priv->soup_session, >- priv->query, >- SOUP_STATUS_CANCELLED); >- priv->query = NULL; >+ if (priv->soup_session) { >+ soup_session_abort (priv->soup_session); >+ g_clear_object (&priv->soup_session); > } > >- g_clear_object (&priv->soup_session); >+ g_clear_object (&priv->query); >+ g_clear_object (&priv->cancellable); > > G_OBJECT_CLASS (gclue_web_source_parent_class)->finalize (gsource); > } >@@ -274,10 +378,8 @@ > > G_OBJECT_CLASS (gclue_web_source_parent_class)->constructed (object); > >- priv->soup_session = soup_session_new_with_options >- (SOUP_SESSION_REMOVE_FEATURE_BY_TYPE, >- SOUP_TYPE_PROXY_RESOLVER_DEFAULT, >- NULL); >+ priv->soup_session = soup_session_new (); >+ soup_session_remove_feature_by_type (priv->soup_session, G_TYPE_PROXY_RESOLVER); > > monitor = g_network_monitor_get_default (); > priv->network_changed_id = >@@ -296,6 +398,42 @@ > } > > static void >+gclue_web_source_get_property (GObject *object, >+ guint prop_id, >+ GValue *value, >+ GParamSpec *pspec) >+{ >+ GClueWebSource *web = GCLUE_WEB_SOURCE (object); >+ >+ switch (prop_id) { >+ case PROP_ACCURACY_LEVEL: >+ g_value_set_enum (value, web->priv->accuracy_level); >+ break; >+ >+ default: >+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); >+ } >+} >+ >+static void >+gclue_web_source_set_property (GObject *object, >+ guint prop_id, >+ const GValue *value, >+ GParamSpec *pspec) >+{ >+ GClueWebSource *web = GCLUE_WEB_SOURCE (object); >+ >+ switch (prop_id) { >+ case PROP_ACCURACY_LEVEL: >+ web->priv->accuracy_level = g_value_get_enum (value); >+ break; >+ >+ default: >+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); >+ } >+} >+ >+static void > gclue_web_source_class_init (GClueWebSourceClass *klass) > { > GObjectClass *gsource_class = G_OBJECT_CLASS (klass); >@@ -303,14 +441,28 @@ > klass->refresh_async = gclue_web_source_real_refresh_async; > klass->refresh_finish = gclue_web_source_real_refresh_finish; > >+ gsource_class->get_property = gclue_web_source_get_property; >+ gsource_class->set_property = gclue_web_source_set_property; > gsource_class->finalize = gclue_web_source_finalize; > gsource_class->constructed = gclue_web_source_constructed; >+ >+ gParamSpecs[PROP_ACCURACY_LEVEL] = g_param_spec_enum ("accuracy-level", >+ "AccuracyLevel", >+ "Max accuracy level", >+ GCLUE_TYPE_ACCURACY_LEVEL, >+ GCLUE_ACCURACY_LEVEL_CITY, >+ G_PARAM_READWRITE | >+ G_PARAM_CONSTRUCT_ONLY); >+ g_object_class_install_property (gsource_class, >+ PROP_ACCURACY_LEVEL, >+ gParamSpecs[PROP_ACCURACY_LEVEL]); > } > > static void > gclue_web_source_init (GClueWebSource *web) > { > web->priv = gclue_web_source_get_instance_private (web); >+ web->priv->cancellable = g_cancellable_new (); > } > > /** >@@ -331,24 +483,33 @@ > > static void > submit_query_callback (SoupSession *session, >- SoupMessage *query, >+ GAsyncResult *result, > gpointer user_data) > { >- SoupURI *uri; >- g_autofree char *str = NULL; >+ g_autoptr(GBytes) body = NULL; >+ g_autoptr(GError) local_error = NULL; >+ SoupMessage *query; >+ g_autofree char *uri_str = NULL; >+ gint status_code; > >- uri = soup_message_get_uri (query); >- str = soup_uri_to_string (uri, FALSE); >- if (query->status_code != SOUP_STATUS_OK && >- query->status_code != SOUP_STATUS_NO_CONTENT) { >+ query = soup_session_get_async_result_message (session, result); >+ uri_str = g_uri_to_string (soup_message_get_uri (query)); >+ >+ body = soup_session_send_and_read_finish (session, result, &local_error); >+ if (!body) { >+ g_warning ("Failed to submit location data to '%s': %s", >+ uri_str, local_error->message); >+ return; >+ } >+ >+ status_code = soup_message_get_status (query); >+ if (status_code != SOUP_STATUS_OK && status_code != SOUP_STATUS_NO_CONTENT) { > g_warning ("Failed to submit location data to '%s': %s", >- str, >- query->reason_phrase); >+ uri_str, soup_message_get_reason_phrase (query)); > return; > } > >- g_debug ("Successfully submitted location data to '%s'", >- str); >+ g_debug ("Successfully submitted location data to '%s'", uri_str); > } > > #define SUBMISSION_ACCURACY_THRESHOLD 100 >@@ -362,8 +523,8 @@ > GClueLocationSource *source = GCLUE_LOCATION_SOURCE (source_object); > GClueWebSource *web = GCLUE_WEB_SOURCE (user_data); > GClueLocation *location; >- SoupMessage *query; >- GError *error = NULL; >+ g_autoptr(SoupMessage) query = NULL; >+ g_autoptr(GError) error = NULL; > > location = gclue_location_source_get_location (source); > if (location == NULL || >@@ -375,7 +536,7 @@ > > web->priv->last_submitted = gclue_location_get_timestamp (location); > >- if (!get_internet_available ()) >+ if (!web->priv->submit_url_reachable) > return; > > query = GCLUE_WEB_SOURCE_GET_CLASS (web)->create_submit_query >@@ -386,15 +547,16 @@ > if (error != NULL) { > g_warning ("Failed to create submission query: %s", > error->message); >- g_error_free (error); > } > > return; > } > >- soup_session_queue_message (web->priv->soup_session, >+ soup_session_send_and_read_async (web->priv->soup_session, > query, >- submit_query_callback, >+ G_PRIORITY_DEFAULT, >+ NULL, >+ (GAsyncReadyCallback)submit_query_callback, > web); > } > >@@ -423,3 +585,17 @@ > > on_submit_source_location_notify (G_OBJECT (submit_source), NULL, web); > } >+ >+void >+gclue_web_source_set_locate_url (GClueWebSource *source, >+ const char *url) >+{ >+ source->priv->locate_url = url; >+} >+ >+void >+gclue_web_source_set_submit_url (GClueWebSource *source, >+ const char *url) >+{ >+ source->priv->submit_url = url; >+} >diff -Nurw geoclue-2.6.0/src/gclue-web-source.h geoclue-2.6.0-soup-3.2/src/gclue-web-source.h >--- geoclue-2.6.0/src/gclue-web-source.h 2022-02-10 21:03:30.000000000 +0300 >+++ geoclue-2.6.0-soup-3.2/src/gclue-web-source.h 2022-11-10 23:30:31.822463754 +0300 >@@ -71,13 +71,11 @@ > GError **error); > > SoupMessage * (*create_query) (GClueWebSource *source, >+ const char **query_data_description, > GError **error); > SoupMessage * (*create_submit_query) (GClueWebSource *source, > GClueLocation *location, > GError **error); >- GClueLocation * (*parse_response) (GClueWebSource *source, >- const char *response, >- GError **error); > GClueAccuracyLevel (*get_available_accuracy_level) > (GClueWebSource *source, > gboolean network_available); >@@ -86,6 +84,10 @@ > void gclue_web_source_refresh (GClueWebSource *source); > void gclue_web_source_set_submit_source (GClueWebSource *source, > GClueLocationSource *submit_source); >+void gclue_web_source_set_locate_url (GClueWebSource *source, >+ const char *url); >+void gclue_web_source_set_submit_url (GClueWebSource *source, >+ const char *url); > > G_END_DECLS > >diff -Nurw geoclue-2.6.0/src/gclue-wifi.c geoclue-2.6.0-soup-3.2/src/gclue-wifi.c >--- geoclue-2.6.0/src/gclue-wifi.c 2022-02-10 21:03:30.000000000 +0300 >+++ geoclue-2.6.0-soup-3.2/src/gclue-wifi.c 2022-11-10 23:30:31.822463754 +0300 >@@ -24,6 +24,7 @@ > #include <string.h> > #include <config.h> > #include "gclue-wifi.h" >+#include "gclue-3g.h" > #include "gclue-config.h" > #include "gclue-error.h" > #include "gclue-mozilla.h" >@@ -49,6 +50,12 @@ > * full cache (excluding overheads). */ > #define CACHE_ENTRY_MAX_AGE_SECONDS (48 * 60 * 60) > >+/* The signal strength can typically vary by ±5 for a stationary laptop, so >+ * match cache entries with that tolerance. >+ * In dBm units. >+ */ >+#define CACHE_ENTRY_MATCH_SIGNAL_WINDOW 10 >+ > /** > * SECTION:gclue-wifi > * @short_description: WiFi-based geolocation >@@ -78,7 +85,58 @@ > static void > disconnect_cache_prune_timeout (GClueWifi *wifi); > >+typedef struct { >+ GArray *signals; >+ GClueLocation *location; >+} LocationCacheElement; >+ >+static LocationCacheElement * >+location_cache_element_new (GArray *signals, >+ GClueLocation *location) >+{ >+ LocationCacheElement *element; >+ >+ element = g_slice_new (LocationCacheElement); >+ element->signals = signals; >+ element->location = g_object_ref (location); >+ return element; >+} >+ >+static void location_cache_element_free (gpointer data) >+{ >+ LocationCacheElement *element = data; >+ >+ if (element->signals) >+ g_array_free (element->signals, TRUE); >+ g_clear_object (&element->location); >+ g_slice_free (LocationCacheElement, element); >+} >+ >+typedef struct { >+ GList *elements; >+} LocationCacheValue; >+ >+static LocationCacheValue * >+location_cache_value_new (void) >+{ >+ LocationCacheValue *value; >+ >+ value = g_slice_new (LocationCacheValue); >+ value->elements = NULL; >+ return value; >+} >+ >+static void location_cache_value_free (gpointer data) >+{ >+ LocationCacheValue *value = data; >+ >+ g_list_free_full (value->elements, location_cache_element_free); >+ g_slice_free (LocationCacheValue, value); >+} >+ > struct _GClueWifiPrivate { >+ GCancellable *intf_cancellable, *bss_cancellable; >+ GClueMozilla *mozilla; > WPASupplicant *supplicant; > WPAInterface *interface; > GHashTable *bss_proxies; >@@ -92,9 +150,7 @@ > > guint scan_timeout; > >- GClueAccuracyLevel accuracy_level; >- >- GHashTable *location_cache; /* (element-type GVariant GClueLocation) (owned) */ >+ GHashTable *location_cache; /* (element-type GVariant LocationCacheValue) (owned) */ > guint cache_prune_timeout_id; > guint cache_hits, cache_misses; > >@@ -104,25 +160,14 @@ > #endif > }; > >-enum >-{ >- PROP_0, >- PROP_ACCURACY_LEVEL, >- LAST_PROP >-}; >-static GParamSpec *gParamSpecs[LAST_PROP]; >- > static SoupMessage * > gclue_wifi_create_query (GClueWebSource *source, >+ const char **query_data_description, > GError **error); > static SoupMessage * > gclue_wifi_create_submit_query (GClueWebSource *source, > GClueLocation *location, > GError **error); >-static GClueLocation * >-gclue_wifi_parse_response (GClueWebSource *source, >- const char *json, >- GError **error); > static GClueAccuracyLevel > gclue_wifi_get_available_accuracy_level (GClueWebSource *source, > gboolean net_available); >@@ -150,6 +195,8 @@ > > G_OBJECT_CLASS (gclue_wifi_parent_class)->finalize (gwifi); > >+ g_cancellable_cancel (wifi->priv->intf_cancellable); >+ > disconnect_bss_signals (wifi); > disconnect_cache_prune_timeout (wifi); > >@@ -158,48 +205,14 @@ > g_clear_pointer (&wifi->priv->bss_proxies, g_hash_table_unref); > g_clear_pointer (&wifi->priv->ignored_bss_proxies, g_hash_table_unref); > g_clear_pointer (&wifi->priv->location_cache, g_hash_table_unref); >+ g_clear_object (&wifi->priv->mozilla); >+ g_clear_object (&wifi->priv->intf_cancellable); > } > > static void > gclue_wifi_constructed (GObject *object); > > static void >-gclue_wifi_get_property (GObject *object, >- guint prop_id, >- GValue *value, >- GParamSpec *pspec) >-{ >- GClueWifi *wifi = GCLUE_WIFI (object); >- >- switch (prop_id) { >- case PROP_ACCURACY_LEVEL: >- g_value_set_enum (value, wifi->priv->accuracy_level); >- break; >- >- default: >- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); >- } >-} >- >-static void >-gclue_wifi_set_property (GObject *object, >- guint prop_id, >- const GValue *value, >- GParamSpec *pspec) >-{ >- GClueWifi *wifi = GCLUE_WIFI (object); >- >- switch (prop_id) { >- case PROP_ACCURACY_LEVEL: >- wifi->priv->accuracy_level = g_value_get_enum (value); >- break; >- >- default: >- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); >- } >-} >- >-static void > gclue_wifi_class_init (GClueWifiClass *klass) > { > GClueWebSourceClass *web_class = GCLUE_WEB_SOURCE_CLASS (klass); >@@ -212,24 +225,10 @@ > web_class->refresh_finish = gclue_wifi_refresh_finish; > web_class->create_submit_query = gclue_wifi_create_submit_query; > web_class->create_query = gclue_wifi_create_query; >- web_class->parse_response = gclue_wifi_parse_response; > web_class->get_available_accuracy_level = > gclue_wifi_get_available_accuracy_level; >- gwifi_class->get_property = gclue_wifi_get_property; >- gwifi_class->set_property = gclue_wifi_set_property; > gwifi_class->finalize = gclue_wifi_finalize; > gwifi_class->constructed = gclue_wifi_constructed; >- >- gParamSpecs[PROP_ACCURACY_LEVEL] = g_param_spec_enum ("accuracy-level", >- "AccuracyLevel", >- "Max accuracy level", >- GCLUE_TYPE_ACCURACY_LEVEL, >- GCLUE_ACCURACY_LEVEL_CITY, >- G_PARAM_READWRITE | >- G_PARAM_CONSTRUCT_ONLY); >- g_object_class_install_property (gwifi_class, >- PROP_ACCURACY_LEVEL, >- gParamSpecs[PROP_ACCURACY_LEVEL]); > } > > static void >@@ -345,19 +344,24 @@ > GAsyncResult *res, > gpointer user_data) > { >- GClueWifi *wifi = GCLUE_WIFI (user_data); >+ GClueWifi *wifi; > WPABSS *bss; >- GError *error = NULL; >+ g_autoptr(GError) error = NULL; > char ssid[MAX_SSID_LEN + 1] = { 0 }; > > bss = wpa_bss_proxy_new_for_bus_finish (res, &error); > if (bss == NULL) { >- g_debug ("%s", error->message); >- g_error_free (error); >+ if (error && !g_error_matches (error, G_IO_ERROR, >+ G_IO_ERROR_CANCELLED)) { >+ g_warning ("BSS proxy setup failed: %s", >+ error->message); >+ } > > return; > } > >+ wifi = GCLUE_WIFI (user_data); >+ > if (gclue_mozilla_should_ignore_bss (bss)) { > g_object_unref (bss); > >@@ -365,7 +369,7 @@ > } > > get_ssid_from_bss (bss, ssid); >- g_debug ("WiFi AP '%s' added.", ssid); >+ g_debug ("Got WiFi AP '%s'", ssid); > > if (wpa_bss_get_signal (bss) <= WIFI_SCAN_BSS_NOISE_LEVEL) { > const char *path; >@@ -396,13 +400,15 @@ > GVariant *properties, > gpointer user_data) > { >+ GClueWifi *wifi = GCLUE_WIFI (user_data); >+ > wpa_bss_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, > G_DBUS_PROXY_FLAGS_NONE, > "fi.w1.wpa_supplicant1", > path, >- NULL, >+ wifi->priv->bss_cancellable, > on_bss_proxy_ready, >- user_data); >+ wifi); > } > > static gboolean >@@ -459,7 +465,7 @@ > > wpa_interface_call_scan (WPA_INTERFACE (priv->interface), > args, >- NULL, >+ priv->bss_cancellable, > on_scan_call_done, > wifi); > } >@@ -492,7 +498,6 @@ > GClueWifi *wifi = GCLUE_WIFI (user_data); > GClueWifiPrivate *priv = wifi->priv; > >- g_debug ("WiFi scan timeout."); > priv->scan_timeout = 0; > > if (priv->interface == NULL) >@@ -503,6 +508,11 @@ > return G_SOURCE_REMOVE; > } > >+gboolean gclue_wifi_should_skip_bsss (GClueAccuracyLevel level) >+{ >+ return level < GCLUE_ACCURACY_LEVEL_STREET; >+} >+ > static gboolean > on_scan_wait_done (gpointer wifi) > { >@@ -511,9 +521,13 @@ > g_return_val_if_fail (GCLUE_IS_WIFI (wifi), G_SOURCE_REMOVE); > priv = GCLUE_WIFI(wifi)->priv; > >+ /* We have the latest scan result */ >+ gclue_mozilla_set_wifi (priv->mozilla, wifi); >+ > if (priv->bss_list_changed) { > priv->bss_list_changed = FALSE; >- g_debug ("Refreshing locationâ¦"); >+ g_debug ("WiFi BSS list changed, refreshing locationâ¦"); >+ gclue_mozilla_set_bss_dirty (priv->mozilla); > gclue_web_source_refresh (GCLUE_WEB_SOURCE (wifi)); > } > priv->scan_wait_id = 0; >@@ -521,6 +535,15 @@ > return G_SOURCE_REMOVE; > } > >+static GClueAccuracyLevel >+get_accuracy_level (GClueWifi *wifi) >+{ >+ GClueAccuracyLevel level; >+ >+ g_object_get (G_OBJECT (wifi), "accuracy-level", &level, NULL); >+ return level; >+} >+ > static void > on_scan_done (WPAInterface *object, > gboolean success, >@@ -535,7 +558,6 @@ > > return; > } >- g_debug ("WiFi scan completed"); > > if (priv->interface == NULL) > return; >@@ -558,14 +580,14 @@ > * user's location can change quickly. With low accuracy, we don't since > * we wouldn't want to drain power unnecessarily. > */ >- if (priv->accuracy_level >= GCLUE_ACCURACY_LEVEL_STREET) >+ if (get_accuracy_level (wifi) >= GCLUE_ACCURACY_LEVEL_STREET) > timeout = WIFI_SCAN_TIMEOUT_HIGH_ACCURACY; > else > timeout = WIFI_SCAN_TIMEOUT_LOW_ACCURACY; > priv->scan_timeout = g_timeout_add_seconds (timeout, > on_scan_timeout, > wifi); >- g_debug ("Next scan scheduled in %u seconds", timeout); >+ g_debug ("WiFi scan done, next scheduled in %u seconds", timeout); > } > > static void >@@ -573,20 +595,24 @@ > GAsyncResult *res, > gpointer user_data) > { >- GClueWifi *wifi = GCLUE_WIFI (user_data); >- GError *error = NULL; >+ g_autoptr(GError) error = NULL; > >- if (!wpa_interface_call_scan_finish >- (WPA_INTERFACE (source_object), >- res, >- &error)) { >+ if (!wpa_interface_call_scan_finish (WPA_INTERFACE (source_object), >+ res, &error)) { >+ GClueWifi *wifi; >+ >+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { >+ return; >+ } >+ >+ wifi = GCLUE_WIFI (user_data); >+ >+ if (error) { > g_warning ("Scanning of WiFi networks failed: %s", > error->message); >- g_error_free (error); >+ } > > cancel_wifi_scan (wifi); >- >- return; > } > } > >@@ -605,6 +631,9 @@ > return; > } > >+ g_assert (!priv->bss_cancellable); >+ priv->bss_cancellable = g_cancellable_new (); >+ > start_wifi_scan (wifi); > > priv->bss_list_changed = TRUE; >@@ -633,6 +662,12 @@ > { > GClueWifiPrivate *priv = wifi->priv; > >+ if (priv->bss_cancellable) { >+ g_debug ("Cancelling WiFi requests"); >+ g_cancellable_cancel (priv->bss_cancellable); >+ g_clear_object (&priv->bss_cancellable); >+ } >+ > cancel_wifi_scan (wifi); > > if (priv->bss_added_id != 0) { >@@ -657,22 +692,46 @@ > GHashTableIter iter; > gpointer value; > guint64 cutoff_seconds; >- guint old_cache_size; >+ guint old_cache_size, removed_elements = 0; > > old_cache_size = g_hash_table_size (priv->location_cache); > cutoff_seconds = g_get_real_time () / G_USEC_PER_SEC - CACHE_ENTRY_MAX_AGE_SECONDS; > > g_hash_table_iter_init (&iter, priv->location_cache); > while (g_hash_table_iter_next (&iter, NULL, &value)) { >- GClueLocation *location = GCLUE_LOCATION (value); >- guint64 timestamp_seconds = gclue_location_get_timestamp (location); >+ LocationCacheValue *lcvalue = (LocationCacheValue *)value; >+ GList *l = lcvalue->elements; >+ >+ g_assert (l); >+ while (l) { >+ LocationCacheElement *element = (LocationCacheElement *)l->data; >+ GList *lnext = l->next; >+ >+ /* Keep this location? */ >+ if (gclue_location_get_timestamp (element->location) > >+ cutoff_seconds) >+ goto next_el; >+ >+ location_cache_element_free (element); >+ lcvalue->elements = g_list_delete_link (lcvalue->elements, l); >+ removed_elements++; > >- if (timestamp_seconds <= cutoff_seconds) >+ /* Deleted the last entry (element) in this hash bucket? >+ * Remove this hash table entry then. >+ */ >+ if (!lcvalue->elements) { >+ g_assert (!lnext); > g_hash_table_iter_remove (&iter); > } > >- g_debug ("Pruned cache (old size: %u, new size: %u)", >- old_cache_size, g_hash_table_size (priv->location_cache)); >+ next_el: >+ l = lnext; >+ } >+ } >+ >+ g_debug ("Pruned cache (old size: %u, new size: %u, removed elements: %u)", >+ old_cache_size, g_hash_table_size (priv->location_cache), >+ removed_elements); > } > > #if GLIB_CHECK_VERSION(2, 64, 0) >@@ -782,6 +841,8 @@ > static GClueLocationSourceStopResult > gclue_wifi_stop (GClueLocationSource *source) > { >+ GClueWifi *wifi = GCLUE_WIFI (source); >+ GClueWifiPrivate *priv = wifi->priv; > GClueLocationSourceClass *base_class; > GClueLocationSourceStopResult base_result; > >@@ -795,6 +856,10 @@ > disconnect_bss_signals (GCLUE_WIFI (source)); > disconnect_cache_prune_timeout (GCLUE_WIFI (source)); > >+ if (gclue_mozilla_test_set_wifi (priv->mozilla, wifi, NULL)) { >+ g_debug ("Removed us as the WiFi source on stop"); >+ } >+ > return base_result; > } > >@@ -802,15 +867,15 @@ > gclue_wifi_get_available_accuracy_level (GClueWebSource *source, > gboolean net_available) > { >- GClueWifiPrivate *priv = GCLUE_WIFI (source)->priv; >+ GClueWifi *wifi = GCLUE_WIFI (source); >+ GClueWifiPrivate *priv = wifi->priv; > > if (!net_available) > return GCLUE_ACCURACY_LEVEL_NONE; >- else if (priv->interface != NULL && >- priv->accuracy_level != GCLUE_ACCURACY_LEVEL_CITY) >- return GCLUE_ACCURACY_LEVEL_STREET; >- else >+ else if (!priv->interface) > return GCLUE_ACCURACY_LEVEL_CITY; >+ else >+ return MIN (get_accuracy_level (wifi), GCLUE_ACCURACY_LEVEL_STREET); > } > > static void >@@ -818,18 +883,25 @@ > GAsyncResult *res, > gpointer user_data) > { >- GClueWifi *wifi = GCLUE_WIFI (user_data); >+ GClueWifi *wifi; > WPAInterface *interface; >- GError *error = NULL; >+ g_autoptr(GError) error = NULL; > > interface = wpa_interface_proxy_new_for_bus_finish (res, &error); > if (interface == NULL) { >- g_debug ("%s", error->message); >- g_error_free (error); >+ if (error) { >+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { >+ return; >+ } >+ >+ g_warning ("Interface proxy add failed: %s", >+ error->message); >+ } > > return; > } > >+ wifi = GCLUE_WIFI (user_data); > if (wifi->priv->interface != NULL) { > g_object_unref (interface); > return; >@@ -860,7 +932,7 @@ > G_DBUS_PROXY_FLAGS_NONE, > "fi.w1.wpa_supplicant1", > path, >- NULL, >+ wifi->priv->intf_cancellable, > on_interface_proxy_ready, > wifi); > } >@@ -888,14 +960,27 @@ > disconnect_bss_signals (wifi); > g_clear_object (&wifi->priv->interface); > >+ if (gclue_mozilla_test_set_wifi (priv->mozilla, wifi, NULL)) { >+ g_debug ("Removed interface was the WiFi source"); >+ } >+ > gclue_web_source_refresh (GCLUE_WEB_SOURCE (wifi)); > } > > static void > gclue_wifi_init (GClueWifi *wifi) > { >+ GClueWebSource *web_source = GCLUE_WEB_SOURCE (wifi); >+ > wifi->priv = gclue_wifi_get_instance_private (wifi); > >+ wifi->priv->intf_cancellable = g_cancellable_new (); >+ wifi->priv->mozilla = gclue_mozilla_get_singleton (); >+ gclue_web_source_set_locate_url (web_source, >+ gclue_mozilla_get_locate_url (wifi->priv->mozilla)); >+ gclue_web_source_set_submit_url (web_source, >+ gclue_mozilla_get_submit_url (wifi->priv->mozilla)); >+ > wifi->priv->bss_proxies = g_hash_table_new_full (g_str_hash, > g_str_equal, > g_free, >@@ -907,7 +992,7 @@ > wifi->priv->location_cache = g_hash_table_new_full (variant_hash, > g_variant_equal, > (GDestroyNotify) g_variant_unref, >- g_object_unref); >+ location_cache_value_free); > } > > static void >@@ -916,11 +1001,11 @@ > GClueWifi *wifi = GCLUE_WIFI (object); > GClueWifiPrivate *priv = wifi->priv; > const gchar *const *interfaces; >- GError *error = NULL; >+ g_autoptr(GError) error = NULL; > > G_OBJECT_CLASS (gclue_wifi_parent_class)->constructed (object); > >- if (wifi->priv->accuracy_level == GCLUE_ACCURACY_LEVEL_CITY) { >+ if (get_accuracy_level (wifi) == GCLUE_ACCURACY_LEVEL_CITY) { > GClueConfig *config = gclue_config_get_singleton (); > > if (!gclue_config_get_enable_wifi_source (config)) >@@ -936,20 +1021,20 @@ > NULL, > &error); > if (priv->supplicant == NULL) { >+ if (error) > g_warning ("Failed to connect to wpa_supplicant service: %s", > error->message); >- g_error_free (error); > goto refresh_n_exit; > } > >- g_signal_connect (priv->supplicant, >+ g_signal_connect_object (priv->supplicant, > "interface-added", > G_CALLBACK (on_interface_added), >- wifi); >- g_signal_connect (priv->supplicant, >+ wifi, 0); >+ g_signal_connect_object (priv->supplicant, > "interface-removed", > G_CALLBACK (on_interface_removed), >- wifi); >+ wifi, 0); > > interfaces = wpa_supplicant_get_interfaces (priv->supplicant); > if (interfaces != NULL && interfaces[0] != NULL) >@@ -982,23 +1067,29 @@ > GClueWifi * > gclue_wifi_get_singleton (GClueAccuracyLevel level) > { >- static GClueWifi *wifi[] = { NULL, NULL }; >+ static GClueWifi *wifi[] = { NULL, NULL, NULL }; > guint i; >+ GClueConfig *config = gclue_config_get_singleton (); >+ gboolean wifi_enabled; > gboolean scramble_location = FALSE; > gboolean compute_movement = FALSE; > > g_return_val_if_fail (level >= GCLUE_ACCURACY_LEVEL_CITY, NULL); >- if (level == GCLUE_ACCURACY_LEVEL_NEIGHBORHOOD) >- level = GCLUE_ACCURACY_LEVEL_CITY; > >+ wifi_enabled = gclue_config_get_enable_wifi_source (config); > if (level == GCLUE_ACCURACY_LEVEL_CITY) { >- GClueConfig *config = gclue_config_get_singleton (); >- > i = 0; >- if (gclue_config_get_enable_wifi_source (config)) >+ if (wifi_enabled) > scramble_location = TRUE; >- } else { >+ } else if (level == GCLUE_ACCURACY_LEVEL_NEIGHBORHOOD) { >+ g_return_val_if_fail (wifi_enabled, NULL); >+ > i = 1; >+ scramble_location = TRUE; >+ } else { >+ g_return_val_if_fail (wifi_enabled, NULL); >+ >+ i = 2; > compute_movement = TRUE; > } > >@@ -1017,38 +1108,33 @@ > return wifi[i]; > } > >-GClueAccuracyLevel >-gclue_wifi_get_accuracy_level (GClueWifi *wifi) >+static gboolean >+wifi_should_skip_tower (GClueWifi *wifi) > { >- g_return_val_if_fail (GCLUE_IS_WIFI (wifi), >- GCLUE_ACCURACY_LEVEL_NONE); >- >- return wifi->priv->accuracy_level; >+ return gclue_3g_should_skip_tower (get_accuracy_level (wifi)); > } > > /* Can return NULL, signifying an empty BSS list. */ >-static GList * >-get_bss_list (GClueWifi *wifi) >+GList * >+gclue_wifi_get_bss_list (GClueWifi *wifi) > { > return g_hash_table_get_values (wifi->priv->bss_proxies); > } > > static SoupMessage * > gclue_wifi_create_query (GClueWebSource *source, >+ const char **query_data_description, > GError **error) > { > GClueWifi *wifi = GCLUE_WIFI (source); >- GList *bss_list = NULL; /* As in Access Points */ >- SoupMessage *msg; >+ gboolean skip_tower; > > if (wifi->priv->interface == NULL) { > goto create_query; > } > >- bss_list = get_bss_list (wifi); >- > /* Empty list? */ >- if (bss_list == NULL) { >+ if (!g_hash_table_size (wifi->priv->bss_proxies)) { > g_set_error_literal (error, > G_IO_ERROR, > G_IO_ERROR_FAILED, >@@ -1057,17 +1143,13 @@ > } > > create_query: >- msg = gclue_mozilla_create_query (bss_list, NULL, error); >- g_list_free (bss_list); >- return msg; >+ skip_tower = wifi_should_skip_tower (wifi); >+ if (skip_tower) { >+ g_debug ("Will skip 3GPP tower in query due to our accuracy level"); > } > >-static GClueLocation * >-gclue_wifi_parse_response (GClueWebSource *source, >- const char *json, >- GError **error) >-{ >- return gclue_mozilla_parse_response (json, error); >+ return gclue_mozilla_create_query (wifi->priv->mozilla, skip_tower, FALSE, >+ query_data_description, error); > } > > static SoupMessage * >@@ -1076,7 +1158,6 @@ > GError **error) > { > GClueWifi *wifi = GCLUE_WIFI (source); >- GList *bss_list; /* As in Access Points */ > SoupMessage * msg; > > if (wifi->priv->interface == NULL) { >@@ -1087,10 +1168,8 @@ > return NULL; > } > >- bss_list = get_bss_list (wifi); >- > /* Empty list? */ >- if (bss_list == NULL) { >+ if (!g_hash_table_size (wifi->priv->bss_proxies)) { > g_set_error_literal (error, > G_IO_ERROR, > G_IO_ERROR_FAILED, >@@ -1098,11 +1177,9 @@ > return NULL; > } > >- msg = gclue_mozilla_create_submit_query (location, >- bss_list, >- NULL, >+ msg = gclue_mozilla_create_submit_query (wifi->priv->mozilla, >+ location, > error); >- g_list_free (bss_list); > return msg; > } > >@@ -1145,20 +1222,46 @@ > return g_bytes_hash (bytes); > } > >-static GVariant * >-get_location_cache_key (GClueWifi *wifi) >+static void location_cache_key_fill_tower (GClueWifi *wifi, GClue3GTower *tower) >+{ >+ GClueWifiPrivate *priv = wifi->priv; >+ GClue3GTower *moztower; >+ >+ memset (tower, 0, sizeof (*tower)); >+ tower->tec = GCLUE_TOWER_TEC_NO_FIX; >+ >+ moztower = gclue_mozilla_get_tower (priv->mozilla); >+ if (!moztower || wifi_should_skip_tower (wifi)) { >+ return; >+ } >+ >+ g_assert (moztower->tec != GCLUE_TOWER_TEC_NO_FIX); >+ *tower = *moztower; >+} >+ >+static void location_cache_key_add_tower (GClueWifi *wifi, GVariantBuilder *builder) >+{ >+ GClue3GTower tower; >+ >+ location_cache_key_fill_tower (wifi, &tower); >+ g_variant_builder_add (builder, "u", (guint32)tower.tec); >+ g_variant_builder_add (builder, "s", tower.opc); >+ g_variant_builder_add (builder, "t", (guint64)tower.lac); >+ g_variant_builder_add (builder, "t", (guint64)tower.cell_id); >+} >+ >+static GPtrArray * >+get_location_cache_bss_array (GClueWifi *wifi) > { > GHashTableIter iter; > gpointer value; > g_autoptr(GPtrArray) bss_array = g_ptr_array_new_with_free_func (NULL); /* (element-type WPABSS) */ >- guint i; >- GVariantBuilder builder; > > /* The Mozilla service puts BSSID and signal strength for each BSS into >- * its query. The signal strength can typically vary by ±5 for a >- * stationary laptop, so quantise by that. Pack the whole lot into a >- * #GVariant for simplicity, sorted by MAC address. The sorting has to >- * happen in an array beforehand, as variants are immutable. */ >+ * its query. Pack the whole lot into a #GVariant for simplicity, sorted >+ * by MAC address. The sorting has to happen in an array beforehand, >+ * as variants are immutable. >+ */ > g_hash_table_iter_init (&iter, wifi->priv->bss_proxies); > > while (g_hash_table_iter_next (&iter, NULL, &value)) { >@@ -1169,39 +1272,142 @@ > > g_ptr_array_sort (bss_array, bss_compare); > >+ return g_steal_pointer (&bss_array); >+} >+ >+static GVariant * >+get_location_cache_hashtable_key (GClueWifi *wifi, GPtrArray *bss_array) >+{ >+ guint i; >+ GVariantBuilder builder; >+ > /* Serialise to a variant. */ >- g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ayn)")); >+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("(usttaay)")); >+ location_cache_key_add_tower (wifi, &builder); >+ >+ g_variant_builder_open (&builder, G_VARIANT_TYPE ("aay")); > for (i = 0; i < bss_array->len; i++) { > WPABSS *bss = WPA_BSS (bss_array->pdata[i]); > GVariant *bssid; > >- g_variant_builder_open (&builder, G_VARIANT_TYPE ("(ayn)")); >- > bssid = wpa_bss_get_bssid (bss); > if (bssid == NULL) > continue; > > g_variant_builder_add_value (&builder, bssid); >- g_variant_builder_add (&builder, "n", wpa_bss_get_signal (bss) / 10); >- >- g_variant_builder_close (&builder); > } >+ g_variant_builder_close (&builder); > > return g_variant_builder_end (&builder); > } > >+static GArray * >+get_location_cache_signal_array (GClueWifi *wifi, GPtrArray *bss_array) >+{ >+ g_autoptr(GArray) signal_array = NULL; >+ guint i; >+ >+ signal_array = g_array_sized_new (FALSE, FALSE, sizeof (gint16), bss_array->len); >+ for (i = 0; i < bss_array->len; i++) { >+ WPABSS *bss = WPA_BSS (bss_array->pdata[i]); >+ gint16 signal = wpa_bss_get_signal (bss); >+ >+ g_array_append_val (signal_array, signal); >+ } >+ >+ return g_steal_pointer (&signal_array); >+} >+ >+static gboolean cached_signals_match (GArray *signals1, GArray *signals2) >+{ >+ guint i; >+ >+ if (signals1->len != signals2->len) { >+ g_warning ("Different signal count in one hash table entry: %u vs %u", >+ signals1->len, signals2->len); >+ return FALSE; >+ } >+ >+ for (i = 0; i < signals1->len; i++) { >+ gint s1 = g_array_index (signals1, gint16, i); >+ gint s2 = g_array_index (signals2, gint16, i); >+ >+ if (ABS (s1 - s2) > CACHE_ENTRY_MATCH_SIGNAL_WINDOW / 2) >+ return FALSE; >+ } >+ >+ return TRUE; >+} >+ > static GClueLocation * >-duplicate_location_new_timestamp (GClueLocation *location) >+find_cached_location (GHashTable *cache, GVariant *key, GArray *signals) > { >- return g_object_new (GCLUE_TYPE_LOCATION, >- "latitude", gclue_location_get_latitude (location), >- "longitude", gclue_location_get_longitude (location), >- "accuracy", gclue_location_get_accuracy (location), >- "altitude", gclue_location_get_altitude (location), >- "timestamp", 0, >- "speed", gclue_location_get_speed (location), >- "heading", gclue_location_get_heading (location), >- NULL); >+ g_autofree gchar *key_str = g_variant_print (key, FALSE); >+ GClueLocation *location = NULL; >+ LocationCacheValue *value; >+ GList *l; >+ >+ value = g_hash_table_lookup (cache, key); >+ if (!value) { >+ g_debug ("Cache miss for key %s", key_str); >+ return NULL; >+ } >+ >+ g_assert (value->elements); >+ for (l = value->elements; l; l = l->next) { >+ LocationCacheElement *element = l->data; >+ >+ if (location && >+ gclue_location_get_accuracy (element->location) >= >+ gclue_location_get_accuracy (location)) { >+ /* Have at least as accurate location already, >+ * don't bother with comparing signals. >+ */ >+ continue; >+ } >+ >+ if (!cached_signals_match (element->signals, signals)) >+ continue; >+ >+ location = element->location; >+ } >+ >+ if (location) { >+ g_debug ("Cache hit for key %s: got location %p (%s)", >+ key_str, location, >+ gclue_location_get_description (location)); >+ } else { >+ g_debug ("Cache had key %s, but with different signals", key_str); >+ } >+ >+ return location; >+} >+ >+typedef struct { >+ GVariant *cache_key; >+ GArray *signals; >+} RefreshTaskData; >+ >+static RefreshTaskData * >+refresh_task_data_new (GVariant *cache_key, >+ GArray *signals) >+{ >+ RefreshTaskData *tdata; >+ >+ tdata = g_slice_new (RefreshTaskData); >+ tdata->cache_key = g_variant_ref (cache_key); >+ tdata->signals = signals; >+ return tdata; >+} >+ >+static void refresh_task_data_free (gpointer data) >+{ >+ RefreshTaskData *rdata = data; >+ >+ g_clear_pointer (&rdata->cache_key, g_variant_unref); >+ if (rdata->signals) >+ g_array_free (rdata->signals, TRUE); >+ g_slice_free (RefreshTaskData, rdata); > } > > static void >@@ -1212,40 +1418,59 @@ > { > GClueWifi *wifi = GCLUE_WIFI (source); > g_autoptr(GTask) task = g_task_new (source, cancellable, callback, user_data); >- g_autoptr(GVariant) cache_key = get_location_cache_key (wifi); >- g_autofree gchar *cache_key_str = g_variant_print (cache_key, FALSE); >- GClueLocation *cached_location = g_hash_table_lookup (wifi->priv->location_cache, cache_key); >+ g_autoptr(GPtrArray) bss_array = get_location_cache_bss_array (wifi); >+ g_autoptr(GVariant) cache_key = get_location_cache_hashtable_key (wifi, bss_array); >+ g_autoptr(GArray) signal_array = get_location_cache_signal_array (wifi, bss_array); >+ GClueLocation *cached_location = find_cached_location (wifi->priv->location_cache, >+ cache_key, signal_array); >+ RefreshTaskData *tdata; > > g_task_set_source_tag (task, gclue_wifi_refresh_async); >- g_task_set_task_data (task, g_steal_pointer (&cache_key), (GDestroyNotify) g_variant_unref); > > if (gclue_location_source_get_active (GCLUE_LOCATION_SOURCE (source))) { > /* Try the cache. */ > if (cached_location != NULL) { > g_autoptr(GClueLocation) new_location = NULL; > >- g_debug ("Cache hit for key %s: got location %p (%s)", >- cache_key_str, cached_location, >- gclue_location_get_description (cached_location)); > wifi->priv->cache_hits++; > > /* Duplicate the location so its timestamp is updated. */ >- new_location = duplicate_location_new_timestamp (cached_location); >+ new_location = gclue_location_duplicate_fresh (cached_location); > gclue_location_source_set_location (GCLUE_LOCATION_SOURCE (source), new_location); > > g_task_return_pointer (task, g_steal_pointer (&new_location), g_object_unref); > return; > } > >- g_debug ("Cache miss for key %s; querying web service", cache_key_str); > wifi->priv->cache_misses++; > } > >+ tdata = refresh_task_data_new (cache_key, g_steal_pointer (&signal_array)); >+ g_task_set_task_data (task, tdata, refresh_task_data_free); >+ > /* Fall back to querying the web service. */ > GCLUE_WEB_SOURCE_CLASS (gclue_wifi_parent_class)->refresh_async (source, cancellable, refresh_cb, g_steal_pointer (&task)); > } > > static void >+add_cached_location (GHashTable *cache, >+ GVariant *key, GArray **signals, >+ GClueLocation *location) >+{ >+ LocationCacheValue *value; >+ LocationCacheElement *element; >+ >+ value = g_hash_table_lookup (cache, key); >+ if (!value) { >+ value = location_cache_value_new (); >+ g_hash_table_insert (cache, g_variant_ref (key), value); >+ } >+ >+ element = location_cache_element_new (g_steal_pointer (signals), location); >+ value->elements = g_list_prepend (value->elements, element); >+} >+ >+static void > refresh_cb (GObject *source_object, > GAsyncResult *result, > gpointer user_data) >@@ -1255,7 +1480,7 @@ > g_autoptr(GTask) task = g_steal_pointer (&user_data); > g_autoptr(GClueLocation) location = NULL; > g_autoptr(GError) local_error = NULL; >- GVariant *cache_key; >+ RefreshTaskData *tdata; > g_autofree gchar *cache_key_str = NULL; > double cache_hit_ratio; > >@@ -1268,9 +1493,11 @@ > } > > /* Cache the result. */ >- cache_key = g_task_get_task_data (task); >- cache_key_str = g_variant_print (cache_key, FALSE); >- g_hash_table_replace (wifi->priv->location_cache, g_variant_ref (cache_key), g_object_ref (location)); >+ tdata = g_task_get_task_data (task); >+ cache_key_str = g_variant_print (tdata->cache_key, FALSE); >+ add_cached_location (wifi->priv->location_cache, >+ tdata->cache_key, &tdata->signals, >+ location); > > if (wifi->priv->cache_hits || wifi->priv->cache_misses) { > double cache_attempts; >diff -Nurw geoclue-2.6.0/src/gclue-wifi.h geoclue-2.6.0-soup-3.2/src/gclue-wifi.h >--- geoclue-2.6.0/src/gclue-wifi.h 2022-02-10 21:03:30.000000000 +0300 >+++ geoclue-2.6.0-soup-3.2/src/gclue-wifi.h 2022-11-10 23:30:31.822463754 +0300 >@@ -63,7 +63,8 @@ > }; > > GClueWifi * gclue_wifi_get_singleton (GClueAccuracyLevel level); >-GClueAccuracyLevel gclue_wifi_get_accuracy_level (GClueWifi *wifi); >+gboolean gclue_wifi_should_skip_bsss (GClueAccuracyLevel level); >+GList *gclue_wifi_get_bss_list (GClueWifi *wifi); > > G_END_DECLS > >diff -Nurw geoclue-2.6.0/src/meson.build geoclue-2.6.0-soup-3.2/src/meson.build >--- geoclue-2.6.0/src/meson.build 2022-02-10 21:03:30.000000000 +0300 >+++ geoclue-2.6.0-soup-3.2/src/meson.build 2022-11-10 23:30:31.822463754 +0300 >@@ -1,5 +1,5 @@ > geoclue_deps = base_deps + [ dependency('json-glib-1.0', version: '>= 0.14.0'), >- dependency('libsoup-2.4', version: '>= 2.42.0') ] >+ dependency('libsoup-3.0', version: '>= 3.0.0') ] > > sources = [ libgeoclue_public_api_gen_sources[1], > geoclue_iface_sources, >@@ -28,7 +28,8 @@ > 'gclue-wifi.h', 'gclue-wifi.c', > 'gclue-mozilla.h', 'gclue-mozilla.c', > 'gclue-min-uint.h', 'gclue-min-uint.c', >- 'gclue-location.h', 'gclue-location.c' ] >+ 'gclue-location.h', 'gclue-location.c', >+ 'gclue-utils.h' ] > > if get_option('3g-source') or get_option('cdma-source') or get_option('modem-gps-source') > geoclue_deps += [ dependency('mm-glib', version: '>= 1.10') ]
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 880847
:
830685
| 830687