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