]> git.ipfire.org Git - thirdparty/cups.git/blob - backend/dnssd.c
Fix build errors on Fedora.
[thirdparty/cups.git] / backend / dnssd.c
1 /*
2 * "$Id$"
3 *
4 * DNS-SD discovery backend for CUPS.
5 *
6 * Copyright 2008-2014 by Apple Inc.
7 *
8 * These coded instructions, statements, and computer programs are the
9 * property of Apple Inc. and are protected by Federal copyright
10 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
11 * "LICENSE" which should have been included with this file. If this
12 * file is missing or damaged, see the license at "http://www.cups.org/".
13 *
14 * This file is subject to the Apple OS-Developed Software exception.
15 */
16
17 /*
18 * Include necessary headers.
19 */
20
21 #include "backend-private.h"
22 #include <cups/array.h>
23 #ifdef HAVE_DNSSD
24 # include <dns_sd.h>
25 #endif /* HAVE_DNSSD */
26 #ifdef HAVE_AVAHI
27 # include <avahi-client/client.h>
28 # include <avahi-client/lookup.h>
29 # include <avahi-common/simple-watch.h>
30 # include <avahi-common/domain.h>
31 # include <avahi-common/error.h>
32 # include <avahi-common/malloc.h>
33 #define kDNSServiceMaxDomainName AVAHI_DOMAIN_NAME_MAX
34 #endif /* HAVE_AVAHI */
35
36
37 /*
38 * Device structure...
39 */
40
41 typedef enum
42 {
43 CUPS_DEVICE_PRINTER = 0, /* lpd://... */
44 CUPS_DEVICE_IPPS, /* ipps://... */
45 CUPS_DEVICE_IPP, /* ipp://... */
46 CUPS_DEVICE_FAX_IPP, /* ipp://... */
47 CUPS_DEVICE_PDL_DATASTREAM, /* socket://... */
48 CUPS_DEVICE_RIOUSBPRINT /* riousbprint://... */
49 } cups_devtype_t;
50
51
52 typedef struct
53 {
54 #ifdef HAVE_DNSSD
55 DNSServiceRef ref; /* Service reference for query */
56 #endif /* HAVE_DNSSD */
57 #ifdef HAVE_AVAHI
58 AvahiRecordBrowser *ref; /* Browser for query */
59 #endif /* HAVE_AVAHI */
60 char *name, /* Service name */
61 *domain, /* Domain name */
62 *fullName, /* Full name */
63 *make_and_model, /* Make and model from TXT record */
64 *device_id, /* 1284 device ID from TXT record */
65 *uuid; /* UUID from TXT record */
66 cups_devtype_t type; /* Device registration type */
67 int priority, /* Priority associated with type */
68 cups_shared, /* CUPS shared printer? */
69 sent; /* Did we list the device? */
70 } cups_device_t;
71
72
73 /*
74 * Local globals...
75 */
76
77 static int job_canceled = 0;
78 /* Set to 1 on SIGTERM */
79 #ifdef HAVE_AVAHI
80 static AvahiSimplePoll *simple_poll = NULL;
81 /* Poll information */
82 static int got_data = 0; /* Got data from poll? */
83 #endif /* HAVE_AVAHI */
84
85
86 /*
87 * Local functions...
88 */
89
90 #ifdef HAVE_DNSSD
91 static void browse_callback(DNSServiceRef sdRef,
92 DNSServiceFlags flags,
93 uint32_t interfaceIndex,
94 DNSServiceErrorType errorCode,
95 const char *serviceName,
96 const char *regtype,
97 const char *replyDomain, void *context)
98 __attribute__((nonnull(1,5,6,7,8)));
99 static void browse_local_callback(DNSServiceRef sdRef,
100 DNSServiceFlags flags,
101 uint32_t interfaceIndex,
102 DNSServiceErrorType errorCode,
103 const char *serviceName,
104 const char *regtype,
105 const char *replyDomain,
106 void *context)
107 __attribute__((nonnull(1,5,6,7,8)));
108 #endif /* HAVE_DNSSD */
109 #ifdef HAVE_AVAHI
110 static void browse_callback(AvahiServiceBrowser *browser,
111 AvahiIfIndex interface,
112 AvahiProtocol protocol,
113 AvahiBrowserEvent event,
114 const char *serviceName,
115 const char *regtype,
116 const char *replyDomain,
117 AvahiLookupResultFlags flags,
118 void *context);
119 static void client_callback(AvahiClient *client,
120 AvahiClientState state,
121 void *context);
122 #endif /* HAVE_AVAHI */
123
124 static int compare_devices(cups_device_t *a, cups_device_t *b);
125 static void exec_backend(char **argv) __attribute__((noreturn));
126 static cups_device_t *get_device(cups_array_t *devices,
127 const char *serviceName,
128 const char *regtype,
129 const char *replyDomain)
130 __attribute__((nonnull(1,2,3,4)));
131 #ifdef HAVE_DNSSD
132 static void query_callback(DNSServiceRef sdRef,
133 DNSServiceFlags flags,
134 uint32_t interfaceIndex,
135 DNSServiceErrorType errorCode,
136 const char *fullName, uint16_t rrtype,
137 uint16_t rrclass, uint16_t rdlen,
138 const void *rdata, uint32_t ttl,
139 void *context)
140 __attribute__((nonnull(1,5,9,11)));
141 #elif defined(HAVE_AVAHI)
142 static int poll_callback(struct pollfd *pollfds,
143 unsigned int num_pollfds, int timeout,
144 void *context);
145 static void query_callback(AvahiRecordBrowser *browser,
146 AvahiIfIndex interface,
147 AvahiProtocol protocol,
148 AvahiBrowserEvent event,
149 const char *name, uint16_t rrclass,
150 uint16_t rrtype, const void *rdata,
151 size_t rdlen,
152 AvahiLookupResultFlags flags,
153 void *context);
154 #endif /* HAVE_DNSSD */
155 static void sigterm_handler(int sig);
156 static void unquote(char *dst, const char *src, size_t dstsize)
157 __attribute__((nonnull(1,2)));
158
159
160 /*
161 * 'main()' - Browse for printers.
162 */
163
164 int /* O - Exit status */
165 main(int argc, /* I - Number of command-line args */
166 char *argv[]) /* I - Command-line arguments */
167 {
168 const char *name; /* Backend name */
169 cups_array_t *devices; /* Device array */
170 cups_device_t *device; /* Current device */
171 char uriName[1024]; /* Unquoted fullName for URI */
172 #ifdef HAVE_DNSSD
173 int fd; /* Main file descriptor */
174 fd_set input; /* Input set for select() */
175 struct timeval timeout; /* Timeout for select() */
176 DNSServiceRef main_ref, /* Main service reference */
177 fax_ipp_ref, /* IPP fax service reference */
178 ipp_ref, /* IPP service reference */
179 ipp_tls_ref, /* IPP w/TLS service reference */
180 ipps_ref, /* IPP service reference */
181 local_fax_ipp_ref, /* Local IPP fax service reference */
182 local_ipp_ref, /* Local IPP service reference */
183 local_ipp_tls_ref, /* Local IPP w/TLS service reference */
184 local_ipps_ref, /* Local IPP service reference */
185 local_printer_ref, /* Local LPD service reference */
186 pdl_datastream_ref, /* AppSocket service reference */
187 printer_ref, /* LPD service reference */
188 riousbprint_ref; /* Remote IO service reference */
189 #endif /* HAVE_DNSSD */
190 #ifdef HAVE_AVAHI
191 AvahiClient *client; /* Client information */
192 int error; /* Error code, if any */
193 #endif /* HAVE_AVAHI */
194 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
195 struct sigaction action; /* Actions for POSIX signals */
196 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
197
198
199 /*
200 * Don't buffer stderr, and catch SIGTERM...
201 */
202
203 setbuf(stderr, NULL);
204
205 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
206 sigset(SIGTERM, sigterm_handler);
207 #elif defined(HAVE_SIGACTION)
208 memset(&action, 0, sizeof(action));
209
210 sigemptyset(&action.sa_mask);
211 action.sa_handler = sigterm_handler;
212 sigaction(SIGTERM, &action, NULL);
213 #else
214 signal(SIGTERM, sigterm_handler);
215 #endif /* HAVE_SIGSET */
216
217 /*
218 * Check command-line...
219 */
220
221 if (argc >= 6)
222 exec_backend(argv);
223 else if (argc != 1)
224 {
225 _cupsLangPrintf(stderr,
226 _("Usage: %s job-id user title copies options [file]"),
227 argv[0]);
228 return (1);
229 }
230
231 /*
232 * Only do discovery when run as "dnssd"...
233 */
234
235 if ((name = strrchr(argv[0], '/')) != NULL)
236 name ++;
237 else
238 name = argv[0];
239
240 if (strcmp(name, "dnssd"))
241 return (0);
242
243 /*
244 * Create an array to track devices...
245 */
246
247 devices = cupsArrayNew((cups_array_func_t)compare_devices, NULL);
248
249 /*
250 * Browse for different kinds of printers...
251 */
252
253 #ifdef HAVE_DNSSD
254 if (DNSServiceCreateConnection(&main_ref) != kDNSServiceErr_NoError)
255 {
256 perror("ERROR: Unable to create service connection");
257 return (1);
258 }
259
260 fd = DNSServiceRefSockFD(main_ref);
261
262 fax_ipp_ref = main_ref;
263 DNSServiceBrowse(&fax_ipp_ref, kDNSServiceFlagsShareConnection, 0,
264 "_fax-ipp._tcp", NULL, browse_callback, devices);
265
266 ipp_ref = main_ref;
267 DNSServiceBrowse(&ipp_ref, kDNSServiceFlagsShareConnection, 0,
268 "_ipp._tcp", NULL, browse_callback, devices);
269
270 ipp_tls_ref = main_ref;
271 DNSServiceBrowse(&ipp_tls_ref, kDNSServiceFlagsShareConnection, 0,
272 "_ipp-tls._tcp", NULL, browse_callback, devices);
273
274 ipps_ref = main_ref;
275 DNSServiceBrowse(&ipps_ref, kDNSServiceFlagsShareConnection, 0,
276 "_ipps._tcp", NULL, browse_callback, devices);
277
278 local_fax_ipp_ref = main_ref;
279 DNSServiceBrowse(&local_fax_ipp_ref, kDNSServiceFlagsShareConnection,
280 kDNSServiceInterfaceIndexLocalOnly,
281 "_fax-ipp._tcp", NULL, browse_local_callback, devices);
282
283 local_ipp_ref = main_ref;
284 DNSServiceBrowse(&local_ipp_ref, kDNSServiceFlagsShareConnection,
285 kDNSServiceInterfaceIndexLocalOnly,
286 "_ipp._tcp", NULL, browse_local_callback, devices);
287
288 local_ipp_tls_ref = main_ref;
289 DNSServiceBrowse(&local_ipp_tls_ref, kDNSServiceFlagsShareConnection,
290 kDNSServiceInterfaceIndexLocalOnly,
291 "_ipp-tls._tcp", NULL, browse_local_callback, devices);
292
293 local_ipps_ref = main_ref;
294 DNSServiceBrowse(&local_ipps_ref, kDNSServiceFlagsShareConnection,
295 kDNSServiceInterfaceIndexLocalOnly,
296 "_ipps._tcp", NULL, browse_local_callback, devices);
297
298 local_printer_ref = main_ref;
299 DNSServiceBrowse(&local_printer_ref, kDNSServiceFlagsShareConnection,
300 kDNSServiceInterfaceIndexLocalOnly,
301 "_printer._tcp", NULL, browse_local_callback, devices);
302
303 pdl_datastream_ref = main_ref;
304 DNSServiceBrowse(&pdl_datastream_ref, kDNSServiceFlagsShareConnection, 0,
305 "_pdl-datastream._tcp", NULL, browse_callback, devices);
306
307 printer_ref = main_ref;
308 DNSServiceBrowse(&printer_ref, kDNSServiceFlagsShareConnection, 0,
309 "_printer._tcp", NULL, browse_callback, devices);
310
311 riousbprint_ref = main_ref;
312 DNSServiceBrowse(&riousbprint_ref, kDNSServiceFlagsShareConnection, 0,
313 "_riousbprint._tcp", NULL, browse_callback, devices);
314 #endif /* HAVE_DNSSD */
315
316 #ifdef HAVE_AVAHI
317 if ((simple_poll = avahi_simple_poll_new()) == NULL)
318 {
319 fputs("DEBUG: Unable to create Avahi simple poll object.\n", stderr);
320 return (1);
321 }
322
323 avahi_simple_poll_set_func(simple_poll, poll_callback, NULL);
324
325 client = avahi_client_new(avahi_simple_poll_get(simple_poll),
326 0, client_callback, simple_poll, &error);
327 if (!client)
328 {
329 fputs("DEBUG: Unable to create Avahi client.\n", stderr);
330 return (1);
331 }
332
333 avahi_service_browser_new(client, AVAHI_IF_UNSPEC,
334 AVAHI_PROTO_UNSPEC,
335 "_fax-ipp._tcp", NULL, 0,
336 browse_callback, devices);
337 avahi_service_browser_new(client, AVAHI_IF_UNSPEC,
338 AVAHI_PROTO_UNSPEC,
339 "_ipp._tcp", NULL, 0,
340 browse_callback, devices);
341 avahi_service_browser_new(client, AVAHI_IF_UNSPEC,
342 AVAHI_PROTO_UNSPEC,
343 "_ipp-tls._tcp", NULL, 0,
344 browse_callback, devices);
345 avahi_service_browser_new(client, AVAHI_IF_UNSPEC,
346 AVAHI_PROTO_UNSPEC,
347 "_ipps._tcp", NULL, 0,
348 browse_callback, devices);
349 avahi_service_browser_new(client, AVAHI_IF_UNSPEC,
350 AVAHI_PROTO_UNSPEC,
351 "_pdl-datastream._tcp",
352 NULL, 0,
353 browse_callback,
354 devices);
355 avahi_service_browser_new(client, AVAHI_IF_UNSPEC,
356 AVAHI_PROTO_UNSPEC,
357 "_printer._tcp", NULL, 0,
358 browse_callback, devices);
359 #endif /* HAVE_AVAHI */
360
361 /*
362 * Loop until we are killed...
363 */
364
365 while (!job_canceled)
366 {
367 int announce = 0; /* Announce printers? */
368
369 #ifdef HAVE_DNSSD
370 FD_ZERO(&input);
371 FD_SET(fd, &input);
372
373 timeout.tv_sec = 0;
374 timeout.tv_usec = 500000;
375
376 if (select(fd + 1, &input, NULL, NULL, &timeout) < 0)
377 continue;
378
379 if (FD_ISSET(fd, &input))
380 {
381 /*
382 * Process results of our browsing...
383 */
384
385 DNSServiceProcessResult(main_ref);
386 }
387 else
388 announce = 1;
389
390 #elif defined(HAVE_AVAHI)
391 got_data = 0;
392
393 if ((error = avahi_simple_poll_iterate(simple_poll, 500)) > 0)
394 {
395 /*
396 * We've been told to exit the loop. Perhaps the connection to
397 * Avahi failed.
398 */
399
400 break;
401 }
402
403 if (!got_data)
404 announce = 1;
405 #endif /* HAVE_DNSSD */
406
407 /* fprintf(stderr, "DEBUG: announce=%d\n", announce);*/
408
409 if (announce)
410 {
411 /*
412 * Announce any devices we've found...
413 */
414
415 #ifdef HAVE_DNSSD
416 DNSServiceErrorType status; /* DNS query status */
417 #endif /* HAVE_DNSSD */
418 cups_device_t *best; /* Best matching device */
419 char device_uri[1024]; /* Device URI */
420 int count; /* Number of queries */
421 int sent; /* Number of sent */
422
423 for (device = (cups_device_t *)cupsArrayFirst(devices),
424 best = NULL, count = 0, sent = 0;
425 device;
426 device = (cups_device_t *)cupsArrayNext(devices))
427 {
428 if (device->sent)
429 sent ++;
430
431 if (device->ref)
432 count ++;
433
434 if (!device->ref && !device->sent)
435 {
436 /*
437 * Found the device, now get the TXT record(s) for it...
438 */
439
440 if (count < 50)
441 {
442 fprintf(stderr, "DEBUG: Querying \"%s\"...\n", device->fullName);
443
444 #ifdef HAVE_DNSSD
445 device->ref = main_ref;
446
447 status = DNSServiceQueryRecord(&(device->ref),
448 kDNSServiceFlagsShareConnection,
449 0, device->fullName,
450 kDNSServiceType_TXT,
451 kDNSServiceClass_IN, query_callback,
452 device);
453 if (status != kDNSServiceErr_NoError)
454 fprintf(stderr,
455 "ERROR: Unable to query \"%s\" for TXT records: %d\n",
456 device->fullName, status);
457 /* Users never see this */
458 else
459 count ++;
460
461 #else
462 if ((device->ref = avahi_record_browser_new(client, AVAHI_IF_UNSPEC,
463 AVAHI_PROTO_UNSPEC,
464 device->fullName,
465 AVAHI_DNS_CLASS_IN,
466 AVAHI_DNS_TYPE_TXT,
467 0,
468 query_callback,
469 device)) == NULL)
470 fprintf(stderr,
471 "ERROR: Unable to query \"%s\" for TXT records: %s\n",
472 device->fullName,
473 avahi_strerror(avahi_client_errno(client)));
474 /* Users never see this */
475 else
476 count ++;
477 #endif /* HAVE_AVAHI */
478 }
479 }
480 else if (!device->sent)
481 {
482 #ifdef HAVE_DNSSD
483 /*
484 * Got the TXT records, now report the device...
485 */
486
487 DNSServiceRefDeallocate(device->ref);
488 #else
489 avahi_record_browser_free(device->ref);
490 #endif /* HAVE_DNSSD */
491
492 device->ref = NULL;
493
494 if (!best)
495 best = device;
496 else if (_cups_strcasecmp(best->name, device->name) ||
497 _cups_strcasecmp(best->domain, device->domain))
498 {
499 unquote(uriName, best->fullName, sizeof(uriName));
500
501 if (best->uuid)
502 httpAssembleURIf(HTTP_URI_CODING_ALL, device_uri,
503 sizeof(device_uri), "dnssd", NULL, uriName, 0,
504 best->cups_shared ? "/cups?uuid=%s" : "/?uuid=%s",
505 best->uuid);
506 else
507 httpAssembleURI(HTTP_URI_CODING_ALL, device_uri,
508 sizeof(device_uri), "dnssd", NULL, uriName, 0,
509 best->cups_shared ? "/cups" : "/");
510
511 cupsBackendReport("network", device_uri, best->make_and_model,
512 best->name, best->device_id, NULL);
513 best->sent = 1;
514 best = device;
515
516 sent ++;
517 }
518 else if (best->priority > device->priority ||
519 (best->priority == device->priority &&
520 best->type < device->type))
521 {
522 best->sent = 1;
523 best = device;
524
525 sent ++;
526 }
527 else
528 {
529 device->sent = 1;
530
531 sent ++;
532 }
533 }
534 }
535
536 if (best)
537 {
538 unquote(uriName, best->fullName, sizeof(uriName));
539
540 if (best->uuid)
541 httpAssembleURIf(HTTP_URI_CODING_ALL, device_uri,
542 sizeof(device_uri), "dnssd", NULL, uriName, 0,
543 best->cups_shared ? "/cups?uuid=%s" : "/?uuid=%s",
544 best->uuid);
545 else
546 httpAssembleURI(HTTP_URI_CODING_ALL, device_uri,
547 sizeof(device_uri), "dnssd", NULL, uriName, 0,
548 best->cups_shared ? "/cups" : "/");
549
550 cupsBackendReport("network", device_uri, best->make_and_model,
551 best->name, best->device_id, NULL);
552 best->sent = 1;
553 sent ++;
554 }
555
556 fprintf(stderr, "DEBUG: sent=%d, count=%d\n", sent, count);
557
558 if (sent == cupsArrayCount(devices))
559 break;
560 }
561 }
562
563 return (CUPS_BACKEND_OK);
564 }
565
566
567 #ifdef HAVE_DNSSD
568 /*
569 * 'browse_callback()' - Browse devices.
570 */
571
572 static void
573 browse_callback(
574 DNSServiceRef sdRef, /* I - Service reference */
575 DNSServiceFlags flags, /* I - Option flags */
576 uint32_t interfaceIndex, /* I - Interface number */
577 DNSServiceErrorType errorCode, /* I - Error, if any */
578 const char *serviceName, /* I - Name of service/device */
579 const char *regtype, /* I - Type of service */
580 const char *replyDomain, /* I - Service domain */
581 void *context) /* I - Devices array */
582 {
583 fprintf(stderr, "DEBUG2: browse_callback(sdRef=%p, flags=%x, "
584 "interfaceIndex=%d, errorCode=%d, serviceName=\"%s\", "
585 "regtype=\"%s\", replyDomain=\"%s\", context=%p)\n",
586 sdRef, flags, interfaceIndex, errorCode,
587 serviceName, regtype, replyDomain, context);
588
589 /*
590 * Only process "add" data...
591 */
592
593 if (errorCode != kDNSServiceErr_NoError || !(flags & kDNSServiceFlagsAdd))
594 return;
595
596 /*
597 * Get the device...
598 */
599
600 get_device((cups_array_t *)context, serviceName, regtype, replyDomain);
601 }
602
603
604 /*
605 * 'browse_local_callback()' - Browse local devices.
606 */
607
608 static void
609 browse_local_callback(
610 DNSServiceRef sdRef, /* I - Service reference */
611 DNSServiceFlags flags, /* I - Option flags */
612 uint32_t interfaceIndex, /* I - Interface number */
613 DNSServiceErrorType errorCode, /* I - Error, if any */
614 const char *serviceName, /* I - Name of service/device */
615 const char *regtype, /* I - Type of service */
616 const char *replyDomain, /* I - Service domain */
617 void *context) /* I - Devices array */
618 {
619 cups_device_t *device; /* Device */
620
621
622 fprintf(stderr, "DEBUG2: browse_local_callback(sdRef=%p, flags=%x, "
623 "interfaceIndex=%d, errorCode=%d, serviceName=\"%s\", "
624 "regtype=\"%s\", replyDomain=\"%s\", context=%p)\n",
625 sdRef, flags, interfaceIndex, errorCode,
626 serviceName, regtype, replyDomain, context);
627
628 /*
629 * Only process "add" data...
630 */
631
632 if (errorCode != kDNSServiceErr_NoError || !(flags & kDNSServiceFlagsAdd))
633 return;
634
635 /*
636 * Get the device...
637 */
638
639 device = get_device((cups_array_t *)context, serviceName, regtype,
640 replyDomain);
641
642 /*
643 * Hide locally-registered devices...
644 */
645
646 fprintf(stderr, "DEBUG: Hiding local printer \"%s\"...\n",
647 device->fullName);
648 device->sent = 1;
649 }
650 #endif /* HAVE_DNSSD */
651
652
653 #ifdef HAVE_AVAHI
654 /*
655 * 'browse_callback()' - Browse devices.
656 */
657
658 static void
659 browse_callback(
660 AvahiServiceBrowser *browser, /* I - Browser */
661 AvahiIfIndex interface, /* I - Interface index (unused) */
662 AvahiProtocol protocol, /* I - Network protocol (unused) */
663 AvahiBrowserEvent event, /* I - What happened */
664 const char *name, /* I - Service name */
665 const char *type, /* I - Registration type */
666 const char *domain, /* I - Domain */
667 AvahiLookupResultFlags flags, /* I - Flags */
668 void *context) /* I - Devices array */
669 {
670 AvahiClient *client = avahi_service_browser_get_client(browser);
671 /* Client information */
672
673
674 (void)interface;
675 (void)protocol;
676 (void)context;
677
678 switch (event)
679 {
680 case AVAHI_BROWSER_FAILURE:
681 fprintf(stderr, "DEBUG: browse_callback: %s\n",
682 avahi_strerror(avahi_client_errno(client)));
683 avahi_simple_poll_quit(simple_poll);
684 break;
685
686 case AVAHI_BROWSER_NEW:
687 /*
688 * This object is new on the network.
689 */
690
691 if (flags & AVAHI_LOOKUP_RESULT_LOCAL)
692 {
693 /*
694 * This comes from the local machine so ignore it.
695 */
696
697 fprintf(stderr, "DEBUG: Ignoring local service %s.\n", name);
698 }
699 else
700 {
701 /*
702 * Create a device entry for it if it doesn't yet exist.
703 */
704
705 get_device((cups_array_t *)context, name, type, domain);
706 }
707 break;
708
709 case AVAHI_BROWSER_REMOVE:
710 case AVAHI_BROWSER_ALL_FOR_NOW:
711 case AVAHI_BROWSER_CACHE_EXHAUSTED:
712 break;
713 }
714 }
715
716
717 /*
718 * 'client_callback()' - Avahi client callback function.
719 */
720
721 static void
722 client_callback(
723 AvahiClient *client, /* I - Client information (unused) */
724 AvahiClientState state, /* I - Current state */
725 void *context) /* I - User data (unused) */
726 {
727 (void)client;
728 (void)context;
729
730 /*
731 * If the connection drops, quit.
732 */
733
734 if (state == AVAHI_CLIENT_FAILURE)
735 {
736 fputs("DEBUG: Avahi connection failed.\n", stderr);
737 avahi_simple_poll_quit(simple_poll);
738 }
739 }
740 #endif /* HAVE_AVAHI */
741
742
743 /*
744 * 'compare_devices()' - Compare two devices.
745 */
746
747 static int /* O - Result of comparison */
748 compare_devices(cups_device_t *a, /* I - First device */
749 cups_device_t *b) /* I - Second device */
750 {
751 return (strcmp(a->name, b->name));
752 }
753
754
755 /*
756 * 'exec_backend()' - Execute the backend that corresponds to the
757 * resolved service name.
758 */
759
760 static void
761 exec_backend(char **argv) /* I - Command-line arguments */
762 {
763 const char *resolved_uri, /* Resolved device URI */
764 *cups_serverbin; /* Location of programs */
765 char scheme[1024], /* Scheme from URI */
766 *ptr, /* Pointer into scheme */
767 filename[1024]; /* Backend filename */
768
769
770 /*
771 * Resolve the device URI...
772 */
773
774 job_canceled = -1;
775
776 while ((resolved_uri = cupsBackendDeviceURI(argv)) == NULL)
777 {
778 _cupsLangPrintFilter(stderr, "INFO", _("Unable to locate printer."));
779 sleep(10);
780
781 if (getenv("CLASS") != NULL)
782 exit(CUPS_BACKEND_FAILED);
783 }
784
785 /*
786 * Extract the scheme from the URI...
787 */
788
789 strlcpy(scheme, resolved_uri, sizeof(scheme));
790 if ((ptr = strchr(scheme, ':')) != NULL)
791 *ptr = '\0';
792
793 /*
794 * Get the filename of the backend...
795 */
796
797 if ((cups_serverbin = getenv("CUPS_SERVERBIN")) == NULL)
798 cups_serverbin = CUPS_SERVERBIN;
799
800 snprintf(filename, sizeof(filename), "%s/backend/%s", cups_serverbin, scheme);
801
802 /*
803 * Overwrite the device URI and run the new backend...
804 */
805
806 setenv("DEVICE_URI", resolved_uri, 1);
807
808 argv[0] = (char *)resolved_uri;
809
810 fprintf(stderr, "DEBUG: Executing backend \"%s\"...\n", filename);
811
812 execv(filename, argv);
813
814 fprintf(stderr, "ERROR: Unable to execute backend \"%s\": %s\n", filename,
815 strerror(errno));
816 exit(CUPS_BACKEND_STOP);
817 }
818
819
820 /*
821 * 'device_type()' - Get DNS-SD type enumeration from string.
822 */
823
824 static cups_devtype_t /* O - Device type */
825 device_type(const char *regtype) /* I - Service registration type */
826 {
827 #ifdef HAVE_AVAHI
828 if (!strcmp(regtype, "_ipp._tcp"))
829 return (CUPS_DEVICE_IPP);
830 else if (!strcmp(regtype, "_ipps._tcp") ||
831 !strcmp(regtype, "_ipp-tls._tcp"))
832 return (CUPS_DEVICE_IPPS);
833 else if (!strcmp(regtype, "_fax-ipp._tcp"))
834 return (CUPS_DEVICE_FAX_IPP);
835 else if (!strcmp(regtype, "_printer._tcp"))
836 return (CUPS_DEVICE_PDL_DATASTREAM);
837 #else
838 if (!strcmp(regtype, "_ipp._tcp."))
839 return (CUPS_DEVICE_IPP);
840 else if (!strcmp(regtype, "_ipps._tcp.") ||
841 !strcmp(regtype, "_ipp-tls._tcp."))
842 return (CUPS_DEVICE_IPPS);
843 else if (!strcmp(regtype, "_fax-ipp._tcp."))
844 return (CUPS_DEVICE_FAX_IPP);
845 else if (!strcmp(regtype, "_printer._tcp."))
846 return (CUPS_DEVICE_PRINTER);
847 else if (!strcmp(regtype, "_pdl-datastream._tcp."))
848 return (CUPS_DEVICE_PDL_DATASTREAM);
849 #endif /* HAVE_AVAHI */
850
851 return (CUPS_DEVICE_RIOUSBPRINT);
852 }
853
854
855 /*
856 * 'get_device()' - Create or update a device.
857 */
858
859 static cups_device_t * /* O - Device */
860 get_device(cups_array_t *devices, /* I - Device array */
861 const char *serviceName, /* I - Name of service/device */
862 const char *regtype, /* I - Type of service */
863 const char *replyDomain) /* I - Service domain */
864 {
865 cups_device_t key, /* Search key */
866 *device; /* Device */
867 char fullName[kDNSServiceMaxDomainName];
868 /* Full name for query */
869
870
871 /*
872 * See if this is a new device...
873 */
874
875 key.name = (char *)serviceName;
876 key.type = device_type(regtype);
877
878 for (device = cupsArrayFind(devices, &key);
879 device;
880 device = cupsArrayNext(devices))
881 if (_cups_strcasecmp(device->name, key.name))
882 break;
883 else if (device->type == key.type)
884 {
885 if (!_cups_strcasecmp(device->domain, "local.") &&
886 _cups_strcasecmp(device->domain, replyDomain))
887 {
888 /*
889 * Update the .local listing to use the "global" domain name instead.
890 * The backend will try local lookups first, then the global domain name.
891 */
892
893 free(device->domain);
894 device->domain = strdup(replyDomain);
895
896 #ifdef HAVE_DNSSD
897 DNSServiceConstructFullName(fullName, device->name, regtype,
898 replyDomain);
899 #else /* HAVE_AVAHI */
900 avahi_service_name_join(fullName, kDNSServiceMaxDomainName,
901 serviceName, regtype, replyDomain);
902 #endif /* HAVE_DNSSD */
903
904 free(device->fullName);
905 device->fullName = strdup(fullName);
906 }
907
908 return (device);
909 }
910
911 /*
912 * Yes, add the device...
913 */
914
915 fprintf(stderr, "DEBUG: Found \"%s.%s%s\"...\n", serviceName, regtype,
916 replyDomain);
917
918 device = calloc(sizeof(cups_device_t), 1);
919 device->name = strdup(serviceName);
920 device->domain = strdup(replyDomain);
921 device->type = key.type;
922 device->priority = 50;
923
924 cupsArrayAdd(devices, device);
925
926 /*
927 * Set the "full name" of this service, which is used for queries...
928 */
929
930 #ifdef HAVE_DNSSD
931 DNSServiceConstructFullName(fullName, serviceName, regtype, replyDomain);
932 #else /* HAVE_AVAHI */
933 avahi_service_name_join(fullName, kDNSServiceMaxDomainName,
934 serviceName, regtype, replyDomain);
935 #endif /* HAVE_DNSSD */
936
937 device->fullName = strdup(fullName);
938
939 return (device);
940 }
941
942
943 #ifdef HAVE_AVAHI
944 /*
945 * 'poll_callback()' - Wait for input on the specified file descriptors.
946 *
947 * Note: This function is needed because avahi_simple_poll_iterate is broken
948 * and always uses a timeout of 0 (!) milliseconds.
949 * (Avahi Ticket #364)
950 */
951
952 static int /* O - Number of file descriptors matching */
953 poll_callback(
954 struct pollfd *pollfds, /* I - File descriptors */
955 unsigned int num_pollfds, /* I - Number of file descriptors */
956 int timeout, /* I - Timeout in milliseconds (unused) */
957 void *context) /* I - User data (unused) */
958 {
959 int val; /* Return value */
960
961
962 (void)timeout;
963 (void)context;
964
965 val = poll(pollfds, num_pollfds, 500);
966
967 if (val < 0)
968 fprintf(stderr, "DEBUG: poll_callback: %s\n", strerror(errno));
969 else if (val > 0)
970 got_data = 1;
971
972 return (val);
973 }
974 #endif /* HAVE_AVAHI */
975
976
977 #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
978 # ifdef HAVE_DNSSD
979 /*
980 * 'query_callback()' - Process query data.
981 */
982
983 static void
984 query_callback(
985 DNSServiceRef sdRef, /* I - Service reference */
986 DNSServiceFlags flags, /* I - Data flags */
987 uint32_t interfaceIndex, /* I - Interface */
988 DNSServiceErrorType errorCode, /* I - Error, if any */
989 const char *fullName, /* I - Full service name */
990 uint16_t rrtype, /* I - Record type */
991 uint16_t rrclass, /* I - Record class */
992 uint16_t rdlen, /* I - Length of record data */
993 const void *rdata, /* I - Record data */
994 uint32_t ttl, /* I - Time-to-live */
995 void *context) /* I - Device */
996 {
997 # else
998 /*
999 * 'query_callback()' - Process query data.
1000 */
1001
1002 static void
1003 query_callback(
1004 AvahiRecordBrowser *browser, /* I - Record browser */
1005 AvahiIfIndex interfaceIndex,
1006 /* I - Interface index (unused) */
1007 AvahiProtocol protocol, /* I - Network protocol (unused) */
1008 AvahiBrowserEvent event, /* I - What happened? */
1009 const char *fullName, /* I - Service name */
1010 uint16_t rrclass, /* I - Record class */
1011 uint16_t rrtype, /* I - Record type */
1012 const void *rdata, /* I - TXT record */
1013 size_t rdlen, /* I - Length of TXT record */
1014 AvahiLookupResultFlags flags, /* I - Flags */
1015 void *context) /* I - Device */
1016 {
1017 AvahiClient *client = avahi_record_browser_get_client(browser);
1018 /* Client information */
1019 # endif /* HAVE_DNSSD */
1020 char *ptr; /* Pointer into string */
1021 cups_device_t *device = (cups_device_t *)context;
1022 /* Device */
1023 const uint8_t *data, /* Pointer into data */
1024 *datanext, /* Next key/value pair */
1025 *dataend; /* End of entire TXT record */
1026 uint8_t datalen; /* Length of current key/value pair */
1027 char key[256], /* Key string */
1028 value[256], /* Value string */
1029 make_and_model[512], /* Manufacturer and model */
1030 model[256], /* Model */
1031 pdl[256], /* PDL */
1032 device_id[2048]; /* 1284 device ID */
1033
1034
1035 # ifdef HAVE_DNSSD
1036 fprintf(stderr, "DEBUG2: query_callback(sdRef=%p, flags=%x, "
1037 "interfaceIndex=%d, errorCode=%d, fullName=\"%s\", "
1038 "rrtype=%u, rrclass=%u, rdlen=%u, rdata=%p, ttl=%u, "
1039 "context=%p)\n",
1040 sdRef, flags, interfaceIndex, errorCode,
1041 fullName ? fullName : "(null)", rrtype, rrclass, rdlen, rdata, ttl,
1042 context);
1043
1044 /*
1045 * Only process "add" data...
1046 */
1047
1048 if (errorCode != kDNSServiceErr_NoError || !(flags & kDNSServiceFlagsAdd))
1049 return;
1050
1051 # else
1052 fprintf(stderr, "DEBUG2: query_callback(browser=%p, interfaceIndex=%d, "
1053 "protocol=%d, event=%d, fullName=\"%s\", rrclass=%u, "
1054 "rrtype=%u, rdata=%p, rdlen=%u, flags=%x, context=%p)\n",
1055 browser, interfaceIndex, protocol, event,
1056 fullName ? fullName : "(null)", rrclass, rrtype, rdata,
1057 (unsigned)rdlen, flags, context);
1058
1059 /*
1060 * Only process "add" data...
1061 */
1062
1063 if (event != AVAHI_BROWSER_NEW)
1064 {
1065 if (event == AVAHI_BROWSER_FAILURE)
1066 fprintf(stderr, "ERROR: %s\n",
1067 avahi_strerror(avahi_client_errno(client)));
1068
1069 return;
1070 }
1071 # endif /* HAVE_DNSSD */
1072
1073 /*
1074 * Pull out the priority and make and model from the TXT
1075 * record and save it...
1076 */
1077
1078 device_id[0] = '\0';
1079 make_and_model[0] = '\0';
1080 pdl[0] = '\0';
1081
1082 strlcpy(model, "Unknown", sizeof(model));
1083
1084 for (data = rdata, dataend = data + rdlen;
1085 data < dataend;
1086 data = datanext)
1087 {
1088 /*
1089 * Read a key/value pair starting with an 8-bit length. Since the
1090 * length is 8 bits and the size of the key/value buffers is 256, we
1091 * don't need to check for overflow...
1092 */
1093
1094 datalen = *data++;
1095
1096 if (!datalen || (data + datalen) > dataend)
1097 break;
1098
1099 datanext = data + datalen;
1100
1101 for (ptr = key; data < datanext && *data != '='; data ++)
1102 *ptr++ = (char)*data;
1103 *ptr = '\0';
1104
1105 if (data < datanext && *data == '=')
1106 {
1107 data ++;
1108
1109 if (data < datanext)
1110 memcpy(value, data, (size_t)(datanext - data));
1111 value[datanext - data] = '\0';
1112
1113 fprintf(stderr, "DEBUG2: query_callback: \"%s=%s\".\n",
1114 key, value);
1115 }
1116 else
1117 {
1118 fprintf(stderr, "DEBUG2: query_callback: \"%s\" with no value.\n",
1119 key);
1120 continue;
1121 }
1122
1123 if (!_cups_strncasecmp(key, "usb_", 4))
1124 {
1125 /*
1126 * Add USB device ID information...
1127 */
1128
1129 ptr = device_id + strlen(device_id);
1130 snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%s:%s;", key + 4, value);
1131 }
1132
1133 if (!_cups_strcasecmp(key, "usb_MFG") || !_cups_strcasecmp(key, "usb_MANU") ||
1134 !_cups_strcasecmp(key, "usb_MANUFACTURER"))
1135 strlcpy(make_and_model, value, sizeof(make_and_model));
1136 else if (!_cups_strcasecmp(key, "usb_MDL") || !_cups_strcasecmp(key, "usb_MODEL"))
1137 strlcpy(model, value, sizeof(model));
1138 else if (!_cups_strcasecmp(key, "product") && !strstr(value, "Ghostscript"))
1139 {
1140 if (value[0] == '(')
1141 {
1142 /*
1143 * Strip parenthesis...
1144 */
1145
1146 if ((ptr = value + strlen(value) - 1) > value && *ptr == ')')
1147 *ptr = '\0';
1148
1149 strlcpy(model, value + 1, sizeof(model));
1150 }
1151 else
1152 strlcpy(model, value, sizeof(model));
1153 }
1154 else if (!_cups_strcasecmp(key, "ty"))
1155 {
1156 strlcpy(model, value, sizeof(model));
1157
1158 if ((ptr = strchr(model, ',')) != NULL)
1159 *ptr = '\0';
1160 }
1161 else if (!_cups_strcasecmp(key, "pdl"))
1162 strlcpy(pdl, value, sizeof(pdl));
1163 else if (!_cups_strcasecmp(key, "priority"))
1164 device->priority = atoi(value);
1165 else if ((device->type == CUPS_DEVICE_IPP ||
1166 device->type == CUPS_DEVICE_IPPS ||
1167 device->type == CUPS_DEVICE_PRINTER) &&
1168 !_cups_strcasecmp(key, "printer-type"))
1169 {
1170 /*
1171 * This is a CUPS printer!
1172 */
1173
1174 device->cups_shared = 1;
1175
1176 if (device->type == CUPS_DEVICE_PRINTER)
1177 device->sent = 1;
1178 }
1179 else if (!_cups_strcasecmp(key, "UUID"))
1180 device->uuid = strdup(value);
1181 }
1182
1183 if (device->device_id)
1184 free(device->device_id);
1185
1186 if (!device_id[0] && strcmp(model, "Unknown"))
1187 {
1188 if (make_and_model[0])
1189 snprintf(device_id, sizeof(device_id), "MFG:%s;MDL:%s;",
1190 make_and_model, model);
1191 else if (!_cups_strncasecmp(model, "designjet ", 10))
1192 snprintf(device_id, sizeof(device_id), "MFG:HP;MDL:%s", model + 10);
1193 else if (!_cups_strncasecmp(model, "stylus ", 7))
1194 snprintf(device_id, sizeof(device_id), "MFG:EPSON;MDL:%s", model + 7);
1195 else if ((ptr = strchr(model, ' ')) != NULL)
1196 {
1197 /*
1198 * Assume the first word is the make...
1199 */
1200
1201 memcpy(make_and_model, model, (size_t)(ptr - model));
1202 make_and_model[ptr - model] = '\0';
1203
1204 snprintf(device_id, sizeof(device_id), "MFG:%s;MDL:%s",
1205 make_and_model, ptr + 1);
1206 }
1207 }
1208
1209 if (device_id[0] &&
1210 !strstr(device_id, "CMD:") &&
1211 !strstr(device_id, "COMMAND SET:") &&
1212 (strstr(pdl, "application/pdf") ||
1213 strstr(pdl, "application/postscript") ||
1214 strstr(pdl, "application/vnd.hp-PCL") ||
1215 strstr(pdl, "image/")))
1216 {
1217 value[0] = '\0';
1218 if (strstr(pdl, "application/pdf"))
1219 strlcat(value, ",PDF", sizeof(value));
1220 if (strstr(pdl, "application/postscript"))
1221 strlcat(value, ",PS", sizeof(value));
1222 if (strstr(pdl, "application/vnd.hp-PCL"))
1223 strlcat(value, ",PCL", sizeof(value));
1224 for (ptr = strstr(pdl, "image/"); ptr; ptr = strstr(ptr, "image/"))
1225 {
1226 char *valptr = value + strlen(value);
1227 /* Pointer into value */
1228
1229 if (valptr < (value + sizeof(value) - 1))
1230 *valptr++ = ',';
1231
1232 ptr += 6;
1233 while (isalnum(*ptr & 255) || *ptr == '-' || *ptr == '.')
1234 {
1235 if (isalnum(*ptr & 255) && valptr < (value + sizeof(value) - 1))
1236 *valptr++ = (char)toupper(*ptr++ & 255);
1237 else
1238 break;
1239 }
1240
1241 *valptr = '\0';
1242 }
1243
1244 ptr = device_id + strlen(device_id);
1245 snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "CMD:%s;", value + 1);
1246 }
1247
1248 if (device_id[0])
1249 device->device_id = strdup(device_id);
1250 else
1251 device->device_id = NULL;
1252
1253 if (device->make_and_model)
1254 free(device->make_and_model);
1255
1256 if (make_and_model[0])
1257 {
1258 strlcat(make_and_model, " ", sizeof(make_and_model));
1259 strlcat(make_and_model, model, sizeof(make_and_model));
1260
1261 device->make_and_model = strdup(make_and_model);
1262 }
1263 else
1264 device->make_and_model = strdup(model);
1265 }
1266 #endif /* HAVE_DNSSD || HAVE_AVAHI */
1267
1268
1269 /*
1270 * 'sigterm_handler()' - Handle termination signals.
1271 */
1272
1273 static void
1274 sigterm_handler(int sig) /* I - Signal number (unused) */
1275 {
1276 (void)sig;
1277
1278 if (job_canceled)
1279 exit(CUPS_BACKEND_OK);
1280 else
1281 job_canceled = 1;
1282 }
1283
1284
1285 /*
1286 * 'unquote()' - Unquote a name string.
1287 */
1288
1289 static void
1290 unquote(char *dst, /* I - Destination buffer */
1291 const char *src, /* I - Source string */
1292 size_t dstsize) /* I - Size of destination buffer */
1293 {
1294 char *dstend = dst + dstsize - 1; /* End of destination buffer */
1295
1296
1297 while (*src && dst < dstend)
1298 {
1299 if (*src == '\\')
1300 {
1301 src ++;
1302 if (isdigit(src[0] & 255) && isdigit(src[1] & 255) &&
1303 isdigit(src[2] & 255))
1304 {
1305 *dst++ = ((((src[0] - '0') * 10) + src[1] - '0') * 10) + src[2] - '0';
1306 src += 3;
1307 }
1308 else
1309 *dst++ = *src++;
1310 }
1311 else
1312 *dst++ = *src ++;
1313 }
1314
1315 *dst = '\0';
1316 }
1317
1318
1319 /*
1320 * End of "$Id$".
1321 */