Blame lib/pin.c

Packit 8fb625
/*
Packit 8fb625
 *
Packit 8fb625
 *  BlueZ - Bluetooth protocol stack for Linux
Packit 8fb625
 *
Packit 8fb625
 *  Copyright (C) 2009  Bastien Nocera <hadess@hadess.net>
Packit 8fb625
 *
Packit 8fb625
 *
Packit 8fb625
 *  This program is free software; you can redistribute it and/or modify
Packit 8fb625
 *  it under the terms of the GNU General Public License as published by
Packit 8fb625
 *  the Free Software Foundation; either version 2 of the License, or
Packit 8fb625
 *  (at your option) any later version.
Packit 8fb625
 *
Packit 8fb625
 *  This program is distributed in the hope that it will be useful,
Packit 8fb625
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 8fb625
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 8fb625
 *  GNU General Public License for more details.
Packit 8fb625
 *
Packit 8fb625
 *  You should have received a copy of the GNU General Public License
Packit 8fb625
 *  along with this program; if not, write to the Free Software
Packit 8fb625
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
Packit 8fb625
 *
Packit 8fb625
 */
Packit 8fb625
Packit 8fb625
#ifdef HAVE_CONFIG_H
Packit 8fb625
#include <config.h>
Packit 8fb625
#endif
Packit 8fb625
Packit 8fb625
#include <stdlib.h>
Packit 8fb625
#include <string.h>
Packit 8fb625
#include <glib.h>
Packit 8fb625
#include <libudev.h>
Packit 8fb625
#include <bluetooth-enums.h>
Packit 8fb625
#include <bluetooth-utils.h>
Packit 8fb625
Packit 8fb625
#include "pin.h"
Packit 8fb625
Packit 8fb625
#define PIN_CODE_DB "pin-code-database.xml"
Packit 8fb625
#define MAX_DIGITS_PIN_PREFIX "max:"
Packit 8fb625
Packit 8fb625
char *
Packit 8fb625
oui_to_vendor (const char *oui)
Packit 8fb625
{
Packit 8fb625
	struct udev *udev = NULL;
Packit 8fb625
	struct udev_hwdb *hwdb = NULL;
Packit 8fb625
	struct udev_list_entry *list, *l;
Packit 8fb625
	char *modalias = NULL;
Packit 8fb625
	char *vendor = NULL;
Packit 8fb625
Packit 8fb625
	if (oui == NULL ||
Packit 8fb625
	    strlen (oui) < 8)
Packit 8fb625
		return NULL;
Packit 8fb625
Packit 8fb625
	udev = udev_new ();
Packit 8fb625
	if (udev == NULL)
Packit 8fb625
		goto bail;
Packit 8fb625
Packit 8fb625
	hwdb = udev_hwdb_new (udev);
Packit 8fb625
	if (hwdb == NULL)
Packit 8fb625
		goto bail;
Packit 8fb625
Packit 8fb625
	modalias = g_strdup_printf ("OUI:%c%c%c%c%c%c",
Packit 8fb625
				    g_ascii_toupper (oui[0]),
Packit 8fb625
				    g_ascii_toupper (oui[1]),
Packit 8fb625
				    g_ascii_toupper (oui[3]),
Packit 8fb625
				    g_ascii_toupper (oui[4]),
Packit 8fb625
				    g_ascii_toupper (oui[6]),
Packit 8fb625
				    g_ascii_toupper (oui[7]));
Packit 8fb625
Packit 8fb625
	list = udev_hwdb_get_properties_list_entry (hwdb, modalias, 0);
Packit 8fb625
Packit 8fb625
	udev_list_entry_foreach (l, list) {
Packit 8fb625
		const char *name = udev_list_entry_get_name (l);
Packit 8fb625
Packit 8fb625
		if (g_strcmp0 (name, "ID_OUI_FROM_DATABASE") == 0) {
Packit 8fb625
			vendor = g_strdup (udev_list_entry_get_value (l));
Packit 8fb625
			break;
Packit 8fb625
		}
Packit 8fb625
	}
Packit 8fb625
Packit 8fb625
bail:
Packit 8fb625
	g_clear_pointer (&modalias, g_free);
Packit 8fb625
	g_clear_pointer (&hwdb, udev_hwdb_unref);
Packit 8fb625
	g_clear_pointer (&udev, udev_unref);
Packit 8fb625
Packit 8fb625
	return vendor;
Packit 8fb625
}
Packit 8fb625
Packit 8fb625
#define TYPE_IS(x, r) {				\
Packit 8fb625
	if (g_str_equal(type, x)) return r;	\
Packit 8fb625
}
Packit 8fb625
Packit 8fb625
static guint string_to_type(const char *type)
Packit 8fb625
{
Packit 8fb625
	TYPE_IS ("any", BLUETOOTH_TYPE_ANY);
Packit 8fb625
	TYPE_IS ("mouse", BLUETOOTH_TYPE_MOUSE);
Packit 8fb625
	TYPE_IS ("tablet", BLUETOOTH_TYPE_TABLET);
Packit 8fb625
	TYPE_IS ("keyboard", BLUETOOTH_TYPE_KEYBOARD);
Packit 8fb625
	TYPE_IS ("headset", BLUETOOTH_TYPE_HEADSET);
Packit 8fb625
	TYPE_IS ("headphones", BLUETOOTH_TYPE_HEADPHONES);
Packit 8fb625
	TYPE_IS ("audio", BLUETOOTH_TYPE_OTHER_AUDIO);
Packit 8fb625
	TYPE_IS ("printer", BLUETOOTH_TYPE_PRINTER);
Packit 8fb625
	TYPE_IS ("network", BLUETOOTH_TYPE_NETWORK);
Packit 8fb625
	TYPE_IS ("joypad", BLUETOOTH_TYPE_JOYPAD);
Packit 8fb625
Packit 8fb625
	g_warning ("unhandled type '%s'", type);
Packit 8fb625
	return BLUETOOTH_TYPE_ANY;
Packit 8fb625
}
Packit 8fb625
Packit 8fb625
typedef struct {
Packit 8fb625
	char *ret_pin;
Packit 8fb625
	guint max_digits;
Packit 8fb625
	guint type;
Packit 8fb625
	const char *address;
Packit 8fb625
	const char *name;
Packit 8fb625
	char *vendor;
Packit 8fb625
	gboolean confirm;
Packit 8fb625
} PinParseData;
Packit 8fb625
Packit 8fb625
static void
Packit 8fb625
pin_db_parse_start_tag (GMarkupParseContext *ctx,
Packit 8fb625
			const gchar         *element_name,
Packit 8fb625
			const gchar        **attr_names,
Packit 8fb625
			const gchar        **attr_values,
Packit 8fb625
			gpointer             data,
Packit 8fb625
			GError             **error)
Packit 8fb625
{
Packit 8fb625
	PinParseData *pdata = (PinParseData *) data;
Packit 8fb625
Packit 8fb625
	if (pdata->ret_pin != NULL || pdata->max_digits != 0)
Packit 8fb625
		return;
Packit 8fb625
	if (g_str_equal (element_name, "device") == FALSE)
Packit 8fb625
		return;
Packit 8fb625
Packit 8fb625
	while (*attr_names && *attr_values) {
Packit 8fb625
		if (g_str_equal (*attr_names, "type")) {
Packit 8fb625
			guint type;
Packit 8fb625
Packit 8fb625
			type = string_to_type (*attr_values);
Packit 8fb625
			if (type != BLUETOOTH_TYPE_ANY && type != pdata->type)
Packit 8fb625
				return;
Packit 8fb625
		} else if (g_str_equal (*attr_names, "oui")) {
Packit 8fb625
			if (g_str_has_prefix (pdata->address, *attr_values) == FALSE)
Packit 8fb625
				return;
Packit 8fb625
		} else if (g_str_equal (*attr_names, "vendor")) {
Packit 8fb625
			if (*attr_values == NULL || pdata->vendor == NULL)
Packit 8fb625
				return;
Packit 8fb625
			if (strstr (pdata->vendor, *attr_values) == NULL)
Packit 8fb625
				return;
Packit 8fb625
		} else if (g_str_equal (*attr_names, "name")) {
Packit 8fb625
			if (*attr_values == NULL || pdata->name == NULL)
Packit 8fb625
				return;
Packit 8fb625
			if (strstr (pdata->name, *attr_values) == NULL)
Packit 8fb625
				return;
Packit 8fb625
			pdata->confirm = FALSE;
Packit 8fb625
		} else if (g_str_equal (*attr_names, "pin")) {
Packit 8fb625
			if (g_str_has_prefix (*attr_values, MAX_DIGITS_PIN_PREFIX) != FALSE) {
Packit 8fb625
				pdata->max_digits = strtoul (*attr_values + strlen (MAX_DIGITS_PIN_PREFIX), NULL, 0);
Packit 8fb625
				g_assert (pdata->max_digits > 0 && pdata->max_digits < PIN_NUM_DIGITS);
Packit 8fb625
			} else {
Packit 8fb625
				pdata->ret_pin = g_strdup (*attr_values);
Packit 8fb625
			}
Packit 8fb625
			return;
Packit 8fb625
		}
Packit 8fb625
Packit 8fb625
		++attr_names;
Packit 8fb625
		++attr_values;
Packit 8fb625
	}
Packit 8fb625
}
Packit 8fb625
Packit 8fb625
char *
Packit 8fb625
get_pincode_for_device (guint       type,
Packit 8fb625
			const char *address,
Packit 8fb625
			const char *name,
Packit 8fb625
			guint      *max_digits,
Packit 8fb625
			gboolean   *confirm)
Packit 8fb625
{
Packit 8fb625
	GMarkupParseContext *ctx;
Packit 8fb625
	GMarkupParser parser = { pin_db_parse_start_tag, NULL, NULL, NULL, NULL };
Packit 8fb625
	PinParseData *data;
Packit 8fb625
	char *buf;
Packit 8fb625
	gsize buf_len;
Packit 8fb625
	GError *err = NULL;
Packit 8fb625
	char *tmp_vendor, *ret_pin;
Packit 8fb625
Packit 8fb625
	g_return_val_if_fail (address != NULL, NULL);
Packit 8fb625
Packit 8fb625
	g_debug ("Getting pincode for device '%s' (type: %s address: %s)",
Packit 8fb625
		 name ? name : "", bluetooth_type_to_string (type), address);
Packit 8fb625
Packit 8fb625
	/* Load the PIN database and split it in lines */
Packit 8fb625
	if (!g_file_get_contents(PIN_CODE_DB, &buf, &buf_len, NULL)) {
Packit 8fb625
		char *filename;
Packit 8fb625
Packit 8fb625
		filename = g_build_filename(PKGDATADIR, PIN_CODE_DB, NULL);
Packit 8fb625
		if (!g_file_get_contents(filename, &buf, &buf_len, NULL)) {
Packit 8fb625
			g_warning("Could not load "PIN_CODE_DB);
Packit 8fb625
			g_free (filename);
Packit 8fb625
			return NULL;
Packit 8fb625
		}
Packit 8fb625
		g_free (filename);
Packit 8fb625
	}
Packit 8fb625
Packit 8fb625
	data = g_new0 (PinParseData, 1);
Packit 8fb625
	data->type = type;
Packit 8fb625
	data->address = address;
Packit 8fb625
	data->name = name;
Packit 8fb625
	data->confirm = TRUE;
Packit 8fb625
Packit 8fb625
	tmp_vendor = oui_to_vendor (address);
Packit 8fb625
	if (tmp_vendor)
Packit 8fb625
		data->vendor = g_ascii_strdown (tmp_vendor, -1);
Packit 8fb625
	g_free (tmp_vendor);
Packit 8fb625
Packit 8fb625
	ctx = g_markup_parse_context_new (&parser, 0, data, NULL);
Packit 8fb625
Packit 8fb625
	if (!g_markup_parse_context_parse (ctx, buf, buf_len, &err)) {
Packit 8fb625
		g_warning ("Failed to parse '%s': %s\n", PIN_CODE_DB, err->message);
Packit 8fb625
		g_error_free (err);
Packit 8fb625
	}
Packit 8fb625
Packit 8fb625
	g_markup_parse_context_free (ctx);
Packit 8fb625
	g_free (buf);
Packit 8fb625
Packit 8fb625
	if (max_digits != NULL)
Packit 8fb625
		*max_digits = data->max_digits;
Packit 8fb625
	if (confirm != NULL)
Packit 8fb625
		*confirm = data->confirm;
Packit 8fb625
Packit 8fb625
	g_debug ("Got pin '%s' (max digits: %d, confirm: %d) for device '%s' (type: %s address: %s, vendor: %s)",
Packit 8fb625
		 data->ret_pin, data->max_digits, data->confirm,
Packit 8fb625
		 name ? name : "", bluetooth_type_to_string (type), address, data->vendor);
Packit 8fb625
Packit 8fb625
	g_free (data->vendor);
Packit 8fb625
	ret_pin = data->ret_pin;
Packit 8fb625
	g_free (data);
Packit 8fb625
Packit 8fb625
	return ret_pin;
Packit 8fb625
}
Packit 8fb625