4 * DNS-SD discovery backend for CUPS.
6 * Copyright 2008-2015 by Apple Inc.
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/".
14 * This file is subject to the Apple OS-Developed Software exception.
18 * Include necessary headers.
21 #include "backend-private.h"
22 #include <cups/array.h>
25 #endif /* HAVE_DNSSD */
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 */
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://... */
55 DNSServiceRef ref
; /* Service reference for query */
56 #endif /* HAVE_DNSSD */
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? */
77 static int job_canceled
= 0;
78 /* Set to 1 on SIGTERM */
80 static AvahiSimplePoll
*simple_poll
= NULL
;
81 /* Poll information */
82 static int got_data
= 0; /* Got data from poll? */
83 static int browsers
= 0; /* Number of running browsers */
84 #endif /* HAVE_AVAHI */
92 static void browse_callback(DNSServiceRef sdRef
,
93 DNSServiceFlags flags
,
94 uint32_t interfaceIndex
,
95 DNSServiceErrorType errorCode
,
96 const char *serviceName
,
98 const char *replyDomain
, void *context
)
99 __attribute__((nonnull(1,5,6,7,8)));
100 static void browse_local_callback(DNSServiceRef sdRef
,
101 DNSServiceFlags flags
,
102 uint32_t interfaceIndex
,
103 DNSServiceErrorType errorCode
,
104 const char *serviceName
,
106 const char *replyDomain
,
108 __attribute__((nonnull(1,5,6,7,8)));
109 #endif /* HAVE_DNSSD */
111 static void browse_callback(AvahiServiceBrowser
*browser
,
112 AvahiIfIndex interface
,
113 AvahiProtocol protocol
,
114 AvahiBrowserEvent event
,
115 const char *serviceName
,
117 const char *replyDomain
,
118 AvahiLookupResultFlags flags
,
120 static void client_callback(AvahiClient
*client
,
121 AvahiClientState state
,
123 #endif /* HAVE_AVAHI */
125 static int compare_devices(cups_device_t
*a
, cups_device_t
*b
);
126 static void exec_backend(char **argv
) __attribute__((noreturn
));
127 static cups_device_t
*get_device(cups_array_t
*devices
,
128 const char *serviceName
,
130 const char *replyDomain
)
131 __attribute__((nonnull(1,2,3,4)));
133 static void query_callback(DNSServiceRef sdRef
,
134 DNSServiceFlags flags
,
135 uint32_t interfaceIndex
,
136 DNSServiceErrorType errorCode
,
137 const char *fullName
, uint16_t rrtype
,
138 uint16_t rrclass
, uint16_t rdlen
,
139 const void *rdata
, uint32_t ttl
,
141 __attribute__((nonnull(1,5,9,11)));
142 #elif defined(HAVE_AVAHI)
143 static int poll_callback(struct pollfd
*pollfds
,
144 unsigned int num_pollfds
, int timeout
,
146 static void query_callback(AvahiRecordBrowser
*browser
,
147 AvahiIfIndex interface
,
148 AvahiProtocol protocol
,
149 AvahiBrowserEvent event
,
150 const char *name
, uint16_t rrclass
,
151 uint16_t rrtype
, const void *rdata
,
153 AvahiLookupResultFlags flags
,
155 #endif /* HAVE_DNSSD */
156 static void sigterm_handler(int sig
);
157 static void unquote(char *dst
, const char *src
, size_t dstsize
)
158 __attribute__((nonnull(1,2)));
162 * 'main()' - Browse for printers.
165 int /* O - Exit status */
166 main(int argc
, /* I - Number of command-line args */
167 char *argv
[]) /* I - Command-line arguments */
169 const char *name
; /* Backend name */
170 cups_array_t
*devices
; /* Device array */
171 cups_device_t
*device
; /* Current device */
172 char uriName
[1024]; /* Unquoted fullName for URI */
174 int fd
; /* Main file descriptor */
175 fd_set input
; /* Input set for select() */
176 struct timeval timeout
; /* Timeout for select() */
177 DNSServiceRef main_ref
, /* Main service reference */
178 fax_ipp_ref
, /* IPP fax service reference */
179 ipp_ref
, /* IPP service reference */
180 ipp_tls_ref
, /* IPP w/TLS service reference */
181 ipps_ref
, /* IPP service reference */
182 local_fax_ipp_ref
, /* Local IPP fax service reference */
183 local_ipp_ref
, /* Local IPP service reference */
184 local_ipp_tls_ref
, /* Local IPP w/TLS service reference */
185 local_ipps_ref
, /* Local IPP service reference */
186 local_printer_ref
, /* Local LPD service reference */
187 pdl_datastream_ref
, /* AppSocket service reference */
188 printer_ref
, /* LPD service reference */
189 riousbprint_ref
; /* Remote IO service reference */
190 #endif /* HAVE_DNSSD */
192 AvahiClient
*client
; /* Client information */
193 int error
; /* Error code, if any */
194 #endif /* HAVE_AVAHI */
195 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
196 struct sigaction action
; /* Actions for POSIX signals */
197 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
201 * Don't buffer stderr, and catch SIGTERM...
204 setbuf(stderr
, NULL
);
206 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
207 sigset(SIGTERM
, sigterm_handler
);
208 #elif defined(HAVE_SIGACTION)
209 memset(&action
, 0, sizeof(action
));
211 sigemptyset(&action
.sa_mask
);
212 action
.sa_handler
= sigterm_handler
;
213 sigaction(SIGTERM
, &action
, NULL
);
215 signal(SIGTERM
, sigterm_handler
);
216 #endif /* HAVE_SIGSET */
219 * Check command-line...
226 _cupsLangPrintf(stderr
,
227 _("Usage: %s job-id user title copies options [file]"),
233 * Only do discovery when run as "dnssd"...
236 if ((name
= strrchr(argv
[0], '/')) != NULL
)
241 if (strcmp(name
, "dnssd"))
245 * Create an array to track devices...
248 devices
= cupsArrayNew((cups_array_func_t
)compare_devices
, NULL
);
251 * Browse for different kinds of printers...
255 if (DNSServiceCreateConnection(&main_ref
) != kDNSServiceErr_NoError
)
257 perror("ERROR: Unable to create service connection");
261 fd
= DNSServiceRefSockFD(main_ref
);
263 fax_ipp_ref
= main_ref
;
264 DNSServiceBrowse(&fax_ipp_ref
, kDNSServiceFlagsShareConnection
, 0,
265 "_fax-ipp._tcp", NULL
, browse_callback
, devices
);
268 DNSServiceBrowse(&ipp_ref
, kDNSServiceFlagsShareConnection
, 0,
269 "_ipp._tcp", NULL
, browse_callback
, devices
);
271 ipp_tls_ref
= main_ref
;
272 DNSServiceBrowse(&ipp_tls_ref
, kDNSServiceFlagsShareConnection
, 0,
273 "_ipp-tls._tcp", NULL
, browse_callback
, devices
);
276 DNSServiceBrowse(&ipps_ref
, kDNSServiceFlagsShareConnection
, 0,
277 "_ipps._tcp", NULL
, browse_callback
, devices
);
279 local_fax_ipp_ref
= main_ref
;
280 DNSServiceBrowse(&local_fax_ipp_ref
, kDNSServiceFlagsShareConnection
,
281 kDNSServiceInterfaceIndexLocalOnly
,
282 "_fax-ipp._tcp", NULL
, browse_local_callback
, devices
);
284 local_ipp_ref
= main_ref
;
285 DNSServiceBrowse(&local_ipp_ref
, kDNSServiceFlagsShareConnection
,
286 kDNSServiceInterfaceIndexLocalOnly
,
287 "_ipp._tcp", NULL
, browse_local_callback
, devices
);
289 local_ipp_tls_ref
= main_ref
;
290 DNSServiceBrowse(&local_ipp_tls_ref
, kDNSServiceFlagsShareConnection
,
291 kDNSServiceInterfaceIndexLocalOnly
,
292 "_ipp-tls._tcp", NULL
, browse_local_callback
, devices
);
294 local_ipps_ref
= main_ref
;
295 DNSServiceBrowse(&local_ipps_ref
, kDNSServiceFlagsShareConnection
,
296 kDNSServiceInterfaceIndexLocalOnly
,
297 "_ipps._tcp", NULL
, browse_local_callback
, devices
);
299 local_printer_ref
= main_ref
;
300 DNSServiceBrowse(&local_printer_ref
, kDNSServiceFlagsShareConnection
,
301 kDNSServiceInterfaceIndexLocalOnly
,
302 "_printer._tcp", NULL
, browse_local_callback
, devices
);
304 pdl_datastream_ref
= main_ref
;
305 DNSServiceBrowse(&pdl_datastream_ref
, kDNSServiceFlagsShareConnection
, 0,
306 "_pdl-datastream._tcp", NULL
, browse_callback
, devices
);
308 printer_ref
= main_ref
;
309 DNSServiceBrowse(&printer_ref
, kDNSServiceFlagsShareConnection
, 0,
310 "_printer._tcp", NULL
, browse_callback
, devices
);
312 riousbprint_ref
= main_ref
;
313 DNSServiceBrowse(&riousbprint_ref
, kDNSServiceFlagsShareConnection
, 0,
314 "_riousbprint._tcp", NULL
, browse_callback
, devices
);
315 #endif /* HAVE_DNSSD */
318 if ((simple_poll
= avahi_simple_poll_new()) == NULL
)
320 fputs("DEBUG: Unable to create Avahi simple poll object.\n", stderr
);
324 avahi_simple_poll_set_func(simple_poll
, poll_callback
, NULL
);
326 client
= avahi_client_new(avahi_simple_poll_get(simple_poll
),
327 0, client_callback
, simple_poll
, &error
);
330 fputs("DEBUG: Unable to create Avahi client.\n", stderr
);
335 avahi_service_browser_new(client
, AVAHI_IF_UNSPEC
,
337 "_fax-ipp._tcp", NULL
, 0,
338 browse_callback
, devices
);
339 avahi_service_browser_new(client
, AVAHI_IF_UNSPEC
,
341 "_ipp._tcp", NULL
, 0,
342 browse_callback
, devices
);
343 avahi_service_browser_new(client
, AVAHI_IF_UNSPEC
,
345 "_ipp-tls._tcp", NULL
, 0,
346 browse_callback
, devices
);
347 avahi_service_browser_new(client
, AVAHI_IF_UNSPEC
,
349 "_ipps._tcp", NULL
, 0,
350 browse_callback
, devices
);
351 avahi_service_browser_new(client
, AVAHI_IF_UNSPEC
,
353 "_pdl-datastream._tcp",
357 avahi_service_browser_new(client
, AVAHI_IF_UNSPEC
,
359 "_printer._tcp", NULL
, 0,
360 browse_callback
, devices
);
361 #endif /* HAVE_AVAHI */
364 * Loop until we are killed...
367 while (!job_canceled
)
369 int announce
= 0; /* Announce printers? */
376 timeout
.tv_usec
= 500000;
378 if (select(fd
+ 1, &input
, NULL
, NULL
, &timeout
) < 0)
381 if (FD_ISSET(fd
, &input
))
384 * Process results of our browsing...
387 DNSServiceProcessResult(main_ref
);
392 #elif defined(HAVE_AVAHI)
395 if ((error
= avahi_simple_poll_iterate(simple_poll
, 500)) > 0)
398 * We've been told to exit the loop. Perhaps the connection to
407 #endif /* HAVE_DNSSD */
409 /* fprintf(stderr, "DEBUG: announce=%d\n", announce);*/
414 * Announce any devices we've found...
418 DNSServiceErrorType status
; /* DNS query status */
419 #endif /* HAVE_DNSSD */
420 cups_device_t
*best
; /* Best matching device */
421 char device_uri
[1024]; /* Device URI */
422 int count
; /* Number of queries */
423 int sent
; /* Number of sent */
425 for (device
= (cups_device_t
*)cupsArrayFirst(devices
),
426 best
= NULL
, count
= 0, sent
= 0;
428 device
= (cups_device_t
*)cupsArrayNext(devices
))
436 if (!device
->ref
&& !device
->sent
)
439 * Found the device, now get the TXT record(s) for it...
444 fprintf(stderr
, "DEBUG: Querying \"%s\"...\n", device
->fullName
);
447 device
->ref
= main_ref
;
449 status
= DNSServiceQueryRecord(&(device
->ref
),
450 kDNSServiceFlagsShareConnection
,
453 kDNSServiceClass_IN
, query_callback
,
455 if (status
!= kDNSServiceErr_NoError
)
457 "ERROR: Unable to query \"%s\" for TXT records: %d\n",
458 device
->fullName
, status
);
459 /* Users never see this */
464 if ((device
->ref
= avahi_record_browser_new(client
, AVAHI_IF_UNSPEC
,
473 "ERROR: Unable to query \"%s\" for TXT records: %s\n",
475 avahi_strerror(avahi_client_errno(client
)));
476 /* Users never see this */
479 #endif /* HAVE_AVAHI */
482 else if (!device
->sent
)
486 * Got the TXT records, now report the device...
489 DNSServiceRefDeallocate(device
->ref
);
491 avahi_record_browser_free(device
->ref
);
492 #endif /* HAVE_DNSSD */
498 else if (_cups_strcasecmp(best
->name
, device
->name
) ||
499 _cups_strcasecmp(best
->domain
, device
->domain
))
501 unquote(uriName
, best
->fullName
, sizeof(uriName
));
504 httpAssembleURIf(HTTP_URI_CODING_ALL
, device_uri
,
505 sizeof(device_uri
), "dnssd", NULL
, uriName
, 0,
506 best
->cups_shared
? "/cups?uuid=%s" : "/?uuid=%s",
509 httpAssembleURI(HTTP_URI_CODING_ALL
, device_uri
,
510 sizeof(device_uri
), "dnssd", NULL
, uriName
, 0,
511 best
->cups_shared
? "/cups" : "/");
513 cupsBackendReport("network", device_uri
, best
->make_and_model
,
514 best
->name
, best
->device_id
, NULL
);
520 else if (best
->priority
> device
->priority
||
521 (best
->priority
== device
->priority
&&
522 best
->type
< device
->type
))
540 unquote(uriName
, best
->fullName
, sizeof(uriName
));
543 httpAssembleURIf(HTTP_URI_CODING_ALL
, device_uri
,
544 sizeof(device_uri
), "dnssd", NULL
, uriName
, 0,
545 best
->cups_shared
? "/cups?uuid=%s" : "/?uuid=%s",
548 httpAssembleURI(HTTP_URI_CODING_ALL
, device_uri
,
549 sizeof(device_uri
), "dnssd", NULL
, uriName
, 0,
550 best
->cups_shared
? "/cups" : "/");
552 cupsBackendReport("network", device_uri
, best
->make_and_model
,
553 best
->name
, best
->device_id
, NULL
);
558 fprintf(stderr
, "DEBUG: sent=%d, count=%d\n", sent
, count
);
561 if (sent
== cupsArrayCount(devices
) && browsers
== 0)
563 if (sent
== cupsArrayCount(devices
))
564 #endif /* HAVE_AVAHI */
569 return (CUPS_BACKEND_OK
);
575 * 'browse_callback()' - Browse devices.
580 DNSServiceRef sdRef
, /* I - Service reference */
581 DNSServiceFlags flags
, /* I - Option flags */
582 uint32_t interfaceIndex
, /* I - Interface number */
583 DNSServiceErrorType errorCode
, /* I - Error, if any */
584 const char *serviceName
, /* I - Name of service/device */
585 const char *regtype
, /* I - Type of service */
586 const char *replyDomain
, /* I - Service domain */
587 void *context
) /* I - Devices array */
589 fprintf(stderr
, "DEBUG2: browse_callback(sdRef=%p, flags=%x, "
590 "interfaceIndex=%d, errorCode=%d, serviceName=\"%s\", "
591 "regtype=\"%s\", replyDomain=\"%s\", context=%p)\n",
592 sdRef
, flags
, interfaceIndex
, errorCode
,
593 serviceName
, regtype
, replyDomain
, context
);
596 * Only process "add" data...
599 if (errorCode
!= kDNSServiceErr_NoError
|| !(flags
& kDNSServiceFlagsAdd
))
606 get_device((cups_array_t
*)context
, serviceName
, regtype
, replyDomain
);
611 * 'browse_local_callback()' - Browse local devices.
615 browse_local_callback(
616 DNSServiceRef sdRef
, /* I - Service reference */
617 DNSServiceFlags flags
, /* I - Option flags */
618 uint32_t interfaceIndex
, /* I - Interface number */
619 DNSServiceErrorType errorCode
, /* I - Error, if any */
620 const char *serviceName
, /* I - Name of service/device */
621 const char *regtype
, /* I - Type of service */
622 const char *replyDomain
, /* I - Service domain */
623 void *context
) /* I - Devices array */
625 cups_device_t
*device
; /* Device */
628 fprintf(stderr
, "DEBUG2: browse_local_callback(sdRef=%p, flags=%x, "
629 "interfaceIndex=%d, errorCode=%d, serviceName=\"%s\", "
630 "regtype=\"%s\", replyDomain=\"%s\", context=%p)\n",
631 sdRef
, flags
, interfaceIndex
, errorCode
,
632 serviceName
, regtype
, replyDomain
, context
);
635 * Only process "add" data...
638 if (errorCode
!= kDNSServiceErr_NoError
|| !(flags
& kDNSServiceFlagsAdd
))
645 device
= get_device((cups_array_t
*)context
, serviceName
, regtype
,
649 * Hide locally-registered devices...
652 fprintf(stderr
, "DEBUG: Hiding local printer \"%s\"...\n",
656 #endif /* HAVE_DNSSD */
661 * 'browse_callback()' - Browse devices.
666 AvahiServiceBrowser
*browser
, /* I - Browser */
667 AvahiIfIndex interface
, /* I - Interface index (unused) */
668 AvahiProtocol protocol
, /* I - Network protocol (unused) */
669 AvahiBrowserEvent event
, /* I - What happened */
670 const char *name
, /* I - Service name */
671 const char *type
, /* I - Registration type */
672 const char *domain
, /* I - Domain */
673 AvahiLookupResultFlags flags
, /* I - Flags */
674 void *context
) /* I - Devices array */
676 AvahiClient
*client
= avahi_service_browser_get_client(browser
);
677 /* Client information */
686 case AVAHI_BROWSER_FAILURE
:
687 fprintf(stderr
, "DEBUG: browse_callback: %s\n",
688 avahi_strerror(avahi_client_errno(client
)));
689 avahi_simple_poll_quit(simple_poll
);
692 case AVAHI_BROWSER_NEW
:
694 * This object is new on the network.
697 if (flags
& AVAHI_LOOKUP_RESULT_LOCAL
)
700 * This comes from the local machine so ignore it.
703 fprintf(stderr
, "DEBUG: Ignoring local service %s.\n", name
);
708 * Create a device entry for it if it doesn't yet exist.
711 get_device((cups_array_t
*)context
, name
, type
, domain
);
715 case AVAHI_BROWSER_REMOVE
:
716 case AVAHI_BROWSER_CACHE_EXHAUSTED
:
719 case AVAHI_BROWSER_ALL_FOR_NOW
:
727 * 'client_callback()' - Avahi client callback function.
732 AvahiClient
*client
, /* I - Client information (unused) */
733 AvahiClientState state
, /* I - Current state */
734 void *context
) /* I - User data (unused) */
740 * If the connection drops, quit.
743 if (state
== AVAHI_CLIENT_FAILURE
)
745 fputs("DEBUG: Avahi connection failed.\n", stderr
);
746 avahi_simple_poll_quit(simple_poll
);
749 #endif /* HAVE_AVAHI */
753 * 'compare_devices()' - Compare two devices.
756 static int /* O - Result of comparison */
757 compare_devices(cups_device_t
*a
, /* I - First device */
758 cups_device_t
*b
) /* I - Second device */
760 return (strcmp(a
->name
, b
->name
));
765 * 'exec_backend()' - Execute the backend that corresponds to the
766 * resolved service name.
770 exec_backend(char **argv
) /* I - Command-line arguments */
772 const char *resolved_uri
, /* Resolved device URI */
773 *cups_serverbin
; /* Location of programs */
774 char scheme
[1024], /* Scheme from URI */
775 *ptr
, /* Pointer into scheme */
776 filename
[1024]; /* Backend filename */
780 * Resolve the device URI...
785 while ((resolved_uri
= cupsBackendDeviceURI(argv
)) == NULL
)
787 _cupsLangPrintFilter(stderr
, "INFO", _("Unable to locate printer."));
790 if (getenv("CLASS") != NULL
)
791 exit(CUPS_BACKEND_FAILED
);
795 * Extract the scheme from the URI...
798 strlcpy(scheme
, resolved_uri
, sizeof(scheme
));
799 if ((ptr
= strchr(scheme
, ':')) != NULL
)
803 * Get the filename of the backend...
806 if ((cups_serverbin
= getenv("CUPS_SERVERBIN")) == NULL
)
807 cups_serverbin
= CUPS_SERVERBIN
;
809 snprintf(filename
, sizeof(filename
), "%s/backend/%s", cups_serverbin
, scheme
);
812 * Overwrite the device URI and run the new backend...
815 setenv("DEVICE_URI", resolved_uri
, 1);
817 argv
[0] = (char *)resolved_uri
;
819 fprintf(stderr
, "DEBUG: Executing backend \"%s\"...\n", filename
);
821 execv(filename
, argv
);
823 fprintf(stderr
, "ERROR: Unable to execute backend \"%s\": %s\n", filename
,
825 exit(CUPS_BACKEND_STOP
);
830 * 'device_type()' - Get DNS-SD type enumeration from string.
833 static cups_devtype_t
/* O - Device type */
834 device_type(const char *regtype
) /* I - Service registration type */
837 if (!strcmp(regtype
, "_ipp._tcp"))
838 return (CUPS_DEVICE_IPP
);
839 else if (!strcmp(regtype
, "_ipps._tcp") ||
840 !strcmp(regtype
, "_ipp-tls._tcp"))
841 return (CUPS_DEVICE_IPPS
);
842 else if (!strcmp(regtype
, "_fax-ipp._tcp"))
843 return (CUPS_DEVICE_FAX_IPP
);
844 else if (!strcmp(regtype
, "_printer._tcp"))
845 return (CUPS_DEVICE_PDL_DATASTREAM
);
847 if (!strcmp(regtype
, "_ipp._tcp."))
848 return (CUPS_DEVICE_IPP
);
849 else if (!strcmp(regtype
, "_ipps._tcp.") ||
850 !strcmp(regtype
, "_ipp-tls._tcp."))
851 return (CUPS_DEVICE_IPPS
);
852 else if (!strcmp(regtype
, "_fax-ipp._tcp."))
853 return (CUPS_DEVICE_FAX_IPP
);
854 else if (!strcmp(regtype
, "_printer._tcp."))
855 return (CUPS_DEVICE_PRINTER
);
856 else if (!strcmp(regtype
, "_pdl-datastream._tcp."))
857 return (CUPS_DEVICE_PDL_DATASTREAM
);
858 #endif /* HAVE_AVAHI */
860 return (CUPS_DEVICE_RIOUSBPRINT
);
865 * 'get_device()' - Create or update a device.
868 static cups_device_t
* /* O - Device */
869 get_device(cups_array_t
*devices
, /* I - Device array */
870 const char *serviceName
, /* I - Name of service/device */
871 const char *regtype
, /* I - Type of service */
872 const char *replyDomain
) /* I - Service domain */
874 cups_device_t key
, /* Search key */
875 *device
; /* Device */
876 char fullName
[kDNSServiceMaxDomainName
];
877 /* Full name for query */
881 * See if this is a new device...
884 key
.name
= (char *)serviceName
;
885 key
.type
= device_type(regtype
);
887 for (device
= cupsArrayFind(devices
, &key
);
889 device
= cupsArrayNext(devices
))
890 if (_cups_strcasecmp(device
->name
, key
.name
))
892 else if (device
->type
== key
.type
)
894 if (!_cups_strcasecmp(device
->domain
, "local.") &&
895 _cups_strcasecmp(device
->domain
, replyDomain
))
898 * Update the .local listing to use the "global" domain name instead.
899 * The backend will try local lookups first, then the global domain name.
902 free(device
->domain
);
903 device
->domain
= strdup(replyDomain
);
906 DNSServiceConstructFullName(fullName
, device
->name
, regtype
,
908 #else /* HAVE_AVAHI */
909 avahi_service_name_join(fullName
, kDNSServiceMaxDomainName
,
910 serviceName
, regtype
, replyDomain
);
911 #endif /* HAVE_DNSSD */
913 free(device
->fullName
);
914 device
->fullName
= strdup(fullName
);
921 * Yes, add the device...
924 device
= calloc(sizeof(cups_device_t
), 1);
925 device
->name
= strdup(serviceName
);
926 device
->domain
= strdup(replyDomain
);
927 device
->type
= key
.type
;
928 device
->priority
= 50;
930 cupsArrayAdd(devices
, device
);
933 * Set the "full name" of this service, which is used for queries...
937 DNSServiceConstructFullName(fullName
, serviceName
, regtype
, replyDomain
);
938 #else /* HAVE_AVAHI */
939 avahi_service_name_join(fullName
, kDNSServiceMaxDomainName
, serviceName
, regtype
, replyDomain
);
940 #endif /* HAVE_DNSSD */
942 device
->fullName
= strdup(fullName
);
950 * 'poll_callback()' - Wait for input on the specified file descriptors.
952 * Note: This function is needed because avahi_simple_poll_iterate is broken
953 * and always uses a timeout of 0 (!) milliseconds.
954 * (Avahi Ticket #364)
957 static int /* O - Number of file descriptors matching */
959 struct pollfd
*pollfds
, /* I - File descriptors */
960 unsigned int num_pollfds
, /* I - Number of file descriptors */
961 int timeout
, /* I - Timeout in milliseconds (unused) */
962 void *context
) /* I - User data (unused) */
964 int val
; /* Return value */
970 val
= poll(pollfds
, num_pollfds
, 500);
973 fprintf(stderr
, "DEBUG: poll_callback: %s\n", strerror(errno
));
979 #endif /* HAVE_AVAHI */
982 #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
985 * 'query_callback()' - Process query data.
990 DNSServiceRef sdRef
, /* I - Service reference */
991 DNSServiceFlags flags
, /* I - Data flags */
992 uint32_t interfaceIndex
, /* I - Interface */
993 DNSServiceErrorType errorCode
, /* I - Error, if any */
994 const char *fullName
, /* I - Full service name */
995 uint16_t rrtype
, /* I - Record type */
996 uint16_t rrclass
, /* I - Record class */
997 uint16_t rdlen
, /* I - Length of record data */
998 const void *rdata
, /* I - Record data */
999 uint32_t ttl
, /* I - Time-to-live */
1000 void *context
) /* I - Device */
1004 * 'query_callback()' - Process query data.
1009 AvahiRecordBrowser
*browser
, /* I - Record browser */
1010 AvahiIfIndex interfaceIndex
,
1011 /* I - Interface index (unused) */
1012 AvahiProtocol protocol
, /* I - Network protocol (unused) */
1013 AvahiBrowserEvent event
, /* I - What happened? */
1014 const char *fullName
, /* I - Service name */
1015 uint16_t rrclass
, /* I - Record class */
1016 uint16_t rrtype
, /* I - Record type */
1017 const void *rdata
, /* I - TXT record */
1018 size_t rdlen
, /* I - Length of TXT record */
1019 AvahiLookupResultFlags flags
, /* I - Flags */
1020 void *context
) /* I - Device */
1022 AvahiClient
*client
= avahi_record_browser_get_client(browser
);
1023 /* Client information */
1024 # endif /* HAVE_DNSSD */
1025 char *ptr
; /* Pointer into string */
1026 cups_device_t
*device
= (cups_device_t
*)context
;
1028 const uint8_t *data
, /* Pointer into data */
1029 *datanext
, /* Next key/value pair */
1030 *dataend
; /* End of entire TXT record */
1031 uint8_t datalen
; /* Length of current key/value pair */
1032 char key
[256], /* Key string */
1033 value
[256], /* Value string */
1034 make_and_model
[512], /* Manufacturer and model */
1035 model
[256], /* Model */
1037 device_id
[2048]; /* 1284 device ID */
1041 fprintf(stderr
, "DEBUG2: query_callback(sdRef=%p, flags=%x, "
1042 "interfaceIndex=%d, errorCode=%d, fullName=\"%s\", "
1043 "rrtype=%u, rrclass=%u, rdlen=%u, rdata=%p, ttl=%u, "
1045 sdRef
, flags
, interfaceIndex
, errorCode
, fullName
, rrtype
, rrclass
, rdlen
, rdata
, ttl
, context
);
1048 * Only process "add" data...
1051 if (errorCode
!= kDNSServiceErr_NoError
|| !(flags
& kDNSServiceFlagsAdd
))
1055 fprintf(stderr
, "DEBUG2: query_callback(browser=%p, interfaceIndex=%d, "
1056 "protocol=%d, event=%d, fullName=\"%s\", rrclass=%u, "
1057 "rrtype=%u, rdata=%p, rdlen=%u, flags=%x, context=%p)\n",
1058 browser
, interfaceIndex
, protocol
, event
, fullName
, rrclass
, rrtype
, rdata
, (unsigned)rdlen
, flags
, context
);
1061 * Only process "add" data...
1064 if (event
!= AVAHI_BROWSER_NEW
)
1066 if (event
== AVAHI_BROWSER_FAILURE
)
1067 fprintf(stderr
, "ERROR: %s\n",
1068 avahi_strerror(avahi_client_errno(client
)));
1072 # endif /* HAVE_DNSSD */
1075 * Pull out the priority and make and model from the TXT
1076 * record and save it...
1079 device_id
[0] = '\0';
1080 make_and_model
[0] = '\0';
1083 strlcpy(model
, "Unknown", sizeof(model
));
1085 for (data
= rdata
, dataend
= data
+ rdlen
;
1090 * Read a key/value pair starting with an 8-bit length. Since the
1091 * length is 8 bits and the size of the key/value buffers is 256, we
1092 * don't need to check for overflow...
1097 if (!datalen
|| (data
+ datalen
) > dataend
)
1100 datanext
= data
+ datalen
;
1102 for (ptr
= key
; data
< datanext
&& *data
!= '='; data
++)
1103 *ptr
++ = (char)*data
;
1106 if (data
< datanext
&& *data
== '=')
1110 if (data
< datanext
)
1111 memcpy(value
, data
, (size_t)(datanext
- data
));
1112 value
[datanext
- data
] = '\0';
1114 fprintf(stderr
, "DEBUG2: query_callback: \"%s=%s\".\n",
1119 fprintf(stderr
, "DEBUG2: query_callback: \"%s\" with no value.\n",
1124 if (!_cups_strncasecmp(key
, "usb_", 4))
1127 * Add USB device ID information...
1130 ptr
= device_id
+ strlen(device_id
);
1131 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%s:%s;", key
+ 4, value
);
1134 if (!_cups_strcasecmp(key
, "usb_MFG") || !_cups_strcasecmp(key
, "usb_MANU") ||
1135 !_cups_strcasecmp(key
, "usb_MANUFACTURER"))
1136 strlcpy(make_and_model
, value
, sizeof(make_and_model
));
1137 else if (!_cups_strcasecmp(key
, "usb_MDL") || !_cups_strcasecmp(key
, "usb_MODEL"))
1138 strlcpy(model
, value
, sizeof(model
));
1139 else if (!_cups_strcasecmp(key
, "product") && !strstr(value
, "Ghostscript"))
1141 if (value
[0] == '(')
1144 * Strip parenthesis...
1147 if ((ptr
= value
+ strlen(value
) - 1) > value
&& *ptr
== ')')
1150 strlcpy(model
, value
+ 1, sizeof(model
));
1153 strlcpy(model
, value
, sizeof(model
));
1155 else if (!_cups_strcasecmp(key
, "ty"))
1157 strlcpy(model
, value
, sizeof(model
));
1159 if ((ptr
= strchr(model
, ',')) != NULL
)
1162 else if (!_cups_strcasecmp(key
, "pdl"))
1163 strlcpy(pdl
, value
, sizeof(pdl
));
1164 else if (!_cups_strcasecmp(key
, "priority"))
1165 device
->priority
= atoi(value
);
1166 else if ((device
->type
== CUPS_DEVICE_IPP
||
1167 device
->type
== CUPS_DEVICE_IPPS
||
1168 device
->type
== CUPS_DEVICE_PRINTER
) &&
1169 !_cups_strcasecmp(key
, "printer-type"))
1172 * This is a CUPS printer!
1175 device
->cups_shared
= 1;
1177 if (device
->type
== CUPS_DEVICE_PRINTER
)
1180 else if (!_cups_strcasecmp(key
, "UUID"))
1181 device
->uuid
= strdup(value
);
1184 if (device
->device_id
)
1185 free(device
->device_id
);
1187 if (!device_id
[0] && strcmp(model
, "Unknown"))
1189 if (make_and_model
[0])
1190 snprintf(device_id
, sizeof(device_id
), "MFG:%s;MDL:%s;",
1191 make_and_model
, model
);
1192 else if (!_cups_strncasecmp(model
, "designjet ", 10))
1193 snprintf(device_id
, sizeof(device_id
), "MFG:HP;MDL:%s", model
+ 10);
1194 else if (!_cups_strncasecmp(model
, "stylus ", 7))
1195 snprintf(device_id
, sizeof(device_id
), "MFG:EPSON;MDL:%s", model
+ 7);
1196 else if ((ptr
= strchr(model
, ' ')) != NULL
)
1199 * Assume the first word is the make...
1202 memcpy(make_and_model
, model
, (size_t)(ptr
- model
));
1203 make_and_model
[ptr
- model
] = '\0';
1205 snprintf(device_id
, sizeof(device_id
), "MFG:%s;MDL:%s",
1206 make_and_model
, ptr
+ 1);
1211 !strstr(device_id
, "CMD:") &&
1212 !strstr(device_id
, "COMMAND SET:") &&
1213 (strstr(pdl
, "application/pdf") ||
1214 strstr(pdl
, "application/postscript") ||
1215 strstr(pdl
, "application/vnd.hp-PCL") ||
1216 strstr(pdl
, "image/")))
1219 if (strstr(pdl
, "application/pdf"))
1220 strlcat(value
, ",PDF", sizeof(value
));
1221 if (strstr(pdl
, "application/postscript"))
1222 strlcat(value
, ",PS", sizeof(value
));
1223 if (strstr(pdl
, "application/vnd.hp-PCL"))
1224 strlcat(value
, ",PCL", sizeof(value
));
1225 for (ptr
= strstr(pdl
, "image/"); ptr
; ptr
= strstr(ptr
, "image/"))
1227 char *valptr
= value
+ strlen(value
);
1228 /* Pointer into value */
1230 if (valptr
< (value
+ sizeof(value
) - 1))
1234 while (isalnum(*ptr
& 255) || *ptr
== '-' || *ptr
== '.')
1236 if (isalnum(*ptr
& 255) && valptr
< (value
+ sizeof(value
) - 1))
1237 *valptr
++ = (char)toupper(*ptr
++ & 255);
1245 ptr
= device_id
+ strlen(device_id
);
1246 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "CMD:%s;", value
+ 1);
1250 device
->device_id
= strdup(device_id
);
1252 device
->device_id
= NULL
;
1254 if (device
->make_and_model
)
1255 free(device
->make_and_model
);
1257 if (make_and_model
[0])
1259 strlcat(make_and_model
, " ", sizeof(make_and_model
));
1260 strlcat(make_and_model
, model
, sizeof(make_and_model
));
1262 device
->make_and_model
= strdup(make_and_model
);
1265 device
->make_and_model
= strdup(model
);
1267 #endif /* HAVE_DNSSD || HAVE_AVAHI */
1271 * 'sigterm_handler()' - Handle termination signals.
1275 sigterm_handler(int sig
) /* I - Signal number (unused) */
1280 _exit(CUPS_BACKEND_OK
);
1287 * 'unquote()' - Unquote a name string.
1291 unquote(char *dst
, /* I - Destination buffer */
1292 const char *src
, /* I - Source string */
1293 size_t dstsize
) /* I - Size of destination buffer */
1295 char *dstend
= dst
+ dstsize
- 1; /* End of destination buffer */
1298 while (*src
&& dst
< dstend
)
1303 if (isdigit(src
[0] & 255) && isdigit(src
[1] & 255) &&
1304 isdigit(src
[2] & 255))
1306 *dst
++ = ((((src
[0] - '0') * 10) + src
[1] - '0') * 10) + src
[2] - '0';