1/*
2 *
3 *  BlueZ - Bluetooth protocol stack for Linux
4 *
5 *  Copyright (C) 2003-2010  Marcel Holtmann <marcel@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
46struct cups_device {
47	char *bdaddr;
48	char *name;
49	char *id;
50};
51
52static GSList *device_list = NULL;
53static GMainLoop *loop = NULL;
54static DBusConnection *conn = NULL;
55static gboolean doing_disco = FALSE;
56
57#define ATTRID_1284ID 0x0300
58
59struct context_data {
60	gboolean found;
61	char *id;
62};
63
64static 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
98static GMarkupParser parser = {
99	element_start, NULL, NULL, NULL, NULL
100};
101
102static 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
124static 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
191static 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
213static 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
244static 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
304static 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
331static 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
397static void discovery_completed(void)
398{
399	g_slist_free(device_list);
400	device_list = NULL;
401
402	g_main_loop_quit(loop);
403}
404
405static 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
422static 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
474static 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
522static 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
622static 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
700int 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
805service_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
843connect:
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