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