3 * BlueZ - Bluetooth protocol stack for Linux
5 * Copyright (C) 2003-2010 Marcel Holtmann <marcel@holtmann.org>
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.
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.
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
35 #include <sys/socket.h>
38 #include <bluetooth/bluetooth.h>
39 #include <bluetooth/sdp.h>
40 #include <bluetooth/sdp_lib.h>
52 static GSList *device_list = NULL;
53 static GMainLoop *loop = NULL;
54 static DBusConnection *conn = NULL;
55 static gboolean doing_disco = FALSE;
57 #define ATTRID_1284ID 0x0300
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)
70 struct context_data *ctx_data = user_data;
72 if (!strcmp(element_name, "record"))
75 if (!strcmp(element_name, "attribute")) {
77 for (i = 0; attribute_names[i]; i++) {
78 if (strcmp(attribute_names[i], "id") != 0)
80 if (strtol(attribute_values[i], 0, 0) == ATTRID_1284ID)
81 ctx_data->found = TRUE;
87 if (ctx_data->found && !strcmp(element_name, "text")) {
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;
98 static GMarkupParser parser = {
99 element_start, NULL, NULL, NULL, NULL
102 static char *sdp_xml_parse_record(const char *data)
104 GMarkupParseContext *ctx;
105 struct context_data ctx_data;
109 ctx_data.found = FALSE;
111 ctx = g_markup_parse_context_new(&parser, 0, &ctx_data, NULL);
113 if (g_markup_parse_context_parse(ctx, data, size, NULL) == FALSE) {
114 g_markup_parse_context_free(ctx);
119 g_markup_parse_context_free(ctx);
124 static char *device_get_ieee1284_id(const char *adapter, const char *device)
126 DBusMessage *message, *reply;
127 DBusMessageIter iter, reply_iter;
128 DBusMessageIter reply_iter_entry;
129 const char *hcr_print = "00001126-0000-1000-8000-00805f9b34fb";
133 /* Look for the service handle of the HCRP service */
134 message = dbus_message_new_method_call("org.bluez", device,
137 dbus_message_iter_init_append(message, &iter);
138 dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &hcr_print);
140 reply = dbus_connection_send_with_reply_and_block(conn,
143 dbus_message_unref(message);
148 dbus_message_iter_init(reply, &reply_iter);
150 if (dbus_message_iter_get_arg_type(&reply_iter) != DBUS_TYPE_ARRAY) {
151 dbus_message_unref(reply);
155 dbus_message_iter_recurse(&reply_iter, &reply_iter_entry);
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) {
161 DBusMessageIter dict_entry;
163 dbus_message_iter_recurse(&reply_iter_entry, &dict_entry);
166 dbus_message_iter_get_basic(&dict_entry, &key);
168 dbus_message_iter_next(&reply_iter_entry);
172 /* Try to get the value */
173 if (!dbus_message_iter_next(&dict_entry)) {
174 dbus_message_iter_next(&reply_iter_entry);
178 dbus_message_iter_get_basic(&dict_entry, &xml);
180 id = sdp_xml_parse_record(xml);
183 dbus_message_iter_next(&reply_iter_entry);
186 dbus_message_unref(reply);
191 static void print_printer_details(const char *name, const char *bdaddr,
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);
206 printf(" \"%s\"\n", id);
213 static void add_device_to_list(const char *name, const char *bdaddr,
216 struct cups_device *device;
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;
223 if (strcmp(device->bdaddr, bdaddr) == 0) {
224 if (device->name != name) {
225 g_free(device->name);
226 device->name = g_strdup(name);
229 device->id = g_strdup(id);
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);
240 device_list = g_slist_prepend(device_list, device);
241 print_printer_details(device->name, device->bdaddr, device->id);
244 static gboolean parse_device_properties(DBusMessageIter *reply_iter,
245 char **name, char **bdaddr)
248 DBusMessageIter reply_iter_entry;
250 if (dbus_message_iter_get_arg_type(reply_iter) != DBUS_TYPE_ARRAY)
253 dbus_message_iter_recurse(reply_iter, &reply_iter_entry);
255 while (dbus_message_iter_get_arg_type(&reply_iter_entry) ==
256 DBUS_TYPE_DICT_ENTRY) {
258 DBusMessageIter dict_entry, iter_dict_val;
260 dbus_message_iter_recurse(&reply_iter_entry, &dict_entry);
263 dbus_message_iter_get_basic(&dict_entry, &key);
265 dbus_message_iter_next(&reply_iter_entry);
269 if (strcmp(key, "Class") != 0 &&
270 strcmp(key, "Alias") != 0 &&
271 strcmp(key, "Address") != 0) {
272 dbus_message_iter_next(&reply_iter_entry);
276 /* Try to get the value */
277 if (!dbus_message_iter_next(&dict_entry)) {
278 dbus_message_iter_next(&reply_iter_entry);
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);
286 dbus_message_iter_get_basic(&iter_dict_val, &value);
287 if (strcmp(key, "Alias") == 0) {
288 *name = g_strdup(value);
290 *bdaddr = g_strdup(value);
293 dbus_message_iter_next(&reply_iter_entry);
298 if (((class & 0x1f00) >> 8) == 0x06 && (class & 0x80))
304 static gboolean device_is_printer(const char *adapter, const char *device_path, char **name, char **bdaddr)
306 DBusMessage *message, *reply;
307 DBusMessageIter reply_iter;
310 message = dbus_message_new_method_call("org.bluez", device_path,
314 reply = dbus_connection_send_with_reply_and_block(conn,
317 dbus_message_unref(message);
322 dbus_message_iter_init(reply, &reply_iter);
324 retval = parse_device_properties(&reply_iter, name, bdaddr);
326 dbus_message_unref(reply);
331 static void remote_device_found(const char *adapter, const char *bdaddr,
334 DBusMessage *message, *reply, *adapter_reply;
335 DBusMessageIter iter;
336 char *object_path = NULL;
339 adapter_reply = NULL;
341 if (adapter == NULL) {
342 message = dbus_message_new_method_call("org.bluez", "/",
346 adapter_reply = dbus_connection_send_with_reply_and_block(conn,
349 dbus_message_unref(message);
354 if (dbus_message_get_args(adapter_reply, NULL,
355 DBUS_TYPE_OBJECT_PATH, &adapter,
356 DBUS_TYPE_INVALID) == FALSE) {
357 dbus_message_unref(adapter_reply);
362 message = dbus_message_new_method_call("org.bluez", adapter,
365 dbus_message_iter_init_append(message, &iter);
366 dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &bdaddr);
368 if (adapter_reply != NULL)
369 dbus_message_unref(adapter_reply);
371 reply = dbus_connection_send_with_reply_and_block(conn,
374 dbus_message_unref(message);
377 message = dbus_message_new_method_call("org.bluez", adapter,
380 dbus_message_iter_init_append(message, &iter);
381 dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &bdaddr);
383 reply = dbus_connection_send_with_reply_and_block(conn,
386 dbus_message_unref(message);
392 if (dbus_message_get_args(reply, NULL,
393 DBUS_TYPE_OBJECT_PATH, &object_path,
394 DBUS_TYPE_INVALID) == FALSE) {
395 dbus_message_unref(reply);
399 id = device_get_ieee1284_id(adapter, object_path);
400 add_device_to_list(name, bdaddr, id);
403 dbus_message_unref(reply);
406 static void discovery_completed(void)
408 g_slist_free(device_list);
411 g_main_loop_quit(loop);
414 static void remote_device_disappeared(const char *bdaddr)
418 for (l = device_list; l != NULL; l = l->next) {
419 struct cups_device *device = l->data;
421 if (strcmp(device->bdaddr, bdaddr) == 0) {
422 g_free(device->name);
423 g_free(device->bdaddr);
425 device_list = g_slist_delete_link(device_list, l);
431 static gboolean list_known_printers(const char *adapter)
433 DBusMessageIter reply_iter, iter_array;
435 DBusMessage *message, *reply;
437 message = dbus_message_new_method_call("org.bluez", adapter,
443 dbus_error_init(&error);
444 reply = dbus_connection_send_with_reply_and_block(conn, message,
447 dbus_message_unref(message);
449 if (dbus_error_is_set(&error))
452 dbus_message_iter_init(reply, &reply_iter);
453 if (dbus_message_iter_get_arg_type(&reply_iter) != DBUS_TYPE_ARRAY) {
454 dbus_message_unref(reply);
458 dbus_message_iter_recurse(&reply_iter, &iter_array);
459 while (dbus_message_iter_get_arg_type(&iter_array) ==
460 DBUS_TYPE_OBJECT_PATH) {
461 const char *object_path;
465 dbus_message_iter_get_basic(&iter_array, &object_path);
466 if (device_is_printer(adapter, object_path, &name, &bdaddr)) {
469 id = device_get_ieee1284_id(adapter, object_path);
470 add_device_to_list(name, bdaddr, id);
475 dbus_message_iter_next(&iter_array);
478 dbus_message_unref(reply);
483 static DBusHandlerResult filter_func(DBusConnection *connection,
484 DBusMessage *message, void *user_data)
486 if (dbus_message_is_signal(message, "org.bluez.Adapter",
488 const char *adapter, *bdaddr;
490 DBusMessageIter iter;
492 dbus_message_iter_init(message, &iter);
493 dbus_message_iter_get_basic(&iter, &bdaddr);
494 dbus_message_iter_next(&iter);
496 adapter = dbus_message_get_path(message);
497 if (parse_device_properties(&iter, &name, NULL))
498 remote_device_found(adapter, bdaddr, name);
500 } else if (dbus_message_is_signal(message, "org.bluez.Adapter",
501 "DeviceDisappeared")) {
504 dbus_message_get_args(message, NULL,
505 DBUS_TYPE_STRING, &bdaddr,
507 remote_device_disappeared(bdaddr);
508 } else if (dbus_message_is_signal(message, "org.bluez.Adapter",
509 "PropertyChanged")) {
510 DBusMessageIter iter, value_iter;
512 gboolean discovering;
514 dbus_message_iter_init(message, &iter);
515 dbus_message_iter_get_basic(&iter, &name);
516 if (name == NULL || strcmp(name, "Discovering") != 0)
517 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
518 dbus_message_iter_next(&iter);
519 dbus_message_iter_recurse(&iter, &value_iter);
520 dbus_message_iter_get_basic(&value_iter, &discovering);
522 if (discovering == FALSE && doing_disco) {
524 discovery_completed();
528 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
531 static gboolean list_printers(void)
533 /* 1. Connect to the bus
535 * 3. Get the default adapter
536 * 4. Get a list of devices
537 * 5. Get the class of each device
538 * 6. Print the details from each printer device
541 dbus_bool_t hcid_exists;
542 DBusMessage *reply, *message;
543 DBusMessageIter reply_iter;
544 char *adapter, *match;
546 conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
550 dbus_error_init(&error);
551 hcid_exists = dbus_bus_name_has_owner(conn, "org.bluez", &error);
552 if (dbus_error_is_set(&error))
558 /* Get the default adapter */
559 message = dbus_message_new_method_call("org.bluez", "/",
562 if (message == NULL) {
563 dbus_connection_unref(conn);
567 reply = dbus_connection_send_with_reply_and_block(conn,
568 message, -1, &error);
570 dbus_message_unref(message);
572 if (dbus_error_is_set(&error)) {
573 dbus_connection_unref(conn);
578 dbus_message_iter_init(reply, &reply_iter);
579 if (dbus_message_iter_get_arg_type(&reply_iter) !=
580 DBUS_TYPE_OBJECT_PATH) {
581 dbus_message_unref(reply);
582 dbus_connection_unref(conn);
586 dbus_message_iter_get_basic(&reply_iter, &adapter);
587 adapter = g_strdup(adapter);
588 dbus_message_unref(reply);
590 if (!dbus_connection_add_filter(conn, filter_func, adapter, g_free)) {
592 dbus_connection_unref(conn);
596 #define MATCH_FORMAT \
598 "interface='org.bluez.Adapter'," \
599 "sender='org.bluez'," \
602 match = g_strdup_printf(MATCH_FORMAT, adapter);
603 dbus_bus_add_match(conn, match, &error);
606 /* Add the the recent devices */
607 list_known_printers(adapter);
610 message = dbus_message_new_method_call("org.bluez", adapter,
614 if (!dbus_connection_send_with_reply(conn, message, NULL, -1)) {
615 dbus_message_unref(message);
616 dbus_connection_unref(conn);
620 dbus_message_unref(message);
622 loop = g_main_loop_new(NULL, TRUE);
623 g_main_loop_run(loop);
626 dbus_connection_unref(conn);
631 static gboolean print_ieee1284(const char *bdaddr)
633 DBusMessage *message, *reply, *adapter_reply;
634 DBusMessageIter iter;
635 char *object_path = NULL;
639 adapter_reply = NULL;
641 conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
645 message = dbus_message_new_method_call("org.bluez", "/",
649 adapter_reply = dbus_connection_send_with_reply_and_block(conn,
652 dbus_message_unref(message);
657 if (dbus_message_get_args(adapter_reply, NULL,
658 DBUS_TYPE_OBJECT_PATH, &adapter,
659 DBUS_TYPE_INVALID) == FALSE) {
660 dbus_message_unref(adapter_reply);
664 message = dbus_message_new_method_call("org.bluez", adapter,
667 dbus_message_iter_init_append(message, &iter);
668 dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &bdaddr);
670 if (adapter_reply != NULL)
671 dbus_message_unref(adapter_reply);
673 reply = dbus_connection_send_with_reply_and_block(conn,
676 dbus_message_unref(message);
679 message = dbus_message_new_method_call("org.bluez", adapter,
682 dbus_message_iter_init_append(message, &iter);
683 dbus_message_iter_append_basic(&iter,
684 DBUS_TYPE_STRING, &bdaddr);
686 reply = dbus_connection_send_with_reply_and_block(conn,
689 dbus_message_unref(message);
695 if (dbus_message_get_args(reply, NULL,
696 DBUS_TYPE_OBJECT_PATH, &object_path,
697 DBUS_TYPE_INVALID) == FALSE) {
698 dbus_message_unref(reply);
702 id = device_get_ieee1284_id(adapter, object_path);
704 dbus_message_unref(reply);
710 dbus_message_unref(reply);
716 * Usage: printer-uri job-id user title copies options [file]
720 int main(int argc, char *argv[])
724 unsigned short ctrl_psm, data_psm;
725 uint8_t channel, b[6];
726 char *ptr, str[3], device[18], service[12];
727 const char *uri, *cups_class;
728 int i, err, fd, copies, proto;
730 /* Make sure status messages are not buffered */
731 setbuf(stderr, NULL);
733 /* Make sure output is not buffered */
734 setbuf(stdout, NULL);
736 /* Ignore SIGPIPE signals */
738 sigset(SIGPIPE, SIG_IGN);
739 #elif defined(HAVE_SIGACTION)
740 memset(&action, 0, sizeof(action));
741 action.sa_handler = SIG_IGN;
742 sigaction(SIGPIPE, &action, NULL);
744 signal(SIGPIPE, SIG_IGN);
745 #endif /* HAVE_SIGSET */
748 if (list_printers() == TRUE)
749 return CUPS_BACKEND_OK;
751 return CUPS_BACKEND_FAILED;
752 } else if (argc == 3 && strcmp(argv[1], "--get-deviceid") == 0) {
753 if (bachk(argv[2]) < 0) {
754 fprintf(stderr, "Invalid Bluetooth address '%s'\n",
756 return CUPS_BACKEND_FAILED;
758 if (print_ieee1284(argv[2]) == FALSE)
759 return CUPS_BACKEND_FAILED;
760 return CUPS_BACKEND_OK;
763 if (argc < 6 || argc > 7) {
764 fprintf(stderr, "Usage: bluetooth job-id user title copies"
765 " options [file]\n");
766 fprintf(stderr, " bluetooth --get-deviceid [bdaddr]\n");
767 return CUPS_BACKEND_FAILED;
774 if ((fd = open(argv[6], O_RDONLY)) < 0) {
775 perror("ERROR: Unable to open print file");
776 return CUPS_BACKEND_FAILED;
778 copies = atoi(argv[4]);
781 uri = getenv("DEVICE_URI");
785 if (strncasecmp(uri, "bluetooth://", 12)) {
786 fprintf(stderr, "ERROR: No device URI found\n");
787 return CUPS_BACKEND_FAILED;
791 for (i = 0; i < 6; i++) {
792 strncpy(str, ptr, 2);
793 b[i] = (uint8_t) strtol(str, NULL, 16);
796 sprintf(device, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X",
797 b[0], b[1], b[2], b[3], b[4], b[5]);
799 str2ba(device, &bdaddr);
801 ptr = strchr(ptr, '/');
803 strncpy(service, ptr + 1, 12);
805 if (!strncasecmp(ptr + 1, "spp", 3))
807 else if (!strncasecmp(ptr + 1, "hcrp", 4))
812 strcpy(service, "auto");
816 cups_class = getenv("CLASS");
819 "DEBUG: %s device %s service %s fd %d copies %d class %s\n",
820 argv[0], device, service, fd, copies,
821 cups_class ? cups_class : "(none)");
823 fputs("STATE: +connecting-to-device\n", stderr);
826 sdp = sdp_connect(BDADDR_ANY, &bdaddr, SDP_RETRY_IF_BUSY);
828 fprintf(stderr, "ERROR: Can't open Bluetooth connection\n");
829 return CUPS_BACKEND_FAILED;
834 err = sdp_search_spp(sdp, &channel);
837 err = sdp_search_hcrp(sdp, &ctrl_psm, &data_psm);
841 err = sdp_search_hcrp(sdp, &ctrl_psm, &data_psm);
844 err = sdp_search_spp(sdp, &channel);
853 fputs("INFO: Unable to contact printer, queuing on "
854 "next printer in class...\n", stderr);
856 return CUPS_BACKEND_FAILED;
859 fprintf(stderr, "ERROR: Can't get service information\n");
866 err = spp_print(BDADDR_ANY, &bdaddr, channel,
867 fd, copies, cups_class);
870 err = hcrp_print(BDADDR_ANY, &bdaddr, ctrl_psm, data_psm,
871 fd, copies, cups_class);
874 err = CUPS_BACKEND_FAILED;
875 fprintf(stderr, "ERROR: Unsupported protocol\n");
879 if (err == CUPS_BACKEND_FAILED && cups_class) {
880 fputs("INFO: Unable to contact printer, queuing on "
881 "next printer in class...\n", stderr);
883 return CUPS_BACKEND_FAILED;
884 } else if (err == CUPS_BACKEND_RETRY) {
893 fprintf(stderr, "INFO: Ready to print\n");