1/*
2 *
3 *  BlueZ - Bluetooth protocol stack for Linux
4 *
5 *  Copyright (C) 2003-2009  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, const gchar **attribute_names,
66		const gchar **attribute_values, gpointer user_data, GError **err)
67{
68	struct context_data *ctx_data = user_data;
69
70	if (!strcmp(element_name, "record"))
71		return;
72
73	if (!strcmp(element_name, "attribute")) {
74		int i;
75		for (i = 0; attribute_names[i]; i++) {
76			if (!strcmp(attribute_names[i], "id")) {
77				if (strtol(attribute_values[i], 0, 0) == ATTRID_1284ID)
78					ctx_data->found = TRUE;
79				break;
80			}
81		}
82		return;
83	}
84
85	if (ctx_data->found  && !strcmp(element_name, "text")) {
86		int i;
87		for (i = 0; attribute_names[i]; i++) {
88			if (!strcmp(attribute_names[i], "value")) {
89				ctx_data->id = g_strdup(attribute_values[i] + 2);
90				ctx_data->found = FALSE;
91			}
92		}
93	}
94}
95
96static GMarkupParser parser = {
97	element_start, NULL, NULL, NULL, NULL
98};
99
100static char *sdp_xml_parse_record(const char *data)
101{
102	GMarkupParseContext *ctx;
103	struct context_data ctx_data;
104	int size;
105
106	size = strlen(data);
107	ctx_data.found = FALSE;
108	ctx_data.id = NULL;
109	ctx = g_markup_parse_context_new(&parser, 0, &ctx_data, NULL);
110
111	if (g_markup_parse_context_parse(ctx, data, size, NULL) == FALSE) {
112		g_markup_parse_context_free(ctx);
113		g_free(ctx_data.id);
114		return NULL;
115	}
116
117	g_markup_parse_context_free(ctx);
118
119	return ctx_data.id;
120}
121
122static char *device_get_ieee1284_id(const char *adapter, const char *device)
123{
124	DBusMessage *message, *reply;
125	DBusMessageIter iter, reply_iter;
126	DBusMessageIter reply_iter_entry;
127	const char *hcr_print = "00001126-0000-1000-8000-00805f9b34fb";
128	const char *xml;
129	char *id = NULL;
130
131	/* Look for the service handle of the HCRP service */
132	message = dbus_message_new_method_call("org.bluez", device,
133						"org.bluez.Device",
134						"DiscoverServices");
135	dbus_message_iter_init_append(message, &iter);
136	dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &hcr_print);
137
138	reply = dbus_connection_send_with_reply_and_block(conn,
139							message, -1, NULL);
140
141	dbus_message_unref(message);
142
143	if (!reply)
144		return NULL;
145
146	dbus_message_iter_init(reply, &reply_iter);
147
148	if (dbus_message_iter_get_arg_type(&reply_iter) != DBUS_TYPE_ARRAY) {
149		dbus_message_unref(reply);
150		return NULL;
151	}
152
153	dbus_message_iter_recurse(&reply_iter, &reply_iter_entry);
154
155	/* Hopefully we only get one handle, or take a punt */
156	while (dbus_message_iter_get_arg_type(&reply_iter_entry) == DBUS_TYPE_DICT_ENTRY) {
157		guint32 key;
158		DBusMessageIter dict_entry;
159
160		dbus_message_iter_recurse(&reply_iter_entry, &dict_entry);
161
162		/* Key ? */
163		dbus_message_iter_get_basic(&dict_entry, &key);
164		if (!key) {
165			dbus_message_iter_next(&reply_iter_entry);
166			continue;
167		}
168
169		/* Try to get the value */
170		if (!dbus_message_iter_next(&dict_entry)) {
171			dbus_message_iter_next(&reply_iter_entry);
172			continue;
173		}
174
175		dbus_message_iter_get_basic(&dict_entry, &xml);
176
177		id = sdp_xml_parse_record(xml);
178		if (id != NULL)
179			break;
180		dbus_message_iter_next(&reply_iter_entry);
181	}
182
183	dbus_message_unref(reply);
184
185	return id;
186}
187
188static void add_device_to_list(const char *name, const char *bdaddr, const char *id)
189{
190	struct cups_device *device;
191	GSList *l;
192
193	/* Look for the device in the list */
194	for (l = device_list; l != NULL; l = l->next) {
195		device = (struct cups_device *) l->data;
196
197		if (strcmp(device->bdaddr, bdaddr) == 0) {
198			if (device->name != name) {
199				g_free(device->name);
200				device->name = g_strdup(name);
201			}
202			g_free(device->id);
203			device->id = g_strdup(id);
204			return;
205		}
206	}
207
208	/* Or add it to the list if it's not there */
209	device = g_new0(struct cups_device, 1);
210	device->bdaddr = g_strdup(bdaddr);
211	device->name = g_strdup(name);
212	device->id = g_strdup(id);
213
214	device_list = g_slist_prepend(device_list, device);
215}
216
217static void print_printer_details(const char *name, const char *bdaddr, const char *id)
218{
219	char *uri, *escaped;
220
221	escaped = g_strdelimit(g_strdup(name), "\"", '\'');
222	uri = g_strdup_printf("bluetooth://%c%c%c%c%c%c%c%c%c%c%c%c",
223		 bdaddr[0], bdaddr[1],
224		 bdaddr[3], bdaddr[4],
225		 bdaddr[6], bdaddr[7],
226		 bdaddr[9], bdaddr[10],
227		 bdaddr[12], bdaddr[13],
228		 bdaddr[15], bdaddr[16]);
229	printf("network %s \"Unknown\" \"%s (Bluetooth)\"", uri, escaped);
230	if (id != NULL)
231		printf(" \"%s\"\n", id);
232	else
233		printf("\n");
234	g_free(escaped);
235	g_free(uri);
236}
237
238static gboolean parse_device_properties(DBusMessageIter *reply_iter, char **name, char **bdaddr)
239{
240	guint32 class = 0;
241	DBusMessageIter reply_iter_entry;
242
243	if (dbus_message_iter_get_arg_type(reply_iter) != DBUS_TYPE_ARRAY)
244		return FALSE;
245
246	dbus_message_iter_recurse(reply_iter, &reply_iter_entry);
247
248	while (dbus_message_iter_get_arg_type(&reply_iter_entry) == DBUS_TYPE_DICT_ENTRY) {
249		const char *key;
250		DBusMessageIter dict_entry, iter_dict_val;
251
252		dbus_message_iter_recurse(&reply_iter_entry, &dict_entry);
253
254		/* Key == Class ? */
255		dbus_message_iter_get_basic(&dict_entry, &key);
256		if (!key) {
257			dbus_message_iter_next(&reply_iter_entry);
258			continue;
259		}
260
261		if (strcmp(key, "Class") != 0 &&
262				strcmp(key, "Alias") != 0 &&
263				strcmp(key, "Address") != 0) {
264			dbus_message_iter_next(&reply_iter_entry);
265			continue;
266		}
267
268		/* Try to get the value */
269		if (!dbus_message_iter_next(&dict_entry)) {
270			dbus_message_iter_next(&reply_iter_entry);
271			continue;
272		}
273		dbus_message_iter_recurse(&dict_entry, &iter_dict_val);
274		if (strcmp(key, "Class") == 0) {
275			dbus_message_iter_get_basic(&iter_dict_val, &class);
276		} else {
277			const char *value;
278			dbus_message_iter_get_basic(&iter_dict_val, &value);
279			if (strcmp(key, "Alias") == 0) {
280				*name = g_strdup(value);
281			} else if (bdaddr) {
282				*bdaddr = g_strdup(value);
283			}
284		}
285		dbus_message_iter_next(&reply_iter_entry);
286	}
287
288	if (class == 0)
289		return FALSE;
290	if (((class & 0x1f00) >> 8) == 0x06 && (class & 0x80))
291		return TRUE;
292
293	return FALSE;
294}
295
296static gboolean device_is_printer(const char *adapter, const char *device_path, char **name, char **bdaddr)
297{
298	DBusMessage *message, *reply;
299	DBusMessageIter reply_iter;
300	gboolean retval;
301
302	message = dbus_message_new_method_call("org.bluez", device_path,
303					       "org.bluez.Device",
304					       "GetProperties");
305
306	reply = dbus_connection_send_with_reply_and_block(conn,
307							message, -1, NULL);
308
309	dbus_message_unref(message);
310
311	if (!reply)
312		return FALSE;
313
314	dbus_message_iter_init(reply, &reply_iter);
315
316	retval = parse_device_properties(&reply_iter, name, bdaddr);
317
318	dbus_message_unref(reply);
319
320	return retval;
321}
322
323static void remote_device_found(const char *adapter, const char *bdaddr, const char *name)
324{
325	DBusMessage *message, *reply, *adapter_reply;
326	DBusMessageIter iter;
327	char *object_path = NULL;
328	char *id;
329
330	adapter_reply = NULL;
331
332	if (adapter == NULL) {
333		message = dbus_message_new_method_call("org.bluez", "/",
334						       "org.bluez.Manager",
335						       "DefaultAdapter");
336
337		adapter_reply = dbus_connection_send_with_reply_and_block(conn,
338								  message, -1, NULL);
339
340		dbus_message_unref(message);
341
342		if (dbus_message_get_args(adapter_reply, NULL, DBUS_TYPE_OBJECT_PATH, &adapter, DBUS_TYPE_INVALID) == FALSE)
343			return;
344	}
345
346	message = dbus_message_new_method_call("org.bluez", adapter,
347					       "org.bluez.Adapter",
348					       "FindDevice");
349	dbus_message_iter_init_append(message, &iter);
350	dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &bdaddr);
351
352	if (adapter_reply != NULL)
353		dbus_message_unref(adapter_reply);
354
355	reply = dbus_connection_send_with_reply_and_block(conn,
356							message, -1, NULL);
357
358	dbus_message_unref(message);
359
360	if (!reply) {
361		message = dbus_message_new_method_call("org.bluez", adapter,
362						       "org.bluez.Adapter",
363						       "CreateDevice");
364		dbus_message_iter_init_append(message, &iter);
365		dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &bdaddr);
366
367		reply = dbus_connection_send_with_reply_and_block(conn,
368								  message, -1, NULL);
369
370		dbus_message_unref(message);
371
372		if (!reply)
373			return;
374	} else {
375		if (dbus_message_get_args(reply, NULL, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID) == FALSE)
376			return;
377	}
378
379	id = device_get_ieee1284_id(adapter, object_path);
380	add_device_to_list(name, bdaddr, id);
381	g_free(id);
382}
383
384static void discovery_completed(void)
385{
386	GSList *l;
387
388	for (l = device_list; l != NULL; l = l->next) {
389		struct cups_device *device = (struct cups_device *) l->data;
390
391		if (device->name == NULL)
392			device->name = g_strdelimit(g_strdup(device->bdaddr), ":", '-');
393		/* Give another try to getting an ID for the device */
394		if (device->id == NULL)
395			remote_device_found(NULL, device->bdaddr, device->name);
396		print_printer_details(device->name, device->bdaddr, device->id);
397		g_free(device->name);
398		g_free(device->bdaddr);
399		g_free(device->id);
400		g_free(device);
401	}
402
403	g_slist_free(device_list);
404	device_list = NULL;
405
406	g_main_loop_quit(loop);
407}
408
409static void remote_device_disappeared(const char *bdaddr)
410{
411	GSList *l;
412
413	for (l = device_list; l != NULL; l = l->next) {
414		struct cups_device *device = (struct cups_device *) l->data;
415
416		if (strcmp(device->bdaddr, bdaddr) == 0) {
417			g_free(device->name);
418			g_free(device->bdaddr);
419			g_free(device);
420			device_list = g_slist_delete_link(device_list, l);
421			return;
422		}
423	}
424}
425
426static gboolean list_known_printers(const char *adapter)
427{
428	DBusMessageIter reply_iter, iter_array;
429	DBusError error;
430	DBusMessage *message, *reply;
431
432	message = dbus_message_new_method_call ("org.bluez", adapter,
433						"org.bluez.Adapter",
434						"ListDevices");
435	if (message == NULL)
436		return FALSE;
437
438	dbus_error_init(&error);
439	reply = dbus_connection_send_with_reply_and_block(conn,
440							message, -1, &error);
441
442	dbus_message_unref(message);
443
444	if (&error != NULL && dbus_error_is_set(&error))
445		return FALSE;
446
447	dbus_message_iter_init(reply, &reply_iter);
448	if (dbus_message_iter_get_arg_type(&reply_iter) != DBUS_TYPE_ARRAY) {
449		dbus_message_unref(reply);
450		return FALSE;
451	}
452
453	dbus_message_iter_recurse(&reply_iter, &iter_array);
454	while (dbus_message_iter_get_arg_type(&iter_array) == DBUS_TYPE_OBJECT_PATH) {
455		const char *object_path;
456		char *name = NULL;
457		char *bdaddr = NULL;
458
459		dbus_message_iter_get_basic(&iter_array, &object_path);
460		if (device_is_printer(adapter, object_path, &name, &bdaddr)) {
461			char *id;
462
463			id = device_get_ieee1284_id(adapter, object_path);
464			add_device_to_list(name, bdaddr, id);
465			g_free(id);
466		}
467		g_free(name);
468		g_free(bdaddr);
469		dbus_message_iter_next(&iter_array);
470	}
471
472	dbus_message_unref(reply);
473
474	return FALSE;
475}
476
477static DBusHandlerResult filter_func(DBusConnection *connection, DBusMessage *message, void *user_data)
478{
479	if (dbus_message_is_signal(message, "org.bluez.Adapter",
480						"DeviceFound")) {
481		const char *adapter, *bdaddr;
482		char *name;
483		DBusMessageIter iter;
484
485		dbus_message_iter_init(message, &iter);
486		dbus_message_iter_get_basic(&iter, &bdaddr);
487		dbus_message_iter_next(&iter);
488
489		adapter = dbus_message_get_path(message);
490		if (parse_device_properties(&iter, &name, NULL))
491			remote_device_found(adapter, bdaddr, name);
492		g_free (name);
493	} else if (dbus_message_is_signal(message, "org.bluez.Adapter",
494						"DeviceDisappeared")) {
495		char *bdaddr;
496
497		dbus_message_get_args(message, NULL,
498					DBUS_TYPE_STRING, &bdaddr,
499					DBUS_TYPE_INVALID);
500		remote_device_disappeared(bdaddr);
501	} else if (dbus_message_is_signal(message, "org.bluez.Adapter",
502						"PropertyChanged")) {
503		DBusMessageIter iter, value_iter;
504		const char *name;
505		gboolean discovering;
506
507		dbus_message_iter_init(message, &iter);
508		dbus_message_iter_get_basic(&iter, &name);
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 (&error != NULL && 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 (&error != NULL && 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) != DBUS_TYPE_OBJECT_PATH) {
571		dbus_message_unref(reply);
572		dbus_connection_unref(conn);
573		return FALSE;
574	}
575
576	dbus_message_iter_get_basic(&reply_iter, &adapter);
577	adapter = g_strdup(adapter);
578	dbus_message_unref(reply);
579
580	if (!dbus_connection_add_filter(conn, filter_func, adapter, g_free)) {
581		g_free(adapter);
582		dbus_connection_unref(conn);
583		return FALSE;
584	}
585
586#define MATCH_FORMAT				\
587	"type='signal',"			\
588	"interface='org.bluez.Adapter',"	\
589	"sender='org.bluez',"			\
590	"path='%s'"
591
592	match = g_strdup_printf(MATCH_FORMAT, adapter);
593	dbus_bus_add_match(conn, match, &error);
594	g_free(match);
595
596	/* Add the the recent devices */
597	list_known_printers(adapter);
598
599	doing_disco = TRUE;
600	message = dbus_message_new_method_call("org.bluez", adapter,
601					"org.bluez.Adapter",
602					"StartDiscovery");
603
604	if (!dbus_connection_send_with_reply(conn, message, NULL, -1)) {
605		dbus_message_unref(message);
606		dbus_connection_unref(conn);
607		g_free(adapter);
608		return FALSE;
609	}
610	dbus_message_unref(message);
611
612	loop = g_main_loop_new(NULL, TRUE);
613	g_main_loop_run(loop);
614
615	dbus_connection_unref(conn);
616
617	return TRUE;
618}
619
620/*
621 *  Usage: printer-uri job-id user title copies options [file]
622 *
623 */
624
625int main(int argc, char *argv[])
626{
627	sdp_session_t *sdp;
628	bdaddr_t bdaddr;
629	unsigned short ctrl_psm, data_psm;
630	uint8_t channel, b[6];
631	char *ptr, str[3], device[18], service[12];
632	const char *uri, *cups_class;
633	int i, err, fd, copies, proto;
634
635	/* Make sure status messages are not buffered */
636	setbuf(stderr, NULL);
637
638	/* Ignore SIGPIPE signals */
639#ifdef HAVE_SIGSET
640	sigset(SIGPIPE, SIG_IGN);
641#elif defined(HAVE_SIGACTION)
642	memset(&action, 0, sizeof(action));
643	action.sa_handler = SIG_IGN;
644	sigaction(SIGPIPE, &action, NULL);
645#else
646	signal(SIGPIPE, SIG_IGN);
647#endif /* HAVE_SIGSET */
648
649	if (argc == 1) {
650		if (list_printers() == TRUE)
651			return CUPS_BACKEND_OK;
652		else
653			return CUPS_BACKEND_FAILED;
654	}
655
656	if (argc < 6 || argc > 7) {
657		fprintf(stderr, "Usage: bluetooth job-id user title copies options [file]\n");
658		return CUPS_BACKEND_FAILED;
659	}
660
661	if (argc == 6) {
662		fd = 0;
663		copies = 1;
664	} else {
665		if ((fd = open(argv[6], O_RDONLY)) < 0) {
666			perror("ERROR: Unable to open print file");
667			return CUPS_BACKEND_FAILED;
668		}
669		copies = atoi(argv[4]);
670	}
671
672	uri = getenv("DEVICE_URI");
673	if (!uri)
674		uri = argv[0];
675
676	if (strncasecmp(uri, "bluetooth://", 12)) {
677		fprintf(stderr, "ERROR: No device URI found\n");
678		return CUPS_BACKEND_FAILED;
679	}
680
681	ptr = argv[0] + 12;
682	for (i = 0; i < 6; i++) {
683		strncpy(str, ptr, 2);
684		b[i] = (uint8_t) strtol(str, NULL, 16);
685		ptr += 2;
686	}
687	sprintf(device, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X",
688		b[0], b[1], b[2], b[3], b[4], b[5]);
689
690	str2ba(device, &bdaddr);
691
692	ptr = strchr(ptr, '/');
693	if (ptr) {
694		strncpy(service, ptr + 1, 12);
695
696		if (!strncasecmp(ptr + 1, "spp", 3))
697			proto = 1;
698		else if (!strncasecmp(ptr + 1, "hcrp", 4))
699			proto = 2;
700		else
701			proto = 0;
702	} else {
703		strcpy(service, "auto");
704		proto = 0;
705	}
706
707	cups_class = getenv("CLASS");
708
709	fprintf(stderr, "DEBUG: %s device %s service %s fd %d copies %d class %s\n",
710			argv[0], device, service, fd, copies,
711					cups_class ? cups_class : "(none)");
712
713	fputs("STATE: +connecting-to-device\n", stderr);
714
715service_search:
716	sdp = sdp_connect(BDADDR_ANY, &bdaddr, SDP_RETRY_IF_BUSY);
717	if (!sdp) {
718		fprintf(stderr, "ERROR: Can't open Bluetooth connection\n");
719		return CUPS_BACKEND_FAILED;
720	}
721
722	switch (proto) {
723	case 1:
724		err = sdp_search_spp(sdp, &channel);
725		break;
726	case 2:
727		err = sdp_search_hcrp(sdp, &ctrl_psm, &data_psm);
728		break;
729	default:
730		proto = 2;
731		err = sdp_search_hcrp(sdp, &ctrl_psm, &data_psm);
732		if (err) {
733			proto = 1;
734			err = sdp_search_spp(sdp, &channel);
735		}
736		break;
737	}
738
739	sdp_close(sdp);
740
741	if (err) {
742		if (cups_class) {
743			fputs("INFO: Unable to contact printer, queuing on "
744					"next printer in class...\n", stderr);
745			sleep(5);
746			return CUPS_BACKEND_FAILED;
747		}
748		sleep(20);
749		fprintf(stderr, "ERROR: Can't get service information\n");
750		goto service_search;
751	}
752
753connect:
754	switch (proto) {
755	case 1:
756		err = spp_print(BDADDR_ANY, &bdaddr, channel,
757						fd, copies, cups_class);
758		break;
759	case 2:
760		err = hcrp_print(BDADDR_ANY, &bdaddr, ctrl_psm, data_psm,
761						fd, copies, cups_class);
762		break;
763	default:
764		err = CUPS_BACKEND_FAILED;
765		fprintf(stderr, "ERROR: Unsupported protocol\n");
766		break;
767	}
768
769	if (err == CUPS_BACKEND_FAILED && cups_class) {
770		fputs("INFO: Unable to contact printer, queuing on "
771					"next printer in class...\n", stderr);
772		sleep(5);
773		return CUPS_BACKEND_FAILED;
774	} else if (err == CUPS_BACKEND_RETRY) {
775		sleep(20);
776		goto connect;
777	}
778
779	if (fd != 0)
780		close(fd);
781
782	if (!err)
783		fprintf(stderr, "INFO: Ready to print\n");
784
785	return err;
786}
787