Blame backend/dnssd.c

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