Blame src/gclue-mozilla.c

Packit Service b2c451
/* vim: set et ts=8 sw=8: */
Packit Service b2c451
/*
Packit Service b2c451
 * Copyright 2014 Red Hat, Inc.
Packit Service b2c451
 *
Packit Service b2c451
 * Geoclue is free software; you can redistribute it and/or modify it under
Packit Service b2c451
 * the terms of the GNU General Public License as published by the Free
Packit Service b2c451
 * Software Foundation; either version 2 of the License, or (at your option)
Packit Service b2c451
 * any later version.
Packit Service b2c451
 *
Packit Service b2c451
 * Geoclue is distributed in the hope that it will be useful, but WITHOUT ANY
Packit Service b2c451
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
Packit Service b2c451
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
Packit Service b2c451
 * details.
Packit Service b2c451
 *
Packit Service b2c451
 * You should have received a copy of the GNU General Public License along
Packit Service b2c451
 * with Geoclue; if not, write to the Free Software Foundation, Inc.,
Packit Service b2c451
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
Packit Service b2c451
 *
Packit Service b2c451
 * Authors: Zeeshan Ali (Khattak) <zeeshanak@gnome.org>
Packit Service b2c451
 */
Packit Service b2c451
Packit Service b2c451
#include <stdlib.h>
Packit Service b2c451
#include <glib.h>
Packit Service b2c451
#include <json-glib/json-glib.h>
Packit Service b2c451
#include <string.h>
Packit Service b2c451
#include <config.h>
Packit Service b2c451
#include "gclue-mozilla.h"
Packit Service b2c451
#include "gclue-config.h"
Packit Service b2c451
#include "gclue-error.h"
Packit Service b2c451
Packit Service b2c451
/**
Packit Service b2c451
 * SECTION:gclue-mozilla
Packit Service b2c451
 * @short_description: Helpers to create queries for and parse response of,
Packit Service b2c451
 * Mozilla Location Service.
Packit Service b2c451
 *
Packit Service b2c451
 * Contains API to get the geolocation based on IP address, nearby WiFi networks
Packit Service b2c451
 * and 3GPP cell tower info. It uses
Packit Service b2c451
 * <ulink url="https://wiki.mozilla.org/CloudServices/Location">Mozilla Location
Packit Service b2c451
 * Service</ulink> to achieve that. The URL is kept in our configuration file so
Packit Service b2c451
 * its easy to switch to Google's API.
Packit Service b2c451
 **/
Packit Service b2c451
Packit Service b2c451
#define BSSID_LEN 7
Packit Service b2c451
#define BSSID_STR_LEN 18
Packit Service b2c451
#define MAX_SSID_LEN 32
Packit Service b2c451
Packit Service b2c451
static guint
Packit Service b2c451
variant_to_string (GVariant *variant, guint max_len, char *ret)
Packit Service b2c451
{
Packit Service b2c451
        guint i;
Packit Service b2c451
        guint len;
Packit Service b2c451
Packit Service b2c451
        len = g_variant_n_children (variant);
Packit Service b2c451
        if (len == 0)
Packit Service b2c451
                return 0;
Packit Service b2c451
        g_return_val_if_fail(len < max_len, 0);
Packit Service b2c451
        ret[len] = '\0';
Packit Service b2c451
Packit Service b2c451
        for (i = 0; i < len; i++)
Packit Service b2c451
                g_variant_get_child (variant,
Packit Service b2c451
                                     i,
Packit Service b2c451
                                     "y",
Packit Service b2c451
                                     &ret[i]);
Packit Service b2c451
Packit Service b2c451
        return len;
Packit Service b2c451
}
Packit Service b2c451
Packit Service b2c451
static guint
Packit Service b2c451
get_ssid_from_bss (WPABSS *bss, char *ssid)
Packit Service b2c451
{
Packit Service b2c451
        GVariant *variant = wpa_bss_get_ssid (bss);
Packit Service b2c451
Packit Service b2c451
        return variant_to_string (variant, MAX_SSID_LEN, ssid);
Packit Service b2c451
}
Packit Service b2c451
Packit Service b2c451
static gboolean
Packit Service b2c451
get_bssid_from_bss (WPABSS *bss, char *bssid)
Packit Service b2c451
{
Packit Service b2c451
        GVariant *variant;
Packit Service b2c451
        char raw_bssid[BSSID_LEN] = { 0 };
Packit Service b2c451
        guint raw_len, i;
Packit Service b2c451
Packit Service b2c451
        variant = wpa_bss_get_bssid (bss);
Packit Service b2c451
        if (variant == NULL)
Packit Service b2c451
                return FALSE;
Packit Service b2c451
Packit Service b2c451
        raw_len = variant_to_string (variant, BSSID_LEN, raw_bssid);
Packit Service b2c451
        g_return_val_if_fail (raw_len == BSSID_LEN - 1, FALSE);
Packit Service b2c451
Packit Service b2c451
        for (i = 0; i < BSSID_LEN - 1; i++) {
Packit Service b2c451
                unsigned char c = (unsigned char) raw_bssid[i];
Packit Service b2c451
Packit Service b2c451
                if (i == BSSID_LEN - 2) {
Packit Service b2c451
                        g_snprintf (bssid + (i * 3), 3, "%02x", c);
Packit Service b2c451
                } else {
Packit Service b2c451
                        g_snprintf (bssid + (i * 3), 4, "%02x:", c);
Packit Service b2c451
                }
Packit Service b2c451
        }
Packit Service b2c451
Packit Service b2c451
        return TRUE;
Packit Service b2c451
}
Packit Service b2c451
Packit Service b2c451
static const char *
Packit Service b2c451
get_url (void)
Packit Service b2c451
{
Packit Service b2c451
        GClueConfig *config;
Packit Service b2c451
Packit Service b2c451
        config = gclue_config_get_singleton ();
Packit Service b2c451
Packit Service b2c451
        return gclue_config_get_wifi_url (config);
Packit Service b2c451
}
Packit Service b2c451
Packit Service b2c451
SoupMessage *
Packit Service b2c451
gclue_mozilla_create_query (GList        *bss_list, /* As in Access Points */
Packit Service b2c451
                            GClue3GTower *tower,
Packit Service b2c451
                            GError      **error)
Packit Service b2c451
{
Packit Service b2c451
        SoupMessage *ret = NULL;
Packit Service b2c451
        JsonBuilder *builder;
Packit Service b2c451
        JsonGenerator *generator;
Packit Service b2c451
        JsonNode *root_node;
Packit Service b2c451
        char *data;
Packit Service b2c451
        gsize data_len;
Packit Service b2c451
        const char *uri;
Packit Service b2c451
Packit Service b2c451
        builder = json_builder_new ();
Packit Service b2c451
        json_builder_begin_object (builder);
Packit Service b2c451
Packit Service b2c451
        /* We send pure geoip query using empty object if both bss_list and
Packit Service b2c451
         * tower are NULL.
Packit Service b2c451
         */
Packit Service b2c451
Packit Service b2c451
        if (tower != NULL) {
Packit Service b2c451
                json_builder_set_member_name (builder, "radioType");
Packit Service b2c451
                json_builder_add_string_value (builder, "gsm");
Packit Service b2c451
Packit Service b2c451
                json_builder_set_member_name (builder, "cellTowers");
Packit Service b2c451
                json_builder_begin_array (builder);
Packit Service b2c451
Packit Service b2c451
                json_builder_begin_object (builder);
Packit Service b2c451
Packit Service b2c451
                json_builder_set_member_name (builder, "cellId");
Packit Service b2c451
                json_builder_add_int_value (builder, tower->cell_id);
Packit Service b2c451
                json_builder_set_member_name (builder, "mobileCountryCode");
Packit Service b2c451
                json_builder_add_int_value (builder, tower->mcc);
Packit Service b2c451
                json_builder_set_member_name (builder, "mobileNetworkCode");
Packit Service b2c451
                json_builder_add_int_value (builder, tower->mnc);
Packit Service b2c451
                json_builder_set_member_name (builder, "locationAreaCode");
Packit Service b2c451
                json_builder_add_int_value (builder, tower->lac);
Packit Service b2c451
Packit Service b2c451
                json_builder_end_object (builder);
Packit Service b2c451
Packit Service b2c451
                json_builder_end_array (builder);
Packit Service b2c451
        }
Packit Service b2c451
Packit Service b2c451
        if (bss_list != NULL) {
Packit Service b2c451
                GList *iter;
Packit Service b2c451
Packit Service b2c451
                json_builder_set_member_name (builder, "wifiAccessPoints");
Packit Service b2c451
                json_builder_begin_array (builder);
Packit Service b2c451
Packit Service b2c451
                for (iter = bss_list; iter != NULL; iter = iter->next) {
Packit Service b2c451
                        WPABSS *bss = WPA_BSS (iter->data);
Packit Service b2c451
                        char mac[BSSID_STR_LEN] = { 0 };
Packit Service b2c451
                        gint16 strength_dbm;
Packit Service b2c451
Packit Service b2c451
                        if (gclue_mozilla_should_ignore_bss (bss))
Packit Service b2c451
                                continue;
Packit Service b2c451
Packit Service b2c451
                        json_builder_begin_object (builder);
Packit Service b2c451
                        json_builder_set_member_name (builder, "macAddress");
Packit Service b2c451
                        get_bssid_from_bss (bss, mac);
Packit Service b2c451
                        json_builder_add_string_value (builder, mac);
Packit Service b2c451
Packit Service b2c451
                        json_builder_set_member_name (builder, "signalStrength");
Packit Service b2c451
                        strength_dbm = wpa_bss_get_signal (bss);
Packit Service b2c451
                        json_builder_add_int_value (builder, strength_dbm);
Packit Service b2c451
                        json_builder_end_object (builder);
Packit Service b2c451
                }
Packit Service b2c451
                json_builder_end_array (builder);
Packit Service b2c451
        }
Packit Service b2c451
        json_builder_end_object (builder);
Packit Service b2c451
Packit Service b2c451
        generator = json_generator_new ();
Packit Service b2c451
        root_node = json_builder_get_root (builder);
Packit Service b2c451
        json_generator_set_root (generator, root_node);
Packit Service b2c451
        data = json_generator_to_data (generator, &data_len);
Packit Service b2c451
Packit Service b2c451
        json_node_free (root_node);
Packit Service b2c451
        g_object_unref (builder);
Packit Service b2c451
        g_object_unref (generator);
Packit Service b2c451
Packit Service b2c451
        uri = get_url ();
Packit Service b2c451
        ret = soup_message_new ("POST", uri);
Packit Service b2c451
        soup_message_set_request (ret,
Packit Service b2c451
                                  "application/json",
Packit Service b2c451
                                  SOUP_MEMORY_TAKE,
Packit Service b2c451
                                  data,
Packit Service b2c451
                                  data_len);
Packit Service b2c451
        g_debug ("Sending following request to '%s':\n%s", uri, data);
Packit Service b2c451
Packit Service b2c451
        return ret;
Packit Service b2c451
}
Packit Service b2c451
Packit Service b2c451
static gboolean
Packit Service b2c451
parse_server_error (JsonObject *object, GError **error)
Packit Service b2c451
{
Packit Service b2c451
        JsonObject *error_obj;
Packit Service b2c451
        int code;
Packit Service b2c451
        const char *message;
Packit Service b2c451
Packit Service b2c451
        if (!json_object_has_member (object, "error"))
Packit Service b2c451
            return FALSE;
Packit Service b2c451
Packit Service b2c451
        error_obj = json_object_get_object_member (object, "error");
Packit Service b2c451
        code = json_object_get_int_member (error_obj, "code");
Packit Service b2c451
        message = json_object_get_string_member (error_obj, "message");
Packit Service b2c451
Packit Service b2c451
        g_set_error_literal (error, G_IO_ERROR, code, message);
Packit Service b2c451
Packit Service b2c451
        return TRUE;
Packit Service b2c451
}
Packit Service b2c451
Packit Service b2c451
GClueLocation *
Packit Service b2c451
gclue_mozilla_parse_response (const char *json,
Packit Service b2c451
                              GError    **error)
Packit Service b2c451
{
Packit Service b2c451
        JsonParser *parser;
Packit Service b2c451
        JsonNode *node;
Packit Service b2c451
        JsonObject *object, *loc_object;
Packit Service b2c451
        GClueLocation *location;
Packit Service b2c451
        gdouble latitude, longitude, accuracy;
Packit Service b2c451
Packit Service b2c451
        parser = json_parser_new ();
Packit Service b2c451
Packit Service b2c451
        if (!json_parser_load_from_data (parser, json, -1, error))
Packit Service b2c451
                return NULL;
Packit Service b2c451
Packit Service b2c451
        node = json_parser_get_root (parser);
Packit Service b2c451
        object = json_node_get_object (node);
Packit Service b2c451
Packit Service b2c451
        if (parse_server_error (object, error))
Packit Service b2c451
                return NULL;
Packit Service b2c451
Packit Service b2c451
        loc_object = json_object_get_object_member (object, "location");
Packit Service b2c451
        latitude = json_object_get_double_member (loc_object, "lat");
Packit Service b2c451
        longitude = json_object_get_double_member (loc_object, "lng");
Packit Service b2c451
Packit Service b2c451
        accuracy = json_object_get_double_member (object, "accuracy");
Packit Service b2c451
Packit Service b2c451
        location = gclue_location_new (latitude, longitude, accuracy);
Packit Service b2c451
Packit Service b2c451
        g_object_unref (parser);
Packit Service b2c451
Packit Service b2c451
        return location;
Packit Service b2c451
}
Packit Service b2c451
Packit Service b2c451
static const char *
Packit Service b2c451
get_submit_config (const char **nick)
Packit Service b2c451
{
Packit Service b2c451
        GClueConfig *config;
Packit Service b2c451
Packit Service b2c451
        config = gclue_config_get_singleton ();
Packit Service b2c451
        if (!gclue_config_get_wifi_submit_data (config))
Packit Service b2c451
                return NULL;
Packit Service b2c451
Packit Service b2c451
        *nick = gclue_config_get_wifi_submit_nick (config);
Packit Service b2c451
Packit Service b2c451
        return gclue_config_get_wifi_submit_url (config);
Packit Service b2c451
}
Packit Service b2c451
Packit Service b2c451
SoupMessage *
Packit Service b2c451
gclue_mozilla_create_submit_query (GClueLocation   *location,
Packit Service b2c451
                                   GList           *bss_list, /* As in Access Points */
Packit Service b2c451
                                   GClue3GTower    *tower,
Packit Service b2c451
                                   GError         **error)
Packit Service b2c451
{
Packit Service b2c451
        SoupMessage *ret = NULL;
Packit Service b2c451
        JsonBuilder *builder;
Packit Service b2c451
        JsonGenerator *generator;
Packit Service b2c451
        JsonNode *root_node;
Packit Service b2c451
        char *data, *timestamp;
Packit Service b2c451
        const char *url, *nick;
Packit Service b2c451
        gsize data_len;
Packit Service b2c451
        GList *iter;
Packit Service b2c451
        gdouble lat, lon, accuracy, altitude;
Packit Service b2c451
        GTimeVal tv;
Packit Service b2c451
Packit Service b2c451
        url = get_submit_config (&nick);
Packit Service b2c451
        if (url == NULL)
Packit Service b2c451
                goto out;
Packit Service b2c451
Packit Service b2c451
        builder = json_builder_new ();
Packit Service b2c451
        json_builder_begin_object (builder);
Packit Service b2c451
Packit Service b2c451
        json_builder_set_member_name (builder, "items");
Packit Service b2c451
        json_builder_begin_array (builder);
Packit Service b2c451
Packit Service b2c451
        json_builder_begin_object (builder);
Packit Service b2c451
Packit Service b2c451
        lat = gclue_location_get_latitude (location);
Packit Service b2c451
        json_builder_set_member_name (builder, "lat");
Packit Service b2c451
        json_builder_add_double_value (builder, lat);
Packit Service b2c451
Packit Service b2c451
        lon = gclue_location_get_longitude (location);
Packit Service b2c451
        json_builder_set_member_name (builder, "lon");
Packit Service b2c451
        json_builder_add_double_value (builder, lon);
Packit Service b2c451
Packit Service b2c451
        accuracy = gclue_location_get_accuracy (location);
Packit Service b2c451
        if (accuracy != GCLUE_LOCATION_ACCURACY_UNKNOWN) {
Packit Service b2c451
                json_builder_set_member_name (builder, "accuracy");
Packit Service b2c451
                json_builder_add_double_value (builder, accuracy);
Packit Service b2c451
        }
Packit Service b2c451
Packit Service b2c451
        altitude = gclue_location_get_altitude (location);
Packit Service b2c451
        if (altitude != GCLUE_LOCATION_ALTITUDE_UNKNOWN) {
Packit Service b2c451
                json_builder_set_member_name (builder, "altitude");
Packit Service b2c451
                json_builder_add_double_value (builder, altitude);
Packit Service b2c451
        }
Packit Service b2c451
Packit Service b2c451
        tv.tv_sec = gclue_location_get_timestamp (location);
Packit Service b2c451
        tv.tv_usec = 0;
Packit Service b2c451
        timestamp = g_time_val_to_iso8601 (&tv;;
Packit Service b2c451
        json_builder_set_member_name (builder, "time");
Packit Service b2c451
        json_builder_add_string_value (builder, timestamp);
Packit Service b2c451
        g_free (timestamp);
Packit Service b2c451
Packit Service b2c451
        json_builder_set_member_name (builder, "radioType");
Packit Service b2c451
        json_builder_add_string_value (builder, "gsm");
Packit Service b2c451
Packit Service b2c451
        if (bss_list != NULL) {
Packit Service b2c451
                json_builder_set_member_name (builder, "wifi");
Packit Service b2c451
                json_builder_begin_array (builder);
Packit Service b2c451
Packit Service b2c451
                for (iter = bss_list; iter != NULL; iter = iter->next) {
Packit Service b2c451
                        WPABSS *bss = WPA_BSS (iter->data);
Packit Service b2c451
                        char mac[BSSID_STR_LEN] = { 0 };
Packit Service b2c451
                        gint16 strength_dbm;
Packit Service b2c451
                        guint16 frequency;
Packit Service b2c451
Packit Service b2c451
                        if (gclue_mozilla_should_ignore_bss (bss))
Packit Service b2c451
                                continue;
Packit Service b2c451
Packit Service b2c451
                        json_builder_begin_object (builder);
Packit Service b2c451
                        json_builder_set_member_name (builder, "key");
Packit Service b2c451
                        get_bssid_from_bss (bss, mac);
Packit Service b2c451
                        json_builder_add_string_value (builder, mac);
Packit Service b2c451
Packit Service b2c451
                        json_builder_set_member_name (builder, "signal");
Packit Service b2c451
                        strength_dbm = wpa_bss_get_signal (bss);
Packit Service b2c451
                        json_builder_add_int_value (builder, strength_dbm);
Packit Service b2c451
Packit Service b2c451
                        json_builder_set_member_name (builder, "frequency");
Packit Service b2c451
                        frequency = wpa_bss_get_frequency (bss);
Packit Service b2c451
                        json_builder_add_int_value (builder, frequency);
Packit Service b2c451
                        json_builder_end_object (builder);
Packit Service b2c451
                }
Packit Service b2c451
Packit Service b2c451
                json_builder_end_array (builder); /* wifi */
Packit Service b2c451
        }
Packit Service b2c451
Packit Service b2c451
        if (tower != NULL) {
Packit Service b2c451
                json_builder_set_member_name (builder, "cell");
Packit Service b2c451
                json_builder_begin_array (builder);
Packit Service b2c451
Packit Service b2c451
                json_builder_begin_object (builder);
Packit Service b2c451
Packit Service b2c451
                json_builder_set_member_name (builder, "radio");
Packit Service b2c451
                json_builder_add_string_value (builder, "gsm");
Packit Service b2c451
                json_builder_set_member_name (builder, "cid");
Packit Service b2c451
                json_builder_add_int_value (builder, tower->cell_id);
Packit Service b2c451
                json_builder_set_member_name (builder, "mcc");
Packit Service b2c451
                json_builder_add_int_value (builder, tower->mcc);
Packit Service b2c451
                json_builder_set_member_name (builder, "mnc");
Packit Service b2c451
                json_builder_add_int_value (builder, tower->mnc);
Packit Service b2c451
                json_builder_set_member_name (builder, "lac");
Packit Service b2c451
                json_builder_add_int_value (builder, tower->lac);
Packit Service b2c451
Packit Service b2c451
                json_builder_end_object (builder);
Packit Service b2c451
Packit Service b2c451
                json_builder_end_array (builder); /* cell */
Packit Service b2c451
        }
Packit Service b2c451
Packit Service b2c451
        json_builder_end_object (builder);
Packit Service b2c451
        json_builder_end_array (builder); /* items */
Packit Service b2c451
        json_builder_end_object (builder);
Packit Service b2c451
Packit Service b2c451
        generator = json_generator_new ();
Packit Service b2c451
        root_node = json_builder_get_root (builder);
Packit Service b2c451
        json_generator_set_root (generator, root_node);
Packit Service b2c451
        data = json_generator_to_data (generator, &data_len);
Packit Service b2c451
Packit Service b2c451
        json_node_free (root_node);
Packit Service b2c451
        g_object_unref (builder);
Packit Service b2c451
        g_object_unref (generator);
Packit Service b2c451
Packit Service b2c451
        ret = soup_message_new ("POST", url);
Packit Service b2c451
        if (nick != NULL && nick[0] != '\0')
Packit Service b2c451
                soup_message_headers_append (ret->request_headers,
Packit Service b2c451
                                             "X-Nickname",
Packit Service b2c451
                                             nick);
Packit Service b2c451
        soup_message_set_request (ret,
Packit Service b2c451
                                  "application/json",
Packit Service b2c451
                                  SOUP_MEMORY_TAKE,
Packit Service b2c451
                                  data,
Packit Service b2c451
                                  data_len);
Packit Service b2c451
        g_debug ("Sending following request to '%s':\n%s", url, data);
Packit Service b2c451
Packit Service b2c451
out:
Packit Service b2c451
        return ret;
Packit Service b2c451
}
Packit Service b2c451
Packit Service b2c451
gboolean
Packit Service b2c451
gclue_mozilla_should_ignore_bss (WPABSS *bss)
Packit Service b2c451
{
Packit Service b2c451
        char ssid[MAX_SSID_LEN] = { 0 };
Packit Service b2c451
        char bssid[BSSID_STR_LEN] = { 0 };
Packit Service b2c451
        guint len;
Packit Service b2c451
Packit Service b2c451
        if (!get_bssid_from_bss (bss, bssid)) {
Packit Service b2c451
                g_debug ("Ignoring WiFi AP with unknown BSSID..");
Packit Service b2c451
                return TRUE;
Packit Service b2c451
        }
Packit Service b2c451
Packit Service b2c451
        len = get_ssid_from_bss (bss, ssid);
Packit Service b2c451
        if (len == 0 || g_str_has_suffix (ssid, "_nomap")) {
Packit Service b2c451
                g_debug ("SSID for WiFi AP '%s' missing or has '_nomap' suffix."
Packit Service b2c451
                         ", Ignoring..",
Packit Service b2c451
                         bssid);
Packit Service b2c451
                return TRUE;
Packit Service b2c451
        }
Packit Service b2c451
Packit Service b2c451
        return FALSE;
Packit Service b2c451
}