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