Home | History | Annotate | Download | only in cups
      1 /*
      2  *
      3  *  BlueZ - Bluetooth protocol stack for Linux
      4  *
      5  *  Copyright (C) 2003-2010  Marcel Holtmann <marcel (at) holtmann.org>
      6  *
      7  *
      8  *  This program is free software; you can redistribute it and/or modify
      9  *  it under the terms of the GNU General Public License as published by
     10  *  the Free Software Foundation; either version 2 of the License, or
     11  *  (at your option) any later version.
     12  *
     13  *  This program is distributed in the hope that it will be useful,
     14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     16  *  GNU General Public License for more details.
     17  *
     18  *  You should have received a copy of the GNU General Public License
     19  *  along with this program; if not, write to the Free Software
     20  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     21  *
     22  */
     23 
     24 #ifdef HAVE_CONFIG_H
     25 #include <config.h>
     26 #endif
     27 
     28 #include <stdio.h>
     29 #include <errno.h>
     30 #include <fcntl.h>
     31 #include <unistd.h>
     32 #include <stdlib.h>
     33 #include <string.h>
     34 #include <signal.h>
     35 #include <sys/socket.h>
     36 #include <glib.h>
     37 
     38 #include <bluetooth/bluetooth.h>
     39 #include <bluetooth/sdp.h>
     40 #include <bluetooth/sdp_lib.h>
     41 
     42 #include <gdbus.h>
     43 
     44 #include "cups.h"
     45 
     46 struct cups_device {
     47 	char *bdaddr;
     48 	char *name;
     49 	char *id;
     50 };
     51 
     52 static GSList *device_list = NULL;
     53 static GMainLoop *loop = NULL;
     54 static DBusConnection *conn = NULL;
     55 static gboolean doing_disco = FALSE;
     56 
     57 #define ATTRID_1284ID 0x0300
     58 
     59 struct context_data {
     60 	gboolean found;
     61 	char *id;
     62 };
     63 
     64 static void element_start(GMarkupParseContext *context,
     65 				const gchar *element_name,
     66 				const gchar **attribute_names,
     67 				const gchar **attribute_values,
     68 				gpointer user_data, GError **err)
     69 {
     70 	struct context_data *ctx_data = user_data;
     71 
     72 	if (!strcmp(element_name, "record"))
     73 		return;
     74 
     75 	if (!strcmp(element_name, "attribute")) {
     76 		int i;
     77 		for (i = 0; attribute_names[i]; i++) {
     78 			if (strcmp(attribute_names[i], "id") != 0)
     79 				continue;
     80 			if (strtol(attribute_values[i], 0, 0) == ATTRID_1284ID)
     81 				ctx_data->found = TRUE;
     82 			break;
     83 		}
     84 		return;
     85 	}
     86 
     87 	if (ctx_data->found  && !strcmp(element_name, "text")) {
     88 		int i;
     89 		for (i = 0; attribute_names[i]; i++) {
     90 			if (!strcmp(attribute_names[i], "value")) {
     91 				ctx_data->id = g_strdup(attribute_values[i] + 2);
     92 				ctx_data->found = FALSE;
     93 			}
     94 		}
     95 	}
     96 }
     97 
     98 static GMarkupParser parser = {
     99 	element_start, NULL, NULL, NULL, NULL
    100 };
    101 
    102 static char *sdp_xml_parse_record(const char *data)
    103 {
    104 	GMarkupParseContext *ctx;
    105 	struct context_data ctx_data;
    106 	int size;
    107 
    108 	size = strlen(data);
    109 	ctx_data.found = FALSE;
    110 	ctx_data.id = NULL;
    111 	ctx = g_markup_parse_context_new(&parser, 0, &ctx_data, NULL);
    112 
    113 	if (g_markup_parse_context_parse(ctx, data, size, NULL) == FALSE) {
    114 		g_markup_parse_context_free(ctx);
    115 		g_free(ctx_data.id);
    116 		return NULL;
    117 	}
    118 
    119 	g_markup_parse_context_free(ctx);
    120 
    121 	return ctx_data.id;
    122 }
    123 
    124 static char *device_get_ieee1284_id(const char *adapter, const char *device)
    125 {
    126 	DBusMessage *message, *reply;
    127 	DBusMessageIter iter, reply_iter;
    128 	DBusMessageIter reply_iter_entry;
    129 	const char *hcr_print = "00001126-0000-1000-8000-00805f9b34fb";
    130 	const char *xml;
    131 	char *id = NULL;
    132 
    133 	/* Look for the service handle of the HCRP service */
    134 	message = dbus_message_new_method_call("org.bluez", device,
    135 						"org.bluez.Device",
    136 						"DiscoverServices");
    137 	dbus_message_iter_init_append(message, &iter);
    138 	dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &hcr_print);
    139 
    140 	reply = dbus_connection_send_with_reply_and_block(conn,
    141 							message, -1, NULL);
    142 
    143 	dbus_message_unref(message);
    144 
    145 	if (!reply)
    146 		return NULL;
    147 
    148 	dbus_message_iter_init(reply, &reply_iter);
    149 
    150 	if (dbus_message_iter_get_arg_type(&reply_iter) != DBUS_TYPE_ARRAY) {
    151 		dbus_message_unref(reply);
    152 		return NULL;
    153 	}
    154 
    155 	dbus_message_iter_recurse(&reply_iter, &reply_iter_entry);
    156 
    157 	/* Hopefully we only get one handle, or take a punt */
    158 	while (dbus_message_iter_get_arg_type(&reply_iter_entry) ==
    159 							DBUS_TYPE_DICT_ENTRY) {
    160 		guint32 key;
    161 		DBusMessageIter dict_entry;
    162 
    163 		dbus_message_iter_recurse(&reply_iter_entry, &dict_entry);
    164 
    165 		/* Key ? */
    166 		dbus_message_iter_get_basic(&dict_entry, &key);
    167 		if (!key) {
    168 			dbus_message_iter_next(&reply_iter_entry);
    169 			continue;
    170 		}
    171 
    172 		/* Try to get the value */
    173 		if (!dbus_message_iter_next(&dict_entry)) {
    174 			dbus_message_iter_next(&reply_iter_entry);
    175 			continue;
    176 		}
    177 
    178 		dbus_message_iter_get_basic(&dict_entry, &xml);
    179 
    180 		id = sdp_xml_parse_record(xml);
    181 		if (id != NULL)
    182 			break;
    183 		dbus_message_iter_next(&reply_iter_entry);
    184 	}
    185 
    186 	dbus_message_unref(reply);
    187 
    188 	return id;
    189 }
    190 
    191 static void print_printer_details(const char *name, const char *bdaddr,
    192 								const char *id)
    193 {
    194 	char *uri, *escaped;
    195 
    196 	escaped = g_strdelimit(g_strdup(name), "\"", '\'');
    197 	uri = g_strdup_printf("bluetooth://%c%c%c%c%c%c%c%c%c%c%c%c",
    198 				bdaddr[0], bdaddr[1],
    199 				bdaddr[3], bdaddr[4],
    200 				bdaddr[6], bdaddr[7],
    201 				bdaddr[9], bdaddr[10],
    202 				bdaddr[12], bdaddr[13],
    203 				bdaddr[15], bdaddr[16]);
    204 	printf("direct %s \"%s\" \"%s (Bluetooth)\"", uri, escaped, escaped);
    205 	if (id != NULL)
    206 		printf(" \"%s\"\n", id);
    207 	else
    208 		printf("\n");
    209 	g_free(escaped);
    210 	g_free(uri);
    211 }
    212 
    213 static void add_device_to_list(const char *name, const char *bdaddr,
    214 								const char *id)
    215 {
    216 	struct cups_device *device;
    217 	GSList *l;
    218 
    219 	/* Look for the device in the list */
    220 	for (l = device_list; l != NULL; l = l->next) {
    221 		device = (struct cups_device *) l->data;
    222 
    223 		if (strcmp(device->bdaddr, bdaddr) == 0) {
    224 			if (device->name != name) {
    225 				g_free(device->name);
    226 				device->name = g_strdup(name);
    227 			}
    228 			g_free(device->id);
    229 			device->id = g_strdup(id);
    230 			return;
    231 		}
    232 	}
    233 
    234 	/* Or add it to the list if it's not there */
    235 	device = g_new0(struct cups_device, 1);
    236 	device->bdaddr = g_strdup(bdaddr);
    237 	device->name = g_strdup(name);
    238 	device->id = g_strdup(id);
    239 
    240 	device_list = g_slist_prepend(device_list, device);
    241 	print_printer_details(device->name, device->bdaddr, device->id);
    242 }
    243 
    244 static gboolean parse_device_properties(DBusMessageIter *reply_iter,
    245 						char **name, char **bdaddr)
    246 {
    247 	guint32 class = 0;
    248 	DBusMessageIter reply_iter_entry;
    249 
    250 	if (dbus_message_iter_get_arg_type(reply_iter) != DBUS_TYPE_ARRAY)
    251 		return FALSE;
    252 
    253 	dbus_message_iter_recurse(reply_iter, &reply_iter_entry);
    254 
    255 	while (dbus_message_iter_get_arg_type(&reply_iter_entry) ==
    256 							DBUS_TYPE_DICT_ENTRY) {
    257 		const char *key;
    258 		DBusMessageIter dict_entry, iter_dict_val;
    259 
    260 		dbus_message_iter_recurse(&reply_iter_entry, &dict_entry);
    261 
    262 		/* Key == Class ? */
    263 		dbus_message_iter_get_basic(&dict_entry, &key);
    264 		if (!key) {
    265 			dbus_message_iter_next(&reply_iter_entry);
    266 			continue;
    267 		}
    268 
    269 		if (strcmp(key, "Class") != 0 &&
    270 				strcmp(key, "Alias") != 0 &&
    271 				strcmp(key, "Address") != 0) {
    272 			dbus_message_iter_next(&reply_iter_entry);
    273 			continue;
    274 		}
    275 
    276 		/* Try to get the value */
    277 		if (!dbus_message_iter_next(&dict_entry)) {
    278 			dbus_message_iter_next(&reply_iter_entry);
    279 			continue;
    280 		}
    281 		dbus_message_iter_recurse(&dict_entry, &iter_dict_val);
    282 		if (strcmp(key, "Class") == 0) {
    283 			dbus_message_iter_get_basic(&iter_dict_val, &class);
    284 		} else {
    285 			const char *value;
    286 			dbus_message_iter_get_basic(&iter_dict_val, &value);
    287 			if (strcmp(key, "Alias") == 0) {
    288 				*name = g_strdup(value);
    289 			} else if (bdaddr) {
    290 				*bdaddr = g_strdup(value);
    291 			}
    292 		}
    293 		dbus_message_iter_next(&reply_iter_entry);
    294 	}
    295 
    296 	if (class == 0)
    297 		return FALSE;
    298 	if (((class & 0x1f00) >> 8) == 0x06 && (class & 0x80))
    299 		return TRUE;
    300 
    301 	return FALSE;
    302 }
    303 
    304 static gboolean device_is_printer(const char *adapter, const char *device_path, char **name, char **bdaddr)
    305 {
    306 	DBusMessage *message, *reply;
    307 	DBusMessageIter reply_iter;
    308 	gboolean retval;
    309 
    310 	message = dbus_message_new_method_call("org.bluez", device_path,
    311 							"org.bluez.Device",
    312 							"GetProperties");
    313 
    314 	reply = dbus_connection_send_with_reply_and_block(conn,
    315 							message, -1, NULL);
    316 
    317 	dbus_message_unref(message);
    318 
    319 	if (!reply)
    320 		return FALSE;
    321 
    322 	dbus_message_iter_init(reply, &reply_iter);
    323 
    324 	retval = parse_device_properties(&reply_iter, name, bdaddr);
    325 
    326 	dbus_message_unref(reply);
    327 
    328 	return retval;
    329 }
    330 
    331 static void remote_device_found(const char *adapter, const char *bdaddr,
    332 							const char *name)
    333 {
    334 	DBusMessage *message, *reply, *adapter_reply;
    335 	DBusMessageIter iter;
    336 	char *object_path = NULL;
    337 	char *id;
    338 
    339 	adapter_reply = NULL;
    340 
    341 	if (adapter == NULL) {
    342 		message = dbus_message_new_method_call("org.bluez", "/",
    343 							"org.bluez.Manager",
    344 							"DefaultAdapter");
    345 
    346 		adapter_reply = dbus_connection_send_with_reply_and_block(conn,
    347 							message, -1, NULL);
    348 
    349 		dbus_message_unref(message);
    350 
    351 		if (dbus_message_get_args(adapter_reply, NULL,
    352 					DBUS_TYPE_OBJECT_PATH, &adapter,
    353 					DBUS_TYPE_INVALID) == FALSE)
    354 			return;
    355 	}
    356 
    357 	message = dbus_message_new_method_call("org.bluez", adapter,
    358 							"org.bluez.Adapter",
    359 							"FindDevice");
    360 	dbus_message_iter_init_append(message, &iter);
    361 	dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &bdaddr);
    362 
    363 	if (adapter_reply != NULL)
    364 		dbus_message_unref(adapter_reply);
    365 
    366 	reply = dbus_connection_send_with_reply_and_block(conn,
    367 							message, -1, NULL);
    368 
    369 	dbus_message_unref(message);
    370 
    371 	if (!reply) {
    372 		message = dbus_message_new_method_call("org.bluez", adapter,
    373 							"org.bluez.Adapter",
    374 							"CreateDevice");
    375 		dbus_message_iter_init_append(message, &iter);
    376 		dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &bdaddr);
    377 
    378 		reply = dbus_connection_send_with_reply_and_block(conn,
    379 							message, -1, NULL);
    380 
    381 		dbus_message_unref(message);
    382 
    383 		if (!reply)
    384 			return;
    385 	}
    386 
    387 	if (dbus_message_get_args(reply, NULL,
    388 					DBUS_TYPE_OBJECT_PATH, &object_path,
    389 					DBUS_TYPE_INVALID) == FALSE)
    390 		return;
    391 
    392 	id = device_get_ieee1284_id(adapter, object_path);
    393 	add_device_to_list(name, bdaddr, id);
    394 	g_free(id);
    395 }
    396 
    397 static void discovery_completed(void)
    398 {
    399 	g_slist_free(device_list);
    400 	device_list = NULL;
    401 
    402 	g_main_loop_quit(loop);
    403 }
    404 
    405 static void remote_device_disappeared(const char *bdaddr)
    406 {
    407 	GSList *l;
    408 
    409 	for (l = device_list; l != NULL; l = l->next) {
    410 		struct cups_device *device = l->data;
    411 
    412 		if (strcmp(device->bdaddr, bdaddr) == 0) {
    413 			g_free(device->name);
    414 			g_free(device->bdaddr);
    415 			g_free(device);
    416 			device_list = g_slist_delete_link(device_list, l);
    417 			return;
    418 		}
    419 	}
    420 }
    421 
    422 static gboolean list_known_printers(const char *adapter)
    423 {
    424 	DBusMessageIter reply_iter, iter_array;
    425 	DBusError error;
    426 	DBusMessage *message, *reply;
    427 
    428 	message = dbus_message_new_method_call("org.bluez", adapter,
    429 						"org.bluez.Adapter",
    430 						"ListDevices");
    431 	if (message == NULL)
    432 		return FALSE;
    433 
    434 	dbus_error_init(&error);
    435 	reply = dbus_connection_send_with_reply_and_block(conn, message,
    436 								-1, &error);
    437 
    438 	dbus_message_unref(message);
    439 
    440 	if (dbus_error_is_set(&error))
    441 		return FALSE;
    442 
    443 	dbus_message_iter_init(reply, &reply_iter);
    444 	if (dbus_message_iter_get_arg_type(&reply_iter) != DBUS_TYPE_ARRAY) {
    445 		dbus_message_unref(reply);
    446 		return FALSE;
    447 	}
    448 
    449 	dbus_message_iter_recurse(&reply_iter, &iter_array);
    450 	while (dbus_message_iter_get_arg_type(&iter_array) ==
    451 						DBUS_TYPE_OBJECT_PATH) {
    452 		const char *object_path;
    453 		char *name = NULL;
    454 		char *bdaddr = NULL;
    455 
    456 		dbus_message_iter_get_basic(&iter_array, &object_path);
    457 		if (device_is_printer(adapter, object_path, &name, &bdaddr)) {
    458 			char *id;
    459 
    460 			id = device_get_ieee1284_id(adapter, object_path);
    461 			add_device_to_list(name, bdaddr, id);
    462 			g_free(id);
    463 		}
    464 		g_free(name);
    465 		g_free(bdaddr);
    466 		dbus_message_iter_next(&iter_array);
    467 	}
    468 
    469 	dbus_message_unref(reply);
    470 
    471 	return FALSE;
    472 }
    473 
    474 static DBusHandlerResult filter_func(DBusConnection *connection,
    475 					DBusMessage *message, void *user_data)
    476 {
    477 	if (dbus_message_is_signal(message, "org.bluez.Adapter",
    478 						"DeviceFound")) {
    479 		const char *adapter, *bdaddr;
    480 		char *name;
    481 		DBusMessageIter iter;
    482 
    483 		dbus_message_iter_init(message, &iter);
    484 		dbus_message_iter_get_basic(&iter, &bdaddr);
    485 		dbus_message_iter_next(&iter);
    486 
    487 		adapter = dbus_message_get_path(message);
    488 		if (parse_device_properties(&iter, &name, NULL))
    489 			remote_device_found(adapter, bdaddr, name);
    490 		g_free (name);
    491 	} else if (dbus_message_is_signal(message, "org.bluez.Adapter",
    492 						"DeviceDisappeared")) {
    493 		const char *bdaddr;
    494 
    495 		dbus_message_get_args(message, NULL,
    496 					DBUS_TYPE_STRING, &bdaddr,
    497 					DBUS_TYPE_INVALID);
    498 		remote_device_disappeared(bdaddr);
    499 	} else if (dbus_message_is_signal(message, "org.bluez.Adapter",
    500 						"PropertyChanged")) {
    501 		DBusMessageIter iter, value_iter;
    502 		const char *name;
    503 		gboolean discovering;
    504 
    505 		dbus_message_iter_init(message, &iter);
    506 		dbus_message_iter_get_basic(&iter, &name);
    507 		if (name == NULL || strcmp(name, "Discovering") != 0)
    508 			return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    509 		dbus_message_iter_next(&iter);
    510 		dbus_message_iter_recurse(&iter, &value_iter);
    511 		dbus_message_iter_get_basic(&value_iter, &discovering);
    512 
    513 		if (discovering == FALSE && doing_disco) {
    514 			doing_disco = FALSE;
    515 			discovery_completed();
    516 		}
    517 	}
    518 
    519 	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    520 }
    521 
    522 static gboolean list_printers(void)
    523 {
    524 	/* 1. Connect to the bus
    525 	 * 2. Get the manager
    526 	 * 3. Get the default adapter
    527 	 * 4. Get a list of devices
    528 	 * 5. Get the class of each device
    529 	 * 6. Print the details from each printer device
    530 	 */
    531 	DBusError error;
    532 	dbus_bool_t hcid_exists;
    533 	DBusMessage *reply, *message;
    534 	DBusMessageIter reply_iter;
    535 	char *adapter, *match;
    536 
    537 	conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
    538 	if (conn == NULL)
    539 		return TRUE;
    540 
    541 	dbus_error_init(&error);
    542 	hcid_exists = dbus_bus_name_has_owner(conn, "org.bluez", &error);
    543 	if (dbus_error_is_set(&error))
    544 		return TRUE;
    545 
    546 	if (!hcid_exists)
    547 		return TRUE;
    548 
    549 	/* Get the default adapter */
    550 	message = dbus_message_new_method_call("org.bluez", "/",
    551 						"org.bluez.Manager",
    552 						"DefaultAdapter");
    553 	if (message == NULL) {
    554 		dbus_connection_unref(conn);
    555 		return FALSE;
    556 	}
    557 
    558 	reply = dbus_connection_send_with_reply_and_block(conn,
    559 							message, -1, &error);
    560 
    561 	dbus_message_unref(message);
    562 
    563 	if (dbus_error_is_set(&error)) {
    564 		dbus_connection_unref(conn);
    565 		/* No adapter */
    566 		return TRUE;
    567 	}
    568 
    569 	dbus_message_iter_init(reply, &reply_iter);
    570 	if (dbus_message_iter_get_arg_type(&reply_iter) !=
    571 						DBUS_TYPE_OBJECT_PATH) {
    572 		dbus_message_unref(reply);
    573 		dbus_connection_unref(conn);
    574 		return FALSE;
    575 	}
    576 
    577 	dbus_message_iter_get_basic(&reply_iter, &adapter);
    578 	adapter = g_strdup(adapter);
    579 	dbus_message_unref(reply);
    580 
    581 	if (!dbus_connection_add_filter(conn, filter_func, adapter, g_free)) {
    582 		g_free(adapter);
    583 		dbus_connection_unref(conn);
    584 		return FALSE;
    585 	}
    586 
    587 #define MATCH_FORMAT				\
    588 	"type='signal',"			\
    589 	"interface='org.bluez.Adapter',"	\
    590 	"sender='org.bluez',"			\
    591 	"path='%s'"
    592 
    593 	match = g_strdup_printf(MATCH_FORMAT, adapter);
    594 	dbus_bus_add_match(conn, match, &error);
    595 	g_free(match);
    596 
    597 	/* Add the the recent devices */
    598 	list_known_printers(adapter);
    599 
    600 	doing_disco = TRUE;
    601 	message = dbus_message_new_method_call("org.bluez", adapter,
    602 					"org.bluez.Adapter",
    603 					"StartDiscovery");
    604 
    605 	if (!dbus_connection_send_with_reply(conn, message, NULL, -1)) {
    606 		dbus_message_unref(message);
    607 		dbus_connection_unref(conn);
    608 		g_free(adapter);
    609 		return FALSE;
    610 	}
    611 	dbus_message_unref(message);
    612 
    613 	loop = g_main_loop_new(NULL, TRUE);
    614 	g_main_loop_run(loop);
    615 
    616 	g_free(adapter);
    617 	dbus_connection_unref(conn);
    618 
    619 	return TRUE;
    620 }
    621 
    622 static gboolean print_ieee1284(const char *bdaddr)
    623 {
    624 	DBusMessage *message, *reply, *adapter_reply;
    625 	DBusMessageIter iter;
    626 	char *object_path = NULL;
    627 	char *adapter;
    628 	char *id;
    629 
    630 	adapter_reply = NULL;
    631 
    632 	conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
    633 	if (conn == NULL)
    634 		return FALSE;
    635 
    636 	message = dbus_message_new_method_call("org.bluez", "/",
    637 			"org.bluez.Manager",
    638 			"DefaultAdapter");
    639 
    640 	adapter_reply = dbus_connection_send_with_reply_and_block(conn,
    641 			message, -1, NULL);
    642 
    643 	dbus_message_unref(message);
    644 
    645 	if (dbus_message_get_args(adapter_reply, NULL,
    646 			DBUS_TYPE_OBJECT_PATH, &adapter,
    647 			DBUS_TYPE_INVALID) == FALSE)
    648 		return FALSE;
    649 
    650 	message = dbus_message_new_method_call("org.bluez", adapter,
    651 			"org.bluez.Adapter",
    652 			"FindDevice");
    653 	dbus_message_iter_init_append(message, &iter);
    654 	dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &bdaddr);
    655 
    656 	if (adapter_reply != NULL)
    657 		dbus_message_unref(adapter_reply);
    658 
    659 	reply = dbus_connection_send_with_reply_and_block(conn,
    660 			message, -1, NULL);
    661 
    662 	dbus_message_unref(message);
    663 
    664 	if (!reply) {
    665 		message = dbus_message_new_method_call("org.bluez", adapter,
    666 				"org.bluez.Adapter",
    667 				"CreateDevice");
    668 		dbus_message_iter_init_append(message, &iter);
    669 		dbus_message_iter_append_basic(&iter,
    670 				DBUS_TYPE_STRING, &bdaddr);
    671 
    672 		reply = dbus_connection_send_with_reply_and_block(conn,
    673 				message, -1, NULL);
    674 
    675 		dbus_message_unref(message);
    676 
    677 		if (!reply)
    678 			return FALSE;
    679 	}
    680 
    681 	if (dbus_message_get_args(reply, NULL,
    682 					DBUS_TYPE_OBJECT_PATH, &object_path,
    683 					DBUS_TYPE_INVALID) == FALSE)
    684 		return FALSE;
    685 
    686 	id = device_get_ieee1284_id(adapter, object_path);
    687 	if (id == NULL)
    688 		return FALSE;
    689 	printf("%s", id);
    690 	g_free(id);
    691 
    692 	return TRUE;
    693 }
    694 
    695 /*
    696  *  Usage: printer-uri job-id user title copies options [file]
    697  *
    698  */
    699 
    700 int main(int argc, char *argv[])
    701 {
    702 	sdp_session_t *sdp;
    703 	bdaddr_t bdaddr;
    704 	unsigned short ctrl_psm, data_psm;
    705 	uint8_t channel, b[6];
    706 	char *ptr, str[3], device[18], service[12];
    707 	const char *uri, *cups_class;
    708 	int i, err, fd, copies, proto;
    709 
    710 	/* Make sure status messages are not buffered */
    711 	setbuf(stderr, NULL);
    712 
    713 	/* Make sure output is not buffered */
    714 	setbuf(stdout, NULL);
    715 
    716 	/* Ignore SIGPIPE signals */
    717 #ifdef HAVE_SIGSET
    718 	sigset(SIGPIPE, SIG_IGN);
    719 #elif defined(HAVE_SIGACTION)
    720 	memset(&action, 0, sizeof(action));
    721 	action.sa_handler = SIG_IGN;
    722 	sigaction(SIGPIPE, &action, NULL);
    723 #else
    724 	signal(SIGPIPE, SIG_IGN);
    725 #endif /* HAVE_SIGSET */
    726 
    727 	if (argc == 1) {
    728 		if (list_printers() == TRUE)
    729 			return CUPS_BACKEND_OK;
    730 		else
    731 			return CUPS_BACKEND_FAILED;
    732 	} else if (argc == 3 && strcmp(argv[1], "--get-deviceid") == 0) {
    733 		if (bachk(argv[2]) < 0) {
    734 			fprintf(stderr, "Invalid Bluetooth address '%s'\n",
    735 					argv[2]);
    736 			return CUPS_BACKEND_FAILED;
    737 		}
    738 		if (print_ieee1284(argv[2]) == FALSE)
    739 			return CUPS_BACKEND_FAILED;
    740 		return CUPS_BACKEND_OK;
    741 	}
    742 
    743 	if (argc < 6 || argc > 7) {
    744 		fprintf(stderr, "Usage: bluetooth job-id user title copies"
    745 				" options [file]\n");
    746 		fprintf(stderr, "       bluetooth --get-deviceid [bdaddr]\n");
    747 		return CUPS_BACKEND_FAILED;
    748 	}
    749 
    750 	if (argc == 6) {
    751 		fd = 0;
    752 		copies = 1;
    753 	} else {
    754 		if ((fd = open(argv[6], O_RDONLY)) < 0) {
    755 			perror("ERROR: Unable to open print file");
    756 			return CUPS_BACKEND_FAILED;
    757 		}
    758 		copies = atoi(argv[4]);
    759 	}
    760 
    761 	uri = getenv("DEVICE_URI");
    762 	if (!uri)
    763 		uri = argv[0];
    764 
    765 	if (strncasecmp(uri, "bluetooth://", 12)) {
    766 		fprintf(stderr, "ERROR: No device URI found\n");
    767 		return CUPS_BACKEND_FAILED;
    768 	}
    769 
    770 	ptr = argv[0] + 12;
    771 	for (i = 0; i < 6; i++) {
    772 		strncpy(str, ptr, 2);
    773 		b[i] = (uint8_t) strtol(str, NULL, 16);
    774 		ptr += 2;
    775 	}
    776 	sprintf(device, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X",
    777 			b[0], b[1], b[2], b[3], b[4], b[5]);
    778 
    779 	str2ba(device, &bdaddr);
    780 
    781 	ptr = strchr(ptr, '/');
    782 	if (ptr) {
    783 		strncpy(service, ptr + 1, 12);
    784 
    785 		if (!strncasecmp(ptr + 1, "spp", 3))
    786 			proto = 1;
    787 		else if (!strncasecmp(ptr + 1, "hcrp", 4))
    788 			proto = 2;
    789 		else
    790 			proto = 0;
    791 	} else {
    792 		strcpy(service, "auto");
    793 		proto = 0;
    794 	}
    795 
    796 	cups_class = getenv("CLASS");
    797 
    798 	fprintf(stderr,
    799 		"DEBUG: %s device %s service %s fd %d copies %d class %s\n",
    800 			argv[0], device, service, fd, copies,
    801 			cups_class ? cups_class : "(none)");
    802 
    803 	fputs("STATE: +connecting-to-device\n", stderr);
    804 
    805 service_search:
    806 	sdp = sdp_connect(BDADDR_ANY, &bdaddr, SDP_RETRY_IF_BUSY);
    807 	if (!sdp) {
    808 		fprintf(stderr, "ERROR: Can't open Bluetooth connection\n");
    809 		return CUPS_BACKEND_FAILED;
    810 	}
    811 
    812 	switch (proto) {
    813 	case 1:
    814 		err = sdp_search_spp(sdp, &channel);
    815 		break;
    816 	case 2:
    817 		err = sdp_search_hcrp(sdp, &ctrl_psm, &data_psm);
    818 		break;
    819 	default:
    820 		proto = 2;
    821 		err = sdp_search_hcrp(sdp, &ctrl_psm, &data_psm);
    822 		if (err) {
    823 			proto = 1;
    824 			err = sdp_search_spp(sdp, &channel);
    825 		}
    826 		break;
    827 	}
    828 
    829 	sdp_close(sdp);
    830 
    831 	if (err) {
    832 		if (cups_class) {
    833 			fputs("INFO: Unable to contact printer, queuing on "
    834 					"next printer in class...\n", stderr);
    835 			sleep(5);
    836 			return CUPS_BACKEND_FAILED;
    837 		}
    838 		sleep(20);
    839 		fprintf(stderr, "ERROR: Can't get service information\n");
    840 		goto service_search;
    841 	}
    842 
    843 connect:
    844 	switch (proto) {
    845 	case 1:
    846 		err = spp_print(BDADDR_ANY, &bdaddr, channel,
    847 						fd, copies, cups_class);
    848 		break;
    849 	case 2:
    850 		err = hcrp_print(BDADDR_ANY, &bdaddr, ctrl_psm, data_psm,
    851 						fd, copies, cups_class);
    852 		break;
    853 	default:
    854 		err = CUPS_BACKEND_FAILED;
    855 		fprintf(stderr, "ERROR: Unsupported protocol\n");
    856 		break;
    857 	}
    858 
    859 	if (err == CUPS_BACKEND_FAILED && cups_class) {
    860 		fputs("INFO: Unable to contact printer, queuing on "
    861 					"next printer in class...\n", stderr);
    862 		sleep(5);
    863 		return CUPS_BACKEND_FAILED;
    864 	} else if (err == CUPS_BACKEND_RETRY) {
    865 		sleep(20);
    866 		goto connect;
    867 	}
    868 
    869 	if (fd != 0)
    870 		close(fd);
    871 
    872 	if (!err)
    873 		fprintf(stderr, "INFO: Ready to print\n");
    874 
    875 	return err;
    876 }
    877