Git init
[framework/connectivity/bluez.git] / cups / main.c
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
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 (!adapter_reply)
352                         return;
353
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);
358                         return;
359                 }
360         }
361
362         message = dbus_message_new_method_call("org.bluez", adapter,
363                                                         "org.bluez.Adapter",
364                                                         "FindDevice");
365         dbus_message_iter_init_append(message, &iter);
366         dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &bdaddr);
367
368         if (adapter_reply != NULL)
369                 dbus_message_unref(adapter_reply);
370
371         reply = dbus_connection_send_with_reply_and_block(conn,
372                                                         message, -1, NULL);
373
374         dbus_message_unref(message);
375
376         if (!reply) {
377                 message = dbus_message_new_method_call("org.bluez", adapter,
378                                                         "org.bluez.Adapter",
379                                                         "CreateDevice");
380                 dbus_message_iter_init_append(message, &iter);
381                 dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &bdaddr);
382
383                 reply = dbus_connection_send_with_reply_and_block(conn,
384                                                         message, -1, NULL);
385
386                 dbus_message_unref(message);
387
388                 if (!reply)
389                         return;
390         }
391
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);
396                 return;
397         }
398
399         id = device_get_ieee1284_id(adapter, object_path);
400         add_device_to_list(name, bdaddr, id);
401         g_free(id);
402
403         dbus_message_unref(reply);
404 }
405
406 static void discovery_completed(void)
407 {
408         g_slist_free(device_list);
409         device_list = NULL;
410
411         g_main_loop_quit(loop);
412 }
413
414 static void remote_device_disappeared(const char *bdaddr)
415 {
416         GSList *l;
417
418         for (l = device_list; l != NULL; l = l->next) {
419                 struct cups_device *device = l->data;
420
421                 if (strcmp(device->bdaddr, bdaddr) == 0) {
422                         g_free(device->name);
423                         g_free(device->bdaddr);
424                         g_free(device);
425                         device_list = g_slist_delete_link(device_list, l);
426                         return;
427                 }
428         }
429 }
430
431 static gboolean list_known_printers(const char *adapter)
432 {
433         DBusMessageIter reply_iter, iter_array;
434         DBusError error;
435         DBusMessage *message, *reply;
436
437         message = dbus_message_new_method_call("org.bluez", adapter,
438                                                 "org.bluez.Adapter",
439                                                 "ListDevices");
440         if (message == NULL)
441                 return FALSE;
442
443         dbus_error_init(&error);
444         reply = dbus_connection_send_with_reply_and_block(conn, message,
445                                                                 -1, &error);
446
447         dbus_message_unref(message);
448
449         if (dbus_error_is_set(&error))
450                 return FALSE;
451
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);
455                 return FALSE;
456         }
457
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;
462                 char *name = NULL;
463                 char *bdaddr = NULL;
464
465                 dbus_message_iter_get_basic(&iter_array, &object_path);
466                 if (device_is_printer(adapter, object_path, &name, &bdaddr)) {
467                         char *id;
468
469                         id = device_get_ieee1284_id(adapter, object_path);
470                         add_device_to_list(name, bdaddr, id);
471                         g_free(id);
472                 }
473                 g_free(name);
474                 g_free(bdaddr);
475                 dbus_message_iter_next(&iter_array);
476         }
477
478         dbus_message_unref(reply);
479
480         return FALSE;
481 }
482
483 static DBusHandlerResult filter_func(DBusConnection *connection,
484                                         DBusMessage *message, void *user_data)
485 {
486         if (dbus_message_is_signal(message, "org.bluez.Adapter",
487                                                 "DeviceFound")) {
488                 const char *adapter, *bdaddr;
489                 char *name;
490                 DBusMessageIter iter;
491
492                 dbus_message_iter_init(message, &iter);
493                 dbus_message_iter_get_basic(&iter, &bdaddr);
494                 dbus_message_iter_next(&iter);
495
496                 adapter = dbus_message_get_path(message);
497                 if (parse_device_properties(&iter, &name, NULL))
498                         remote_device_found(adapter, bdaddr, name);
499                 g_free (name);
500         } else if (dbus_message_is_signal(message, "org.bluez.Adapter",
501                                                 "DeviceDisappeared")) {
502                 const char *bdaddr;
503
504                 dbus_message_get_args(message, NULL,
505                                         DBUS_TYPE_STRING, &bdaddr,
506                                         DBUS_TYPE_INVALID);
507                 remote_device_disappeared(bdaddr);
508         } else if (dbus_message_is_signal(message, "org.bluez.Adapter",
509                                                 "PropertyChanged")) {
510                 DBusMessageIter iter, value_iter;
511                 const char *name;
512                 gboolean discovering;
513
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);
521
522                 if (discovering == FALSE && doing_disco) {
523                         doing_disco = FALSE;
524                         discovery_completed();
525                 }
526         }
527
528         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
529 }
530
531 static gboolean list_printers(void)
532 {
533         /* 1. Connect to the bus
534          * 2. Get the manager
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
539          */
540         DBusError error;
541         dbus_bool_t hcid_exists;
542         DBusMessage *reply, *message;
543         DBusMessageIter reply_iter;
544         char *adapter, *match;
545
546         conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
547         if (conn == NULL)
548                 return TRUE;
549
550         dbus_error_init(&error);
551         hcid_exists = dbus_bus_name_has_owner(conn, "org.bluez", &error);
552         if (dbus_error_is_set(&error))
553                 return TRUE;
554
555         if (!hcid_exists)
556                 return TRUE;
557
558         /* Get the default adapter */
559         message = dbus_message_new_method_call("org.bluez", "/",
560                                                 "org.bluez.Manager",
561                                                 "DefaultAdapter");
562         if (message == NULL) {
563                 dbus_connection_unref(conn);
564                 return FALSE;
565         }
566
567         reply = dbus_connection_send_with_reply_and_block(conn,
568                                                         message, -1, &error);
569
570         dbus_message_unref(message);
571
572         if (dbus_error_is_set(&error)) {
573                 dbus_connection_unref(conn);
574                 /* No adapter */
575                 return TRUE;
576         }
577
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);
583                 return FALSE;
584         }
585
586         dbus_message_iter_get_basic(&reply_iter, &adapter);
587         adapter = g_strdup(adapter);
588         dbus_message_unref(reply);
589
590         if (!dbus_connection_add_filter(conn, filter_func, adapter, g_free)) {
591                 g_free(adapter);
592                 dbus_connection_unref(conn);
593                 return FALSE;
594         }
595
596 #define MATCH_FORMAT                            \
597         "type='signal',"                        \
598         "interface='org.bluez.Adapter',"        \
599         "sender='org.bluez',"                   \
600         "path='%s'"
601
602         match = g_strdup_printf(MATCH_FORMAT, adapter);
603         dbus_bus_add_match(conn, match, &error);
604         g_free(match);
605
606         /* Add the the recent devices */
607         list_known_printers(adapter);
608
609         doing_disco = TRUE;
610         message = dbus_message_new_method_call("org.bluez", adapter,
611                                         "org.bluez.Adapter",
612                                         "StartDiscovery");
613
614         if (!dbus_connection_send_with_reply(conn, message, NULL, -1)) {
615                 dbus_message_unref(message);
616                 dbus_connection_unref(conn);
617                 g_free(adapter);
618                 return FALSE;
619         }
620         dbus_message_unref(message);
621
622         loop = g_main_loop_new(NULL, TRUE);
623         g_main_loop_run(loop);
624
625         g_free(adapter);
626         dbus_connection_unref(conn);
627
628         return TRUE;
629 }
630
631 static gboolean print_ieee1284(const char *bdaddr)
632 {
633         DBusMessage *message, *reply, *adapter_reply;
634         DBusMessageIter iter;
635         char *object_path = NULL;
636         char *adapter;
637         char *id;
638
639         adapter_reply = NULL;
640
641         conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
642         if (conn == NULL)
643                 return FALSE;
644
645         message = dbus_message_new_method_call("org.bluez", "/",
646                         "org.bluez.Manager",
647                         "DefaultAdapter");
648
649         adapter_reply = dbus_connection_send_with_reply_and_block(conn,
650                         message, -1, NULL);
651
652         dbus_message_unref(message);
653
654         if (!adapter_reply)
655                 return FALSE;
656
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);
661                 return FALSE;
662         }
663
664         message = dbus_message_new_method_call("org.bluez", adapter,
665                         "org.bluez.Adapter",
666                         "FindDevice");
667         dbus_message_iter_init_append(message, &iter);
668         dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &bdaddr);
669
670         if (adapter_reply != NULL)
671                 dbus_message_unref(adapter_reply);
672
673         reply = dbus_connection_send_with_reply_and_block(conn,
674                         message, -1, NULL);
675
676         dbus_message_unref(message);
677
678         if (!reply) {
679                 message = dbus_message_new_method_call("org.bluez", adapter,
680                                 "org.bluez.Adapter",
681                                 "CreateDevice");
682                 dbus_message_iter_init_append(message, &iter);
683                 dbus_message_iter_append_basic(&iter,
684                                 DBUS_TYPE_STRING, &bdaddr);
685
686                 reply = dbus_connection_send_with_reply_and_block(conn,
687                                 message, -1, NULL);
688
689                 dbus_message_unref(message);
690
691                 if (!reply)
692                         return FALSE;
693         }
694
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);
699                 return FALSE;
700         }
701
702         id = device_get_ieee1284_id(adapter, object_path);
703         if (id == NULL) {
704                 dbus_message_unref(reply);
705                 return FALSE;
706         }
707         printf("%s", id);
708         g_free(id);
709
710         dbus_message_unref(reply);
711
712         return TRUE;
713 }
714
715 /*
716  *  Usage: printer-uri job-id user title copies options [file]
717  *
718  */
719
720 int main(int argc, char *argv[])
721 {
722         sdp_session_t *sdp;
723         bdaddr_t bdaddr;
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;
729
730         /* Make sure status messages are not buffered */
731         setbuf(stderr, NULL);
732
733         /* Make sure output is not buffered */
734         setbuf(stdout, NULL);
735
736         /* Ignore SIGPIPE signals */
737 #ifdef HAVE_SIGSET
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);
743 #else
744         signal(SIGPIPE, SIG_IGN);
745 #endif /* HAVE_SIGSET */
746
747         if (argc == 1) {
748                 if (list_printers() == TRUE)
749                         return CUPS_BACKEND_OK;
750                 else
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",
755                                         argv[2]);
756                         return CUPS_BACKEND_FAILED;
757                 }
758                 if (print_ieee1284(argv[2]) == FALSE)
759                         return CUPS_BACKEND_FAILED;
760                 return CUPS_BACKEND_OK;
761         }
762
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;
768         }
769
770         if (argc == 6) {
771                 fd = 0;
772                 copies = 1;
773         } else {
774                 if ((fd = open(argv[6], O_RDONLY)) < 0) {
775                         perror("ERROR: Unable to open print file");
776                         return CUPS_BACKEND_FAILED;
777                 }
778                 copies = atoi(argv[4]);
779         }
780
781         uri = getenv("DEVICE_URI");
782         if (!uri)
783                 uri = argv[0];
784
785         if (strncasecmp(uri, "bluetooth://", 12)) {
786                 fprintf(stderr, "ERROR: No device URI found\n");
787                 return CUPS_BACKEND_FAILED;
788         }
789
790         ptr = argv[0] + 12;
791         for (i = 0; i < 6; i++) {
792                 strncpy(str, ptr, 2);
793                 b[i] = (uint8_t) strtol(str, NULL, 16);
794                 ptr += 2;
795         }
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]);
798
799         str2ba(device, &bdaddr);
800
801         ptr = strchr(ptr, '/');
802         if (ptr) {
803                 strncpy(service, ptr + 1, 12);
804
805                 if (!strncasecmp(ptr + 1, "spp", 3))
806                         proto = 1;
807                 else if (!strncasecmp(ptr + 1, "hcrp", 4))
808                         proto = 2;
809                 else
810                         proto = 0;
811         } else {
812                 strcpy(service, "auto");
813                 proto = 0;
814         }
815
816         cups_class = getenv("CLASS");
817
818         fprintf(stderr,
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)");
822
823         fputs("STATE: +connecting-to-device\n", stderr);
824
825 service_search:
826         sdp = sdp_connect(BDADDR_ANY, &bdaddr, SDP_RETRY_IF_BUSY);
827         if (!sdp) {
828                 fprintf(stderr, "ERROR: Can't open Bluetooth connection\n");
829                 return CUPS_BACKEND_FAILED;
830         }
831
832         switch (proto) {
833         case 1:
834                 err = sdp_search_spp(sdp, &channel);
835                 break;
836         case 2:
837                 err = sdp_search_hcrp(sdp, &ctrl_psm, &data_psm);
838                 break;
839         default:
840                 proto = 2;
841                 err = sdp_search_hcrp(sdp, &ctrl_psm, &data_psm);
842                 if (err) {
843                         proto = 1;
844                         err = sdp_search_spp(sdp, &channel);
845                 }
846                 break;
847         }
848
849         sdp_close(sdp);
850
851         if (err) {
852                 if (cups_class) {
853                         fputs("INFO: Unable to contact printer, queuing on "
854                                         "next printer in class...\n", stderr);
855                         sleep(5);
856                         return CUPS_BACKEND_FAILED;
857                 }
858                 sleep(20);
859                 fprintf(stderr, "ERROR: Can't get service information\n");
860                 goto service_search;
861         }
862
863 connect:
864         switch (proto) {
865         case 1:
866                 err = spp_print(BDADDR_ANY, &bdaddr, channel,
867                                                 fd, copies, cups_class);
868                 break;
869         case 2:
870                 err = hcrp_print(BDADDR_ANY, &bdaddr, ctrl_psm, data_psm,
871                                                 fd, copies, cups_class);
872                 break;
873         default:
874                 err = CUPS_BACKEND_FAILED;
875                 fprintf(stderr, "ERROR: Unsupported protocol\n");
876                 break;
877         }
878
879         if (err == CUPS_BACKEND_FAILED && cups_class) {
880                 fputs("INFO: Unable to contact printer, queuing on "
881                                         "next printer in class...\n", stderr);
882                 sleep(5);
883                 return CUPS_BACKEND_FAILED;
884         } else if (err == CUPS_BACKEND_RETRY) {
885                 sleep(20);
886                 goto connect;
887         }
888
889         if (fd != 0)
890                 close(fd);
891
892         if (!err)
893                 fprintf(stderr, "INFO: Ready to print\n");
894
895         return err;
896 }