mirror of https://gitee.com/openkylin/cups.git
1296 lines
36 KiB
C
1296 lines
36 KiB
C
|
/*
|
|||
|
* DNS-SD discovery backend for CUPS.
|
|||
|
*
|
|||
|
* Copyright © 2021-2023 by OpenPrinting.
|
|||
|
* Copyright © 2008-2018 by Apple Inc.
|
|||
|
*
|
|||
|
* Licensed under Apache License v2.0. See the file "LICENSE" for more
|
|||
|
* information.
|
|||
|
*/
|
|||
|
|
|||
|
/*
|
|||
|
* Include necessary headers.
|
|||
|
*/
|
|||
|
|
|||
|
#include "backend-private.h"
|
|||
|
#include <cups/array.h>
|
|||
|
#ifdef HAVE_MDNSRESPONDER
|
|||
|
# include <dns_sd.h>
|
|||
|
#endif /* HAVE_MDNSRESPONDER */
|
|||
|
#ifdef HAVE_AVAHI
|
|||
|
# include <avahi-client/client.h>
|
|||
|
# include <avahi-client/lookup.h>
|
|||
|
# include <avahi-common/simple-watch.h>
|
|||
|
# include <avahi-common/domain.h>
|
|||
|
# include <avahi-common/error.h>
|
|||
|
# include <avahi-common/malloc.h>
|
|||
|
#define kDNSServiceMaxDomainName AVAHI_DOMAIN_NAME_MAX
|
|||
|
#endif /* HAVE_AVAHI */
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* Device structure...
|
|||
|
*/
|
|||
|
|
|||
|
typedef enum
|
|||
|
{
|
|||
|
CUPS_DEVICE_PRINTER = 0, /* lpd://... */
|
|||
|
CUPS_DEVICE_IPPS, /* ipps://... */
|
|||
|
CUPS_DEVICE_IPP, /* ipp://... */
|
|||
|
CUPS_DEVICE_FAX_IPP, /* ipp://... */
|
|||
|
CUPS_DEVICE_PDL_DATASTREAM, /* socket://... */
|
|||
|
CUPS_DEVICE_RIOUSBPRINT /* riousbprint://... */
|
|||
|
} cups_devtype_t;
|
|||
|
|
|||
|
|
|||
|
typedef struct
|
|||
|
{
|
|||
|
#ifdef HAVE_MDNSRESPONDER
|
|||
|
DNSServiceRef ref; /* Service reference for query */
|
|||
|
#endif /* HAVE_MDNSRESPONDER */
|
|||
|
#ifdef HAVE_AVAHI
|
|||
|
AvahiRecordBrowser *ref; /* Browser for query */
|
|||
|
#endif /* HAVE_AVAHI */
|
|||
|
char *name, /* Service name */
|
|||
|
*domain, /* Domain name */
|
|||
|
*fullName, /* Full name */
|
|||
|
*make_and_model, /* Make and model from TXT record */
|
|||
|
*device_id, /* 1284 device ID from TXT record */
|
|||
|
*uuid; /* UUID from TXT record */
|
|||
|
cups_devtype_t type; /* Device registration type */
|
|||
|
int priority, /* Priority associated with type */
|
|||
|
cups_shared, /* CUPS shared printer? */
|
|||
|
sent; /* Did we list the device? */
|
|||
|
} cups_device_t;
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* Local globals...
|
|||
|
*/
|
|||
|
|
|||
|
static int job_canceled = 0;
|
|||
|
/* Set to 1 on SIGTERM */
|
|||
|
#ifdef HAVE_AVAHI
|
|||
|
static AvahiSimplePoll *simple_poll = NULL;
|
|||
|
/* Poll information */
|
|||
|
static int got_data = 0; /* Got data from poll? */
|
|||
|
static int browsers = 0; /* Number of running browsers */
|
|||
|
#endif /* HAVE_AVAHI */
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* Local functions...
|
|||
|
*/
|
|||
|
|
|||
|
#ifdef HAVE_MDNSRESPONDER
|
|||
|
static void browse_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *serviceName, const char *regtype, const char *replyDomain, void *context) _CUPS_NONNULL(1,5,6,7,8);
|
|||
|
static void browse_local_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *serviceName, const char *regtype, const char *replyDomain, void *context) _CUPS_NONNULL(1,5,6,7,8);
|
|||
|
#endif /* HAVE_MDNSRESPONDER */
|
|||
|
#ifdef HAVE_AVAHI
|
|||
|
static void browse_callback(AvahiServiceBrowser *browser,
|
|||
|
AvahiIfIndex interface,
|
|||
|
AvahiProtocol protocol,
|
|||
|
AvahiBrowserEvent event,
|
|||
|
const char *serviceName,
|
|||
|
const char *regtype,
|
|||
|
const char *replyDomain,
|
|||
|
AvahiLookupResultFlags flags,
|
|||
|
void *context);
|
|||
|
static void client_callback(AvahiClient *client,
|
|||
|
AvahiClientState state,
|
|||
|
void *context);
|
|||
|
#endif /* HAVE_AVAHI */
|
|||
|
|
|||
|
static int compare_devices(cups_device_t *a, cups_device_t *b);
|
|||
|
static void exec_backend(char **argv) _CUPS_NORETURN;
|
|||
|
static cups_device_t *get_device(cups_array_t *devices, const char *serviceName, const char *regtype, const char *replyDomain) _CUPS_NONNULL(1,2,3,4);
|
|||
|
#ifdef HAVE_MDNSRESPONDER
|
|||
|
static void query_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *fullName, uint16_t rrtype, uint16_t rrclass, uint16_t rdlen, const void *rdata, uint32_t ttl, void *context) _CUPS_NONNULL(1,5,9,11);
|
|||
|
#elif defined(HAVE_AVAHI)
|
|||
|
static int poll_callback(struct pollfd *pollfds,
|
|||
|
unsigned int num_pollfds, int timeout,
|
|||
|
void *context);
|
|||
|
static void query_callback(AvahiRecordBrowser *browser,
|
|||
|
AvahiIfIndex interface,
|
|||
|
AvahiProtocol protocol,
|
|||
|
AvahiBrowserEvent event,
|
|||
|
const char *name, uint16_t rrclass,
|
|||
|
uint16_t rrtype, const void *rdata,
|
|||
|
size_t rdlen,
|
|||
|
AvahiLookupResultFlags flags,
|
|||
|
void *context);
|
|||
|
#endif /* HAVE_MDNSRESPONDER */
|
|||
|
static void sigterm_handler(int sig);
|
|||
|
static void unquote(char *dst, const char *src, size_t dstsize) _CUPS_NONNULL(1,2);
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* 'main()' - Browse for printers.
|
|||
|
*/
|
|||
|
|
|||
|
int /* O - Exit status */
|
|||
|
main(int argc, /* I - Number of command-line args */
|
|||
|
char *argv[]) /* I - Command-line arguments */
|
|||
|
{
|
|||
|
const char *name; /* Backend name */
|
|||
|
cups_array_t *devices; /* Device array */
|
|||
|
cups_device_t *device; /* Current device */
|
|||
|
char uriName[1024]; /* Unquoted fullName for URI */
|
|||
|
#ifdef HAVE_MDNSRESPONDER
|
|||
|
int fd; /* Main file descriptor */
|
|||
|
fd_set input; /* Input set for select() */
|
|||
|
struct timeval timeout; /* Timeout for select() */
|
|||
|
DNSServiceRef main_ref, /* Main service reference */
|
|||
|
fax_ipp_ref, /* IPP fax service reference */
|
|||
|
ipp_ref, /* IPP service reference */
|
|||
|
ipp_tls_ref, /* IPP w/TLS service reference */
|
|||
|
ipps_ref, /* IPP service reference */
|
|||
|
local_fax_ipp_ref, /* Local IPP fax service reference */
|
|||
|
local_ipp_ref, /* Local IPP service reference */
|
|||
|
local_ipp_tls_ref, /* Local IPP w/TLS service reference */
|
|||
|
local_ipps_ref, /* Local IPP service reference */
|
|||
|
local_printer_ref, /* Local LPD service reference */
|
|||
|
pdl_datastream_ref, /* AppSocket service reference */
|
|||
|
printer_ref, /* LPD service reference */
|
|||
|
riousbprint_ref; /* Remote IO service reference */
|
|||
|
#endif /* HAVE_MDNSRESPONDER */
|
|||
|
#ifdef HAVE_AVAHI
|
|||
|
AvahiClient *client; /* Client information */
|
|||
|
int error; /* Error code, if any */
|
|||
|
#endif /* HAVE_AVAHI */
|
|||
|
#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
|
|||
|
struct sigaction action; /* Actions for POSIX signals */
|
|||
|
#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* Don't buffer stderr, and catch SIGTERM...
|
|||
|
*/
|
|||
|
|
|||
|
setbuf(stderr, NULL);
|
|||
|
|
|||
|
#ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
|
|||
|
sigset(SIGTERM, sigterm_handler);
|
|||
|
#elif defined(HAVE_SIGACTION)
|
|||
|
memset(&action, 0, sizeof(action));
|
|||
|
|
|||
|
sigemptyset(&action.sa_mask);
|
|||
|
action.sa_handler = sigterm_handler;
|
|||
|
sigaction(SIGTERM, &action, NULL);
|
|||
|
#else
|
|||
|
signal(SIGTERM, sigterm_handler);
|
|||
|
#endif /* HAVE_SIGSET */
|
|||
|
|
|||
|
/*
|
|||
|
* Check command-line...
|
|||
|
*/
|
|||
|
|
|||
|
if (argc >= 6)
|
|||
|
exec_backend(argv);
|
|||
|
else if (argc != 1)
|
|||
|
{
|
|||
|
_cupsLangPrintf(stderr,
|
|||
|
_("Usage: %s job-id user title copies options [file]"),
|
|||
|
argv[0]);
|
|||
|
return (1);
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Only do discovery when run as "dnssd"...
|
|||
|
*/
|
|||
|
|
|||
|
if ((name = strrchr(argv[0], '/')) != NULL)
|
|||
|
name ++;
|
|||
|
else
|
|||
|
name = argv[0];
|
|||
|
|
|||
|
if (strcmp(name, "dnssd"))
|
|||
|
return (0);
|
|||
|
|
|||
|
/*
|
|||
|
* Create an array to track devices...
|
|||
|
*/
|
|||
|
|
|||
|
devices = cupsArrayNew((cups_array_func_t)compare_devices, NULL);
|
|||
|
|
|||
|
/*
|
|||
|
* Browse for different kinds of printers...
|
|||
|
*/
|
|||
|
|
|||
|
#ifdef HAVE_MDNSRESPONDER
|
|||
|
if (DNSServiceCreateConnection(&main_ref) != kDNSServiceErr_NoError)
|
|||
|
{
|
|||
|
perror("ERROR: Unable to create service connection");
|
|||
|
return (1);
|
|||
|
}
|
|||
|
|
|||
|
fd = DNSServiceRefSockFD(main_ref);
|
|||
|
|
|||
|
fax_ipp_ref = main_ref;
|
|||
|
DNSServiceBrowse(&fax_ipp_ref, kDNSServiceFlagsShareConnection, 0,
|
|||
|
"_fax-ipp._tcp", NULL, browse_callback, devices);
|
|||
|
|
|||
|
ipp_ref = main_ref;
|
|||
|
DNSServiceBrowse(&ipp_ref, kDNSServiceFlagsShareConnection, 0,
|
|||
|
"_ipp._tcp", NULL, browse_callback, devices);
|
|||
|
|
|||
|
ipp_tls_ref = main_ref;
|
|||
|
DNSServiceBrowse(&ipp_tls_ref, kDNSServiceFlagsShareConnection, 0,
|
|||
|
"_ipp-tls._tcp", NULL, browse_callback, devices);
|
|||
|
|
|||
|
ipps_ref = main_ref;
|
|||
|
DNSServiceBrowse(&ipps_ref, kDNSServiceFlagsShareConnection, 0,
|
|||
|
"_ipps._tcp", NULL, browse_callback, devices);
|
|||
|
|
|||
|
local_fax_ipp_ref = main_ref;
|
|||
|
DNSServiceBrowse(&local_fax_ipp_ref, kDNSServiceFlagsShareConnection,
|
|||
|
kDNSServiceInterfaceIndexLocalOnly,
|
|||
|
"_fax-ipp._tcp", NULL, browse_local_callback, devices);
|
|||
|
|
|||
|
local_ipp_ref = main_ref;
|
|||
|
DNSServiceBrowse(&local_ipp_ref, kDNSServiceFlagsShareConnection,
|
|||
|
kDNSServiceInterfaceIndexLocalOnly,
|
|||
|
"_ipp._tcp", NULL, browse_local_callback, devices);
|
|||
|
|
|||
|
local_ipp_tls_ref = main_ref;
|
|||
|
DNSServiceBrowse(&local_ipp_tls_ref, kDNSServiceFlagsShareConnection,
|
|||
|
kDNSServiceInterfaceIndexLocalOnly,
|
|||
|
"_ipp-tls._tcp", NULL, browse_local_callback, devices);
|
|||
|
|
|||
|
local_ipps_ref = main_ref;
|
|||
|
DNSServiceBrowse(&local_ipps_ref, kDNSServiceFlagsShareConnection,
|
|||
|
kDNSServiceInterfaceIndexLocalOnly,
|
|||
|
"_ipps._tcp", NULL, browse_local_callback, devices);
|
|||
|
|
|||
|
local_printer_ref = main_ref;
|
|||
|
DNSServiceBrowse(&local_printer_ref, kDNSServiceFlagsShareConnection,
|
|||
|
kDNSServiceInterfaceIndexLocalOnly,
|
|||
|
"_printer._tcp", NULL, browse_local_callback, devices);
|
|||
|
|
|||
|
pdl_datastream_ref = main_ref;
|
|||
|
DNSServiceBrowse(&pdl_datastream_ref, kDNSServiceFlagsShareConnection, 0,
|
|||
|
"_pdl-datastream._tcp", NULL, browse_callback, devices);
|
|||
|
|
|||
|
printer_ref = main_ref;
|
|||
|
DNSServiceBrowse(&printer_ref, kDNSServiceFlagsShareConnection, 0,
|
|||
|
"_printer._tcp", NULL, browse_callback, devices);
|
|||
|
|
|||
|
riousbprint_ref = main_ref;
|
|||
|
DNSServiceBrowse(&riousbprint_ref, kDNSServiceFlagsShareConnection, 0,
|
|||
|
"_riousbprint._tcp", NULL, browse_callback, devices);
|
|||
|
#endif /* HAVE_MDNSRESPONDER */
|
|||
|
|
|||
|
#ifdef HAVE_AVAHI
|
|||
|
if ((simple_poll = avahi_simple_poll_new()) == NULL)
|
|||
|
{
|
|||
|
fputs("DEBUG: Unable to create Avahi simple poll object.\n", stderr);
|
|||
|
return (0);
|
|||
|
}
|
|||
|
|
|||
|
avahi_simple_poll_set_func(simple_poll, poll_callback, NULL);
|
|||
|
|
|||
|
client = avahi_client_new(avahi_simple_poll_get(simple_poll),
|
|||
|
0, client_callback, simple_poll, &error);
|
|||
|
if (!client)
|
|||
|
{
|
|||
|
fputs("DEBUG: Unable to create Avahi client.\n", stderr);
|
|||
|
return (0);
|
|||
|
}
|
|||
|
|
|||
|
browsers = 6;
|
|||
|
avahi_service_browser_new(client, AVAHI_IF_UNSPEC,
|
|||
|
AVAHI_PROTO_UNSPEC,
|
|||
|
"_fax-ipp._tcp", NULL, 0,
|
|||
|
browse_callback, devices);
|
|||
|
avahi_service_browser_new(client, AVAHI_IF_UNSPEC,
|
|||
|
AVAHI_PROTO_UNSPEC,
|
|||
|
"_ipp._tcp", NULL, 0,
|
|||
|
browse_callback, devices);
|
|||
|
avahi_service_browser_new(client, AVAHI_IF_UNSPEC,
|
|||
|
AVAHI_PROTO_UNSPEC,
|
|||
|
"_ipp-tls._tcp", NULL, 0,
|
|||
|
browse_callback, devices);
|
|||
|
avahi_service_browser_new(client, AVAHI_IF_UNSPEC,
|
|||
|
AVAHI_PROTO_UNSPEC,
|
|||
|
"_ipps._tcp", NULL, 0,
|
|||
|
browse_callback, devices);
|
|||
|
avahi_service_browser_new(client, AVAHI_IF_UNSPEC,
|
|||
|
AVAHI_PROTO_UNSPEC,
|
|||
|
"_pdl-datastream._tcp",
|
|||
|
NULL, 0,
|
|||
|
browse_callback,
|
|||
|
devices);
|
|||
|
avahi_service_browser_new(client, AVAHI_IF_UNSPEC,
|
|||
|
AVAHI_PROTO_UNSPEC,
|
|||
|
"_printer._tcp", NULL, 0,
|
|||
|
browse_callback, devices);
|
|||
|
#endif /* HAVE_AVAHI */
|
|||
|
|
|||
|
/*
|
|||
|
* Loop until we are killed...
|
|||
|
*/
|
|||
|
|
|||
|
while (!job_canceled)
|
|||
|
{
|
|||
|
int announce = 0; /* Announce printers? */
|
|||
|
|
|||
|
#ifdef HAVE_MDNSRESPONDER
|
|||
|
FD_ZERO(&input);
|
|||
|
FD_SET(fd, &input);
|
|||
|
|
|||
|
timeout.tv_sec = 0;
|
|||
|
timeout.tv_usec = 500000;
|
|||
|
|
|||
|
if (select(fd + 1, &input, NULL, NULL, &timeout) < 0)
|
|||
|
continue;
|
|||
|
|
|||
|
if (FD_ISSET(fd, &input))
|
|||
|
{
|
|||
|
/*
|
|||
|
* Process results of our browsing...
|
|||
|
*/
|
|||
|
|
|||
|
DNSServiceProcessResult(main_ref);
|
|||
|
}
|
|||
|
else
|
|||
|
announce = 1;
|
|||
|
|
|||
|
#elif defined(HAVE_AVAHI)
|
|||
|
got_data = 0;
|
|||
|
|
|||
|
if ((error = avahi_simple_poll_iterate(simple_poll, 500)) > 0)
|
|||
|
{
|
|||
|
/*
|
|||
|
* We've been told to exit the loop. Perhaps the connection to
|
|||
|
* Avahi failed.
|
|||
|
*/
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
if (!got_data)
|
|||
|
announce = 1;
|
|||
|
#endif /* HAVE_MDNSRESPONDER */
|
|||
|
|
|||
|
/* fprintf(stderr, "DEBUG: announce=%d\n", announce);*/
|
|||
|
|
|||
|
if (announce)
|
|||
|
{
|
|||
|
/*
|
|||
|
* Announce any devices we've found...
|
|||
|
*/
|
|||
|
|
|||
|
#ifdef HAVE_MDNSRESPONDER
|
|||
|
DNSServiceErrorType status; /* DNS query status */
|
|||
|
#endif /* HAVE_MDNSRESPONDER */
|
|||
|
cups_device_t *best; /* Best matching device */
|
|||
|
char device_uri[1024]; /* Device URI */
|
|||
|
int count; /* Number of queries */
|
|||
|
int sent; /* Number of sent */
|
|||
|
|
|||
|
for (device = (cups_device_t *)cupsArrayFirst(devices),
|
|||
|
best = NULL, count = 0, sent = 0;
|
|||
|
device;
|
|||
|
device = (cups_device_t *)cupsArrayNext(devices))
|
|||
|
{
|
|||
|
if (device->sent)
|
|||
|
sent ++;
|
|||
|
|
|||
|
if (device->ref)
|
|||
|
count ++;
|
|||
|
|
|||
|
if (!device->ref && !device->sent)
|
|||
|
{
|
|||
|
/*
|
|||
|
* Found the device, now get the TXT record(s) for it...
|
|||
|
*/
|
|||
|
|
|||
|
if (count < 50)
|
|||
|
{
|
|||
|
fprintf(stderr, "DEBUG: Querying \"%s\"...\n", device->fullName);
|
|||
|
|
|||
|
#ifdef HAVE_MDNSRESPONDER
|
|||
|
device->ref = main_ref;
|
|||
|
|
|||
|
status = DNSServiceQueryRecord(&(device->ref),
|
|||
|
kDNSServiceFlagsShareConnection,
|
|||
|
0, device->fullName,
|
|||
|
kDNSServiceType_TXT,
|
|||
|
kDNSServiceClass_IN, query_callback,
|
|||
|
device);
|
|||
|
if (status != kDNSServiceErr_NoError)
|
|||
|
fprintf(stderr,
|
|||
|
"ERROR: Unable to query \"%s\" for TXT records: %d\n",
|
|||
|
device->fullName, status);
|
|||
|
/* Users never see this */
|
|||
|
else
|
|||
|
count ++;
|
|||
|
|
|||
|
#else
|
|||
|
if ((device->ref = avahi_record_browser_new(client, AVAHI_IF_UNSPEC,
|
|||
|
AVAHI_PROTO_UNSPEC,
|
|||
|
device->fullName,
|
|||
|
AVAHI_DNS_CLASS_IN,
|
|||
|
AVAHI_DNS_TYPE_TXT,
|
|||
|
0,
|
|||
|
query_callback,
|
|||
|
device)) == NULL)
|
|||
|
fprintf(stderr,
|
|||
|
"ERROR: Unable to query \"%s\" for TXT records: %s\n",
|
|||
|
device->fullName,
|
|||
|
avahi_strerror(avahi_client_errno(client)));
|
|||
|
/* Users never see this */
|
|||
|
else
|
|||
|
count ++;
|
|||
|
#endif /* HAVE_AVAHI */
|
|||
|
}
|
|||
|
}
|
|||
|
else if (!device->sent)
|
|||
|
{
|
|||
|
#ifdef HAVE_MDNSRESPONDER
|
|||
|
/*
|
|||
|
* Got the TXT records, now report the device...
|
|||
|
*/
|
|||
|
|
|||
|
DNSServiceRefDeallocate(device->ref);
|
|||
|
#else
|
|||
|
avahi_record_browser_free(device->ref);
|
|||
|
#endif /* HAVE_MDNSRESPONDER */
|
|||
|
|
|||
|
device->ref = NULL;
|
|||
|
|
|||
|
if (!best)
|
|||
|
best = device;
|
|||
|
else if (_cups_strcasecmp(best->name, device->name) ||
|
|||
|
_cups_strcasecmp(best->domain, device->domain))
|
|||
|
{
|
|||
|
unquote(uriName, best->fullName, sizeof(uriName));
|
|||
|
|
|||
|
if (best->uuid)
|
|||
|
httpAssembleURIf(HTTP_URI_CODING_ALL, device_uri,
|
|||
|
sizeof(device_uri), "dnssd", NULL, uriName, 0,
|
|||
|
best->cups_shared ? "/cups?uuid=%s" : "/?uuid=%s",
|
|||
|
best->uuid);
|
|||
|
else
|
|||
|
httpAssembleURI(HTTP_URI_CODING_ALL, device_uri,
|
|||
|
sizeof(device_uri), "dnssd", NULL, uriName, 0,
|
|||
|
best->cups_shared ? "/cups" : "/");
|
|||
|
|
|||
|
cupsBackendReport("network", device_uri, best->make_and_model,
|
|||
|
best->name, best->device_id, NULL);
|
|||
|
best->sent = 1;
|
|||
|
best = device;
|
|||
|
|
|||
|
sent ++;
|
|||
|
}
|
|||
|
else if (best->priority > device->priority ||
|
|||
|
(best->priority == device->priority &&
|
|||
|
best->type < device->type))
|
|||
|
{
|
|||
|
best->sent = 1;
|
|||
|
best = device;
|
|||
|
|
|||
|
sent ++;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
device->sent = 1;
|
|||
|
|
|||
|
sent ++;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (best)
|
|||
|
{
|
|||
|
unquote(uriName, best->fullName, sizeof(uriName));
|
|||
|
|
|||
|
if (best->uuid)
|
|||
|
httpAssembleURIf(HTTP_URI_CODING_ALL, device_uri,
|
|||
|
sizeof(device_uri), "dnssd", NULL, uriName, 0,
|
|||
|
best->cups_shared ? "/cups?uuid=%s" : "/?uuid=%s",
|
|||
|
best->uuid);
|
|||
|
else
|
|||
|
httpAssembleURI(HTTP_URI_CODING_ALL, device_uri,
|
|||
|
sizeof(device_uri), "dnssd", NULL, uriName, 0,
|
|||
|
best->cups_shared ? "/cups" : "/");
|
|||
|
|
|||
|
cupsBackendReport("network", device_uri, best->make_and_model,
|
|||
|
best->name, best->device_id, NULL);
|
|||
|
best->sent = 1;
|
|||
|
sent ++;
|
|||
|
}
|
|||
|
|
|||
|
fprintf(stderr, "DEBUG: sent=%d, count=%d\n", sent, count);
|
|||
|
|
|||
|
#ifdef HAVE_AVAHI
|
|||
|
if (sent == cupsArrayCount(devices) && browsers == 0)
|
|||
|
#else
|
|||
|
if (sent == cupsArrayCount(devices))
|
|||
|
#endif /* HAVE_AVAHI */
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return (CUPS_BACKEND_OK);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
#ifdef HAVE_MDNSRESPONDER
|
|||
|
/*
|
|||
|
* 'browse_callback()' - Browse devices.
|
|||
|
*/
|
|||
|
|
|||
|
static void
|
|||
|
browse_callback(
|
|||
|
DNSServiceRef sdRef, /* I - Service reference */
|
|||
|
DNSServiceFlags flags, /* I - Option flags */
|
|||
|
uint32_t interfaceIndex, /* I - Interface number */
|
|||
|
DNSServiceErrorType errorCode, /* I - Error, if any */
|
|||
|
const char *serviceName, /* I - Name of service/device */
|
|||
|
const char *regtype, /* I - Type of service */
|
|||
|
const char *replyDomain, /* I - Service domain */
|
|||
|
void *context) /* I - Devices array */
|
|||
|
{
|
|||
|
fprintf(stderr, "DEBUG2: browse_callback(sdRef=%p, flags=%x, "
|
|||
|
"interfaceIndex=%u, errorCode=%d, serviceName=\"%s\", "
|
|||
|
"regtype=\"%s\", replyDomain=\"%s\", context=%p)\n",
|
|||
|
sdRef, flags, interfaceIndex, errorCode,
|
|||
|
serviceName, regtype, replyDomain, context);
|
|||
|
|
|||
|
/*
|
|||
|
* Only process "add" data...
|
|||
|
*/
|
|||
|
|
|||
|
if (errorCode != kDNSServiceErr_NoError || !(flags & kDNSServiceFlagsAdd))
|
|||
|
return;
|
|||
|
|
|||
|
/*
|
|||
|
* Get the device...
|
|||
|
*/
|
|||
|
|
|||
|
get_device((cups_array_t *)context, serviceName, regtype, replyDomain);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* 'browse_local_callback()' - Browse local devices.
|
|||
|
*/
|
|||
|
|
|||
|
static void
|
|||
|
browse_local_callback(
|
|||
|
DNSServiceRef sdRef, /* I - Service reference */
|
|||
|
DNSServiceFlags flags, /* I - Option flags */
|
|||
|
uint32_t interfaceIndex, /* I - Interface number */
|
|||
|
DNSServiceErrorType errorCode, /* I - Error, if any */
|
|||
|
const char *serviceName, /* I - Name of service/device */
|
|||
|
const char *regtype, /* I - Type of service */
|
|||
|
const char *replyDomain, /* I - Service domain */
|
|||
|
void *context) /* I - Devices array */
|
|||
|
{
|
|||
|
cups_device_t *device; /* Device */
|
|||
|
|
|||
|
|
|||
|
fprintf(stderr, "DEBUG2: browse_local_callback(sdRef=%p, flags=%x, "
|
|||
|
"interfaceIndex=%u, errorCode=%d, serviceName=\"%s\", "
|
|||
|
"regtype=\"%s\", replyDomain=\"%s\", context=%p)\n",
|
|||
|
sdRef, flags, interfaceIndex, errorCode,
|
|||
|
serviceName, regtype, replyDomain, context);
|
|||
|
|
|||
|
/*
|
|||
|
* Only process "add" data...
|
|||
|
*/
|
|||
|
|
|||
|
if (errorCode != kDNSServiceErr_NoError || !(flags & kDNSServiceFlagsAdd))
|
|||
|
return;
|
|||
|
|
|||
|
/*
|
|||
|
* Get the device...
|
|||
|
*/
|
|||
|
|
|||
|
device = get_device((cups_array_t *)context, serviceName, regtype,
|
|||
|
replyDomain);
|
|||
|
|
|||
|
/*
|
|||
|
* Hide locally-registered devices...
|
|||
|
*/
|
|||
|
|
|||
|
fprintf(stderr, "DEBUG: Hiding local printer \"%s\"...\n",
|
|||
|
device->fullName);
|
|||
|
device->sent = 1;
|
|||
|
}
|
|||
|
#endif /* HAVE_MDNSRESPONDER */
|
|||
|
|
|||
|
|
|||
|
#ifdef HAVE_AVAHI
|
|||
|
/*
|
|||
|
* 'browse_callback()' - Browse devices.
|
|||
|
*/
|
|||
|
|
|||
|
static void
|
|||
|
browse_callback(
|
|||
|
AvahiServiceBrowser *browser, /* I - Browser */
|
|||
|
AvahiIfIndex interface, /* I - Interface index (unused) */
|
|||
|
AvahiProtocol protocol, /* I - Network protocol (unused) */
|
|||
|
AvahiBrowserEvent event, /* I - What happened */
|
|||
|
const char *name, /* I - Service name */
|
|||
|
const char *type, /* I - Registration type */
|
|||
|
const char *domain, /* I - Domain */
|
|||
|
AvahiLookupResultFlags flags, /* I - Flags */
|
|||
|
void *context) /* I - Devices array */
|
|||
|
{
|
|||
|
AvahiClient *client = avahi_service_browser_get_client(browser);
|
|||
|
/* Client information */
|
|||
|
|
|||
|
|
|||
|
(void)interface;
|
|||
|
(void)protocol;
|
|||
|
(void)context;
|
|||
|
|
|||
|
switch (event)
|
|||
|
{
|
|||
|
case AVAHI_BROWSER_FAILURE:
|
|||
|
fprintf(stderr, "DEBUG: browse_callback: %s\n",
|
|||
|
avahi_strerror(avahi_client_errno(client)));
|
|||
|
avahi_simple_poll_quit(simple_poll);
|
|||
|
break;
|
|||
|
|
|||
|
case AVAHI_BROWSER_NEW:
|
|||
|
/*
|
|||
|
* This object is new on the network.
|
|||
|
*/
|
|||
|
|
|||
|
if (flags & AVAHI_LOOKUP_RESULT_LOCAL)
|
|||
|
{
|
|||
|
/*
|
|||
|
* This comes from the local machine so ignore it.
|
|||
|
*/
|
|||
|
|
|||
|
fprintf(stderr, "DEBUG: Ignoring local service %s.\n", name);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
/*
|
|||
|
* Create a device entry for it if it doesn't yet exist.
|
|||
|
*/
|
|||
|
|
|||
|
get_device((cups_array_t *)context, name, type, domain);
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case AVAHI_BROWSER_REMOVE:
|
|||
|
case AVAHI_BROWSER_CACHE_EXHAUSTED:
|
|||
|
break;
|
|||
|
|
|||
|
case AVAHI_BROWSER_ALL_FOR_NOW:
|
|||
|
browsers--;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* 'client_callback()' - Avahi client callback function.
|
|||
|
*/
|
|||
|
|
|||
|
static void
|
|||
|
client_callback(
|
|||
|
AvahiClient *client, /* I - Client information (unused) */
|
|||
|
AvahiClientState state, /* I - Current state */
|
|||
|
void *context) /* I - User data (unused) */
|
|||
|
{
|
|||
|
(void)client;
|
|||
|
(void)context;
|
|||
|
|
|||
|
/*
|
|||
|
* If the connection drops, quit.
|
|||
|
*/
|
|||
|
|
|||
|
if (state == AVAHI_CLIENT_FAILURE)
|
|||
|
{
|
|||
|
fputs("DEBUG: Avahi connection failed.\n", stderr);
|
|||
|
avahi_simple_poll_quit(simple_poll);
|
|||
|
}
|
|||
|
}
|
|||
|
#endif /* HAVE_AVAHI */
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* 'compare_devices()' - Compare two devices.
|
|||
|
*/
|
|||
|
|
|||
|
static int /* O - Result of comparison */
|
|||
|
compare_devices(cups_device_t *a, /* I - First device */
|
|||
|
cups_device_t *b) /* I - Second device */
|
|||
|
{
|
|||
|
return (strcmp(a->name, b->name));
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* 'exec_backend()' - Execute the backend that corresponds to the
|
|||
|
* resolved service name.
|
|||
|
*/
|
|||
|
|
|||
|
static void
|
|||
|
exec_backend(char **argv) /* I - Command-line arguments */
|
|||
|
{
|
|||
|
const char *resolved_uri, /* Resolved device URI */
|
|||
|
*cups_serverbin; /* Location of programs */
|
|||
|
char scheme[1024], /* Scheme from URI */
|
|||
|
*ptr, /* Pointer into scheme */
|
|||
|
filename[1024]; /* Backend filename */
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* Resolve the device URI...
|
|||
|
*/
|
|||
|
|
|||
|
job_canceled = -1;
|
|||
|
|
|||
|
while ((resolved_uri = cupsBackendDeviceURI(argv)) == NULL)
|
|||
|
{
|
|||
|
_cupsLangPrintFilter(stderr, "INFO", _("Unable to locate printer."));
|
|||
|
sleep(10);
|
|||
|
|
|||
|
if (getenv("CLASS") != NULL)
|
|||
|
exit(CUPS_BACKEND_FAILED);
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Extract the scheme from the URI...
|
|||
|
*/
|
|||
|
|
|||
|
strlcpy(scheme, resolved_uri, sizeof(scheme));
|
|||
|
if ((ptr = strchr(scheme, ':')) != NULL)
|
|||
|
*ptr = '\0';
|
|||
|
|
|||
|
/*
|
|||
|
* Get the filename of the backend...
|
|||
|
*/
|
|||
|
|
|||
|
if ((cups_serverbin = getenv("CUPS_SERVERBIN")) == NULL)
|
|||
|
cups_serverbin = CUPS_SERVERBIN;
|
|||
|
|
|||
|
snprintf(filename, sizeof(filename), "%s/backend/%s", cups_serverbin, scheme);
|
|||
|
|
|||
|
/*
|
|||
|
* Overwrite the device URI and run the new backend...
|
|||
|
*/
|
|||
|
|
|||
|
setenv("DEVICE_URI", resolved_uri, 1);
|
|||
|
|
|||
|
argv[0] = (char *)resolved_uri;
|
|||
|
|
|||
|
fprintf(stderr, "DEBUG: Executing backend \"%s\"...\n", filename);
|
|||
|
|
|||
|
execv(filename, argv);
|
|||
|
|
|||
|
fprintf(stderr, "ERROR: Unable to execute backend \"%s\": %s\n", filename,
|
|||
|
strerror(errno));
|
|||
|
exit(CUPS_BACKEND_STOP);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* 'device_type()' - Get DNS-SD type enumeration from string.
|
|||
|
*/
|
|||
|
|
|||
|
static cups_devtype_t /* O - Device type */
|
|||
|
device_type(const char *regtype) /* I - Service registration type */
|
|||
|
{
|
|||
|
#ifdef HAVE_AVAHI
|
|||
|
if (!strcmp(regtype, "_ipp._tcp"))
|
|||
|
return (CUPS_DEVICE_IPP);
|
|||
|
else if (!strcmp(regtype, "_ipps._tcp") ||
|
|||
|
!strcmp(regtype, "_ipp-tls._tcp"))
|
|||
|
return (CUPS_DEVICE_IPPS);
|
|||
|
else if (!strcmp(regtype, "_fax-ipp._tcp"))
|
|||
|
return (CUPS_DEVICE_FAX_IPP);
|
|||
|
else if (!strcmp(regtype, "_printer._tcp"))
|
|||
|
return (CUPS_DEVICE_PDL_DATASTREAM);
|
|||
|
#else
|
|||
|
if (!strcmp(regtype, "_ipp._tcp."))
|
|||
|
return (CUPS_DEVICE_IPP);
|
|||
|
else if (!strcmp(regtype, "_ipps._tcp.") ||
|
|||
|
!strcmp(regtype, "_ipp-tls._tcp."))
|
|||
|
return (CUPS_DEVICE_IPPS);
|
|||
|
else if (!strcmp(regtype, "_fax-ipp._tcp."))
|
|||
|
return (CUPS_DEVICE_FAX_IPP);
|
|||
|
else if (!strcmp(regtype, "_printer._tcp."))
|
|||
|
return (CUPS_DEVICE_PRINTER);
|
|||
|
else if (!strcmp(regtype, "_pdl-datastream._tcp."))
|
|||
|
return (CUPS_DEVICE_PDL_DATASTREAM);
|
|||
|
#endif /* HAVE_AVAHI */
|
|||
|
|
|||
|
return (CUPS_DEVICE_RIOUSBPRINT);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* 'get_device()' - Create or update a device.
|
|||
|
*/
|
|||
|
|
|||
|
static cups_device_t * /* O - Device */
|
|||
|
get_device(cups_array_t *devices, /* I - Device array */
|
|||
|
const char *serviceName, /* I - Name of service/device */
|
|||
|
const char *regtype, /* I - Type of service */
|
|||
|
const char *replyDomain) /* I - Service domain */
|
|||
|
{
|
|||
|
cups_device_t key, /* Search key */
|
|||
|
*device; /* Device */
|
|||
|
char fullName[kDNSServiceMaxDomainName];
|
|||
|
/* Full name for query */
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* See if this is a new device...
|
|||
|
*/
|
|||
|
|
|||
|
key.name = (char *)serviceName;
|
|||
|
key.type = device_type(regtype);
|
|||
|
|
|||
|
for (device = cupsArrayFind(devices, &key);
|
|||
|
device;
|
|||
|
device = cupsArrayNext(devices))
|
|||
|
if (_cups_strcasecmp(device->name, key.name))
|
|||
|
break;
|
|||
|
else if (device->type == key.type)
|
|||
|
{
|
|||
|
if (!_cups_strcasecmp(device->domain, "local.") &&
|
|||
|
_cups_strcasecmp(device->domain, replyDomain))
|
|||
|
{
|
|||
|
/*
|
|||
|
* Update the .local listing to use the "global" domain name instead.
|
|||
|
* The backend will try local lookups first, then the global domain name.
|
|||
|
*/
|
|||
|
|
|||
|
free(device->domain);
|
|||
|
device->domain = strdup(replyDomain);
|
|||
|
|
|||
|
#ifdef HAVE_MDNSRESPONDER
|
|||
|
DNSServiceConstructFullName(fullName, device->name, regtype,
|
|||
|
replyDomain);
|
|||
|
#else /* HAVE_AVAHI */
|
|||
|
avahi_service_name_join(fullName, kDNSServiceMaxDomainName,
|
|||
|
serviceName, regtype, replyDomain);
|
|||
|
#endif /* HAVE_MDNSRESPONDER */
|
|||
|
|
|||
|
free(device->fullName);
|
|||
|
device->fullName = strdup(fullName);
|
|||
|
}
|
|||
|
|
|||
|
return (device);
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Yes, add the device...
|
|||
|
*/
|
|||
|
|
|||
|
if ((device = calloc(1, sizeof(cups_device_t))) == NULL)
|
|||
|
{
|
|||
|
perror("DEBUG: Out of memory adding a device");
|
|||
|
return (NULL);
|
|||
|
}
|
|||
|
|
|||
|
device->name = strdup(serviceName);
|
|||
|
device->domain = strdup(replyDomain);
|
|||
|
device->type = key.type;
|
|||
|
device->priority = 50;
|
|||
|
|
|||
|
cupsArrayAdd(devices, device);
|
|||
|
|
|||
|
/*
|
|||
|
* Set the "full name" of this service, which is used for queries...
|
|||
|
*/
|
|||
|
|
|||
|
#ifdef HAVE_MDNSRESPONDER
|
|||
|
DNSServiceConstructFullName(fullName, serviceName, regtype, replyDomain);
|
|||
|
#else /* HAVE_AVAHI */
|
|||
|
avahi_service_name_join(fullName, kDNSServiceMaxDomainName, serviceName, regtype, replyDomain);
|
|||
|
#endif /* HAVE_MDNSRESPONDER */
|
|||
|
|
|||
|
device->fullName = strdup(fullName);
|
|||
|
|
|||
|
return (device);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
#ifdef HAVE_AVAHI
|
|||
|
/*
|
|||
|
* 'poll_callback()' - Wait for input on the specified file descriptors.
|
|||
|
*
|
|||
|
* Note: This function is needed because avahi_simple_poll_iterate is broken
|
|||
|
* and always uses a timeout of 0 (!) milliseconds.
|
|||
|
* (https://github.com/lathiat/avahi/issues/127)
|
|||
|
*/
|
|||
|
|
|||
|
static int /* O - Number of file descriptors matching */
|
|||
|
poll_callback(
|
|||
|
struct pollfd *pollfds, /* I - File descriptors */
|
|||
|
unsigned int num_pollfds, /* I - Number of file descriptors */
|
|||
|
int timeout, /* I - Timeout in milliseconds (unused) */
|
|||
|
void *context) /* I - User data (unused) */
|
|||
|
{
|
|||
|
int val; /* Return value */
|
|||
|
|
|||
|
|
|||
|
(void)timeout;
|
|||
|
(void)context;
|
|||
|
|
|||
|
val = poll(pollfds, num_pollfds, 500);
|
|||
|
|
|||
|
if (val < 0)
|
|||
|
fprintf(stderr, "DEBUG: poll_callback: %s\n", strerror(errno));
|
|||
|
else if (val > 0)
|
|||
|
got_data = 1;
|
|||
|
|
|||
|
return (val);
|
|||
|
}
|
|||
|
#endif /* HAVE_AVAHI */
|
|||
|
|
|||
|
|
|||
|
#ifdef HAVE_DNSSD
|
|||
|
# ifdef HAVE_MDNSRESPONDER
|
|||
|
/*
|
|||
|
* 'query_callback()' - Process query data.
|
|||
|
*/
|
|||
|
|
|||
|
static void
|
|||
|
query_callback(
|
|||
|
DNSServiceRef sdRef, /* I - Service reference */
|
|||
|
DNSServiceFlags flags, /* I - Data flags */
|
|||
|
uint32_t interfaceIndex, /* I - Interface */
|
|||
|
DNSServiceErrorType errorCode, /* I - Error, if any */
|
|||
|
const char *fullName, /* I - Full service name */
|
|||
|
uint16_t rrtype, /* I - Record type */
|
|||
|
uint16_t rrclass, /* I - Record class */
|
|||
|
uint16_t rdlen, /* I - Length of record data */
|
|||
|
const void *rdata, /* I - Record data */
|
|||
|
uint32_t ttl, /* I - Time-to-live */
|
|||
|
void *context) /* I - Device */
|
|||
|
{
|
|||
|
# else
|
|||
|
/*
|
|||
|
* 'query_callback()' - Process query data.
|
|||
|
*/
|
|||
|
|
|||
|
static void
|
|||
|
query_callback(
|
|||
|
AvahiRecordBrowser *browser, /* I - Record browser */
|
|||
|
AvahiIfIndex interfaceIndex,
|
|||
|
/* I - Interface index (unused) */
|
|||
|
AvahiProtocol protocol, /* I - Network protocol (unused) */
|
|||
|
AvahiBrowserEvent event, /* I - What happened? */
|
|||
|
const char *fullName, /* I - Service name */
|
|||
|
uint16_t rrclass, /* I - Record class */
|
|||
|
uint16_t rrtype, /* I - Record type */
|
|||
|
const void *rdata, /* I - TXT record */
|
|||
|
size_t rdlen, /* I - Length of TXT record */
|
|||
|
AvahiLookupResultFlags flags, /* I - Flags */
|
|||
|
void *context) /* I - Device */
|
|||
|
{
|
|||
|
AvahiClient *client = avahi_record_browser_get_client(browser);
|
|||
|
/* Client information */
|
|||
|
# endif /* HAVE_MDNSRESPONDER */
|
|||
|
char *ptr; /* Pointer into string */
|
|||
|
cups_device_t *device = (cups_device_t *)context;
|
|||
|
/* Device */
|
|||
|
const uint8_t *data, /* Pointer into data */
|
|||
|
*datanext, /* Next key/value pair */
|
|||
|
*dataend; /* End of entire TXT record */
|
|||
|
uint8_t datalen; /* Length of current key/value pair */
|
|||
|
char key[256], /* Key string */
|
|||
|
value[256], /* Value string */
|
|||
|
make_and_model[512], /* Manufacturer and model */
|
|||
|
model[256], /* Model */
|
|||
|
pdl[256], /* PDL */
|
|||
|
device_id[2048]; /* 1284 device ID */
|
|||
|
|
|||
|
|
|||
|
# ifdef HAVE_MDNSRESPONDER
|
|||
|
fprintf(stderr, "DEBUG2: query_callback(sdRef=%p, flags=%x, "
|
|||
|
"interfaceIndex=%u, errorCode=%d, fullName=\"%s\", "
|
|||
|
"rrtype=%u, rrclass=%u, rdlen=%u, rdata=%p, ttl=%u, "
|
|||
|
"context=%p)\n",
|
|||
|
sdRef, flags, interfaceIndex, errorCode, fullName, rrtype, rrclass, rdlen, rdata, ttl, context);
|
|||
|
|
|||
|
/*
|
|||
|
* Only process "add" data...
|
|||
|
*/
|
|||
|
|
|||
|
if (errorCode != kDNSServiceErr_NoError || !(flags & kDNSServiceFlagsAdd))
|
|||
|
return;
|
|||
|
|
|||
|
# else
|
|||
|
fprintf(stderr, "DEBUG2: query_callback(browser=%p, interfaceIndex=%u, "
|
|||
|
"protocol=%d, event=%d, fullName=\"%s\", rrclass=%u, "
|
|||
|
"rrtype=%u, rdata=%p, rdlen=%u, flags=%x, context=%p)\n",
|
|||
|
browser, interfaceIndex, protocol, event, fullName, rrclass, rrtype, rdata, (unsigned)rdlen, flags, context);
|
|||
|
|
|||
|
/*
|
|||
|
* Only process "add" data...
|
|||
|
*/
|
|||
|
|
|||
|
if (event != AVAHI_BROWSER_NEW)
|
|||
|
{
|
|||
|
if (event == AVAHI_BROWSER_FAILURE)
|
|||
|
fprintf(stderr, "ERROR: %s\n",
|
|||
|
avahi_strerror(avahi_client_errno(client)));
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
# endif /* HAVE_MDNSRESPONDER */
|
|||
|
|
|||
|
/*
|
|||
|
* Pull out the priority and make and model from the TXT
|
|||
|
* record and save it...
|
|||
|
*/
|
|||
|
|
|||
|
device_id[0] = '\0';
|
|||
|
make_and_model[0] = '\0';
|
|||
|
pdl[0] = '\0';
|
|||
|
|
|||
|
strlcpy(model, "Unknown", sizeof(model));
|
|||
|
|
|||
|
for (data = rdata, dataend = data + rdlen;
|
|||
|
data < dataend;
|
|||
|
data = datanext)
|
|||
|
{
|
|||
|
/*
|
|||
|
* Read a key/value pair starting with an 8-bit length. Since the
|
|||
|
* length is 8 bits and the size of the key/value buffers is 256, we
|
|||
|
* don't need to check for overflow...
|
|||
|
*/
|
|||
|
|
|||
|
datalen = *data++;
|
|||
|
|
|||
|
if (!datalen || (data + datalen) > dataend)
|
|||
|
break;
|
|||
|
|
|||
|
datanext = data + datalen;
|
|||
|
|
|||
|
for (ptr = key; data < datanext && *data != '='; data ++)
|
|||
|
*ptr++ = (char)*data;
|
|||
|
*ptr = '\0';
|
|||
|
|
|||
|
if (data < datanext && *data == '=')
|
|||
|
{
|
|||
|
data ++;
|
|||
|
|
|||
|
if (data < datanext)
|
|||
|
memcpy(value, data, (size_t)(datanext - data));
|
|||
|
value[datanext - data] = '\0';
|
|||
|
|
|||
|
fprintf(stderr, "DEBUG2: query_callback: \"%s=%s\".\n",
|
|||
|
key, value);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
fprintf(stderr, "DEBUG2: query_callback: \"%s\" with no value.\n",
|
|||
|
key);
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
if (!_cups_strncasecmp(key, "usb_", 4))
|
|||
|
{
|
|||
|
/*
|
|||
|
* Add USB device ID information...
|
|||
|
*/
|
|||
|
|
|||
|
ptr = device_id + strlen(device_id);
|
|||
|
snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%s:%s;", key + 4, value);
|
|||
|
}
|
|||
|
|
|||
|
if (!_cups_strcasecmp(key, "usb_MFG") || !_cups_strcasecmp(key, "usb_MANU") ||
|
|||
|
!_cups_strcasecmp(key, "usb_MANUFACTURER"))
|
|||
|
strlcpy(make_and_model, value, sizeof(make_and_model));
|
|||
|
else if (!_cups_strcasecmp(key, "usb_MDL") || !_cups_strcasecmp(key, "usb_MODEL"))
|
|||
|
strlcpy(model, value, sizeof(model));
|
|||
|
else if (!_cups_strcasecmp(key, "product") && !strstr(value, "Ghostscript"))
|
|||
|
{
|
|||
|
if (value[0] == '(')
|
|||
|
{
|
|||
|
/*
|
|||
|
* Strip parenthesis...
|
|||
|
*/
|
|||
|
|
|||
|
if ((ptr = value + strlen(value) - 1) > value && *ptr == ')')
|
|||
|
*ptr = '\0';
|
|||
|
|
|||
|
strlcpy(model, value + 1, sizeof(model));
|
|||
|
}
|
|||
|
else
|
|||
|
strlcpy(model, value, sizeof(model));
|
|||
|
}
|
|||
|
else if (!_cups_strcasecmp(key, "ty"))
|
|||
|
{
|
|||
|
strlcpy(model, value, sizeof(model));
|
|||
|
|
|||
|
if ((ptr = strchr(model, ',')) != NULL)
|
|||
|
*ptr = '\0';
|
|||
|
}
|
|||
|
else if (!_cups_strcasecmp(key, "pdl"))
|
|||
|
strlcpy(pdl, value, sizeof(pdl));
|
|||
|
else if (!_cups_strcasecmp(key, "priority"))
|
|||
|
device->priority = atoi(value);
|
|||
|
else if ((device->type == CUPS_DEVICE_IPP ||
|
|||
|
device->type == CUPS_DEVICE_IPPS ||
|
|||
|
device->type == CUPS_DEVICE_PRINTER) &&
|
|||
|
!_cups_strcasecmp(key, "printer-type"))
|
|||
|
{
|
|||
|
/*
|
|||
|
* This is a CUPS printer!
|
|||
|
*/
|
|||
|
|
|||
|
device->cups_shared = 1;
|
|||
|
|
|||
|
if (device->type == CUPS_DEVICE_PRINTER)
|
|||
|
device->sent = 1;
|
|||
|
}
|
|||
|
else if (!_cups_strcasecmp(key, "UUID"))
|
|||
|
device->uuid = strdup(value);
|
|||
|
}
|
|||
|
|
|||
|
if (device->device_id)
|
|||
|
free(device->device_id);
|
|||
|
|
|||
|
if (!device_id[0] && strcmp(model, "Unknown"))
|
|||
|
{
|
|||
|
if (make_and_model[0])
|
|||
|
snprintf(device_id, sizeof(device_id), "MFG:%s;MDL:%s;",
|
|||
|
make_and_model, model);
|
|||
|
else if (!_cups_strncasecmp(model, "designjet ", 10))
|
|||
|
snprintf(device_id, sizeof(device_id), "MFG:HP;MDL:%s;", model + 10);
|
|||
|
else if (!_cups_strncasecmp(model, "stylus ", 7))
|
|||
|
snprintf(device_id, sizeof(device_id), "MFG:EPSON;MDL:%s;", model + 7);
|
|||
|
else if ((ptr = strchr(model, ' ')) != NULL)
|
|||
|
{
|
|||
|
/*
|
|||
|
* Assume the first word is the make...
|
|||
|
*/
|
|||
|
|
|||
|
memcpy(make_and_model, model, (size_t)(ptr - model));
|
|||
|
make_and_model[ptr - model] = '\0';
|
|||
|
|
|||
|
snprintf(device_id, sizeof(device_id), "MFG:%s;MDL:%s;",
|
|||
|
make_and_model, ptr + 1);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (device_id[0] &&
|
|||
|
!strstr(device_id, "CMD:") &&
|
|||
|
!strstr(device_id, "COMMAND SET:") &&
|
|||
|
(strstr(pdl, "application/pdf") ||
|
|||
|
strstr(pdl, "application/postscript") ||
|
|||
|
strstr(pdl, "application/vnd.hp-PCL") ||
|
|||
|
strstr(pdl, "image/")))
|
|||
|
{
|
|||
|
value[0] = '\0';
|
|||
|
if (strstr(pdl, "application/pdf"))
|
|||
|
strlcat(value, ",PDF", sizeof(value));
|
|||
|
if (strstr(pdl, "application/postscript"))
|
|||
|
strlcat(value, ",PS", sizeof(value));
|
|||
|
if (strstr(pdl, "application/vnd.hp-PCL"))
|
|||
|
strlcat(value, ",PCL", sizeof(value));
|
|||
|
for (ptr = strstr(pdl, "image/"); ptr; ptr = strstr(ptr, "image/"))
|
|||
|
{
|
|||
|
char *valptr = value + strlen(value);
|
|||
|
/* Pointer into value */
|
|||
|
|
|||
|
if (valptr < (value + sizeof(value) - 1))
|
|||
|
*valptr++ = ',';
|
|||
|
|
|||
|
ptr += 6;
|
|||
|
while (isalnum(*ptr & 255) || *ptr == '-' || *ptr == '.')
|
|||
|
{
|
|||
|
if (isalnum(*ptr & 255) && valptr < (value + sizeof(value) - 1))
|
|||
|
*valptr++ = (char)toupper(*ptr++ & 255);
|
|||
|
else
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
*valptr = '\0';
|
|||
|
}
|
|||
|
|
|||
|
ptr = device_id + strlen(device_id);
|
|||
|
snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "CMD:%s;", value + 1);
|
|||
|
}
|
|||
|
|
|||
|
if (device_id[0])
|
|||
|
device->device_id = strdup(device_id);
|
|||
|
else
|
|||
|
device->device_id = NULL;
|
|||
|
|
|||
|
if (device->make_and_model)
|
|||
|
free(device->make_and_model);
|
|||
|
|
|||
|
if (make_and_model[0])
|
|||
|
{
|
|||
|
strlcat(make_and_model, " ", sizeof(make_and_model));
|
|||
|
strlcat(make_and_model, model, sizeof(make_and_model));
|
|||
|
|
|||
|
if (!_cups_strncasecmp(make_and_model, "EPSON EPSON ", 12))
|
|||
|
_cups_strcpy(make_and_model, make_and_model + 6);
|
|||
|
else if (!_cups_strncasecmp(make_and_model, "HP HP ", 6))
|
|||
|
_cups_strcpy(make_and_model, make_and_model + 3);
|
|||
|
else if (!_cups_strncasecmp(make_and_model, "Lexmark International Lexmark ", 30))
|
|||
|
_cups_strcpy(make_and_model, make_and_model + 22);
|
|||
|
|
|||
|
device->make_and_model = strdup(make_and_model);
|
|||
|
}
|
|||
|
else
|
|||
|
device->make_and_model = strdup(model);
|
|||
|
}
|
|||
|
#endif /* HAVE_DNSSD */
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* 'sigterm_handler()' - Handle termination signals.
|
|||
|
*/
|
|||
|
|
|||
|
static void
|
|||
|
sigterm_handler(int sig) /* I - Signal number (unused) */
|
|||
|
{
|
|||
|
(void)sig;
|
|||
|
|
|||
|
if (job_canceled)
|
|||
|
_exit(CUPS_BACKEND_OK);
|
|||
|
else
|
|||
|
job_canceled = 1;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* 'unquote()' - Unquote a name string.
|
|||
|
*/
|
|||
|
|
|||
|
static void
|
|||
|
unquote(char *dst, /* I - Destination buffer */
|
|||
|
const char *src, /* I - Source string */
|
|||
|
size_t dstsize) /* I - Size of destination buffer */
|
|||
|
{
|
|||
|
char *dstend = dst + dstsize - 1; /* End of destination buffer */
|
|||
|
|
|||
|
|
|||
|
while (*src && dst < dstend)
|
|||
|
{
|
|||
|
if (*src == '\\')
|
|||
|
{
|
|||
|
src ++;
|
|||
|
if (isdigit(src[0] & 255) && isdigit(src[1] & 255) &&
|
|||
|
isdigit(src[2] & 255))
|
|||
|
{
|
|||
|
*dst++ = ((((src[0] - '0') * 10) + src[1] - '0') * 10) + src[2] - '0';
|
|||
|
src += 3;
|
|||
|
}
|
|||
|
else
|
|||
|
*dst++ = *src++;
|
|||
|
}
|
|||
|
else
|
|||
|
*dst++ = *src ++;
|
|||
|
}
|
|||
|
|
|||
|
*dst = '\0';
|
|||
|
}
|