mirror of https://gitee.com/openkylin/cups.git
1875 lines
46 KiB
C
1875 lines
46 KiB
C
/*
|
||
* Directory services routines for the CUPS scheduler.
|
||
*
|
||
* Copyright © 2007-2018 by Apple Inc.
|
||
* Copyright © 1997-2007 by Easy Software Products, all rights reserved.
|
||
*
|
||
* Licensed under Apache License v2.0. See the file "LICENSE" for more
|
||
* information.
|
||
*/
|
||
|
||
/*
|
||
* Include necessary headers...
|
||
*/
|
||
|
||
#include "cupsd.h"
|
||
#include <grp.h>
|
||
|
||
#if defined(HAVE_DNSSD) && defined(__APPLE__)
|
||
# include <nameser.h>
|
||
# include <CoreFoundation/CoreFoundation.h>
|
||
# include <SystemConfiguration/SystemConfiguration.h>
|
||
#endif /* HAVE_DNSSD && __APPLE__ */
|
||
|
||
|
||
/*
|
||
* Local globals...
|
||
*/
|
||
|
||
#ifdef HAVE_AVAHI
|
||
static int avahi_running = 0;
|
||
#endif /* HAVE_AVAHI */
|
||
|
||
|
||
/*
|
||
* Local functions...
|
||
*/
|
||
|
||
#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
|
||
static char *get_auth_info_required(cupsd_printer_t *p,
|
||
char *buffer, size_t bufsize);
|
||
#endif /* HAVE_DNSSD || HAVE_AVAHI */
|
||
#ifdef __APPLE__
|
||
static int get_hostconfig(const char *name);
|
||
#endif /* __APPLE__ */
|
||
static void update_lpd(int onoff);
|
||
static void update_smb(int onoff);
|
||
|
||
|
||
#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
|
||
# ifdef __APPLE__
|
||
static void dnssdAddAlias(const void *key, const void *value,
|
||
void *context);
|
||
# endif /* __APPLE__ */
|
||
static cupsd_txt_t dnssdBuildTxtRecord(cupsd_printer_t *p, int for_lpd);
|
||
# ifdef HAVE_AVAHI
|
||
static void dnssdClientCallback(AvahiClient *c, AvahiClientState state, void *userdata);
|
||
# endif /* HAVE_AVAHI */
|
||
static void dnssdDeregisterAllPrinters(int from_callback);
|
||
static void dnssdDeregisterInstance(cupsd_srv_t *srv, int from_callback);
|
||
static void dnssdDeregisterPrinter(cupsd_printer_t *p, int clear_name, int from_callback);
|
||
static const char *dnssdErrorString(int error);
|
||
static void dnssdFreeTxtRecord(cupsd_txt_t *txt);
|
||
static void dnssdRegisterAllPrinters(int from_callback);
|
||
# ifdef HAVE_DNSSD
|
||
static void dnssdRegisterCallback(DNSServiceRef sdRef,
|
||
DNSServiceFlags flags,
|
||
DNSServiceErrorType errorCode,
|
||
const char *name,
|
||
const char *regtype,
|
||
const char *domain,
|
||
void *context);
|
||
# else
|
||
static void dnssdRegisterCallback(AvahiEntryGroup *p,
|
||
AvahiEntryGroupState state,
|
||
void *context);
|
||
# endif /* HAVE_DNSSD */
|
||
static int dnssdRegisterInstance(cupsd_srv_t *srv, cupsd_printer_t *p, char *name, const char *type, const char *subtypes, int port, cupsd_txt_t *txt, int commit, int from_callback);
|
||
static void dnssdRegisterPrinter(cupsd_printer_t *p, int from_callback);
|
||
static void dnssdStop(void);
|
||
# ifdef HAVE_DNSSD
|
||
static void dnssdUpdate(void);
|
||
# endif /* HAVE_DNSSD */
|
||
static void dnssdUpdateDNSSDName(int from_callback);
|
||
#endif /* HAVE_DNSSD || HAVE_AVAHI */
|
||
|
||
|
||
/*
|
||
* 'cupsdDeregisterPrinter()' - Stop sending broadcast information for a
|
||
* local printer and remove any pending
|
||
* references to remote printers.
|
||
*/
|
||
|
||
void
|
||
cupsdDeregisterPrinter(
|
||
cupsd_printer_t *p, /* I - Printer to register */
|
||
int removeit) /* I - Printer being permanently removed */
|
||
{
|
||
/*
|
||
* Only deregister if browsing is enabled and it's a local printer...
|
||
*/
|
||
|
||
cupsdLogMessage(CUPSD_LOG_DEBUG,
|
||
"cupsdDeregisterPrinter(p=%p(%s), removeit=%d)", p, p->name,
|
||
removeit);
|
||
|
||
if (!Browsing || !p->shared ||
|
||
(p->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_SCANNER)))
|
||
return;
|
||
|
||
/*
|
||
* Announce the deletion...
|
||
*/
|
||
|
||
#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
|
||
if (removeit && (BrowseLocalProtocols & BROWSE_DNSSD) && DNSSDMaster)
|
||
dnssdDeregisterPrinter(p, 1, 0);
|
||
#endif /* HAVE_DNSSD || HAVE_AVAHI */
|
||
}
|
||
|
||
|
||
/*
|
||
* 'cupsdRegisterPrinter()' - Start sending broadcast information for a
|
||
* printer or update the broadcast contents.
|
||
*/
|
||
|
||
void
|
||
cupsdRegisterPrinter(cupsd_printer_t *p)/* I - Printer */
|
||
{
|
||
cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdRegisterPrinter(p=%p(%s))", p,
|
||
p->name);
|
||
|
||
if (!Browsing || !BrowseLocalProtocols ||
|
||
(p->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_SCANNER)))
|
||
return;
|
||
|
||
#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
|
||
if ((BrowseLocalProtocols & BROWSE_DNSSD) && DNSSDMaster)
|
||
dnssdRegisterPrinter(p, 0);
|
||
#endif /* HAVE_DNSSD || HAVE_AVAHI */
|
||
}
|
||
|
||
|
||
/*
|
||
* 'cupsdStartBrowsing()' - Start sending and receiving broadcast information.
|
||
*/
|
||
|
||
void
|
||
cupsdStartBrowsing(void)
|
||
{
|
||
if (!Browsing || !BrowseLocalProtocols)
|
||
return;
|
||
|
||
#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
|
||
if (BrowseLocalProtocols & BROWSE_DNSSD)
|
||
{
|
||
# ifdef HAVE_DNSSD
|
||
DNSServiceErrorType error; /* Error from service creation */
|
||
|
||
/*
|
||
* First create a "master" connection for all registrations...
|
||
*/
|
||
|
||
if ((error = DNSServiceCreateConnection(&DNSSDMaster))
|
||
!= kDNSServiceErr_NoError)
|
||
{
|
||
cupsdLogMessage(CUPSD_LOG_ERROR,
|
||
"Unable to create master DNS-SD reference: %d", error);
|
||
|
||
if (FatalErrors & CUPSD_FATAL_BROWSE)
|
||
cupsdEndProcess(getpid(), 0);
|
||
}
|
||
else
|
||
{
|
||
/*
|
||
* Add the master connection to the select list...
|
||
*/
|
||
|
||
int fd = DNSServiceRefSockFD(DNSSDMaster);
|
||
|
||
fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
|
||
|
||
cupsdAddSelect(fd, (cupsd_selfunc_t)dnssdUpdate, NULL, NULL);
|
||
}
|
||
|
||
/*
|
||
* Set the computer name and register the web interface...
|
||
*/
|
||
|
||
DNSSDPort = 0;
|
||
cupsdUpdateDNSSDName();
|
||
|
||
# else /* HAVE_AVAHI */
|
||
if ((DNSSDMaster = avahi_threaded_poll_new()) == NULL)
|
||
{
|
||
cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create DNS-SD thread.");
|
||
|
||
if (FatalErrors & CUPSD_FATAL_BROWSE)
|
||
cupsdEndProcess(getpid(), 0);
|
||
}
|
||
else
|
||
{
|
||
int error; /* Error code, if any */
|
||
|
||
DNSSDClient = avahi_client_new(avahi_threaded_poll_get(DNSSDMaster), AVAHI_CLIENT_NO_FAIL, dnssdClientCallback, NULL, &error);
|
||
|
||
if (DNSSDClient == NULL)
|
||
{
|
||
cupsdLogMessage(CUPSD_LOG_ERROR,
|
||
"Unable to communicate with avahi-daemon: %s",
|
||
dnssdErrorString(error));
|
||
|
||
if (FatalErrors & CUPSD_FATAL_BROWSE)
|
||
cupsdEndProcess(getpid(), 0);
|
||
|
||
avahi_threaded_poll_free(DNSSDMaster);
|
||
DNSSDMaster = NULL;
|
||
}
|
||
else
|
||
avahi_threaded_poll_start(DNSSDMaster);
|
||
}
|
||
# endif /* HAVE_DNSSD */
|
||
}
|
||
#endif /* HAVE_DNSSD || HAVE_AVAHI */
|
||
|
||
/*
|
||
* Enable LPD and SMB printer sharing as needed through external programs...
|
||
*/
|
||
|
||
if (BrowseLocalProtocols & BROWSE_LPD)
|
||
update_lpd(1);
|
||
|
||
if (BrowseLocalProtocols & BROWSE_SMB)
|
||
update_smb(1);
|
||
|
||
#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
|
||
/*
|
||
* Register the individual printers
|
||
*/
|
||
|
||
dnssdRegisterAllPrinters(0);
|
||
#endif /* HAVE_DNSSD || HAVE_AVAHI */
|
||
}
|
||
|
||
|
||
/*
|
||
* 'cupsdStopBrowsing()' - Stop sending and receiving broadcast information.
|
||
*/
|
||
|
||
void
|
||
cupsdStopBrowsing(void)
|
||
{
|
||
if (!Browsing || !BrowseLocalProtocols)
|
||
return;
|
||
|
||
#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
|
||
/*
|
||
* De-register the individual printers
|
||
*/
|
||
|
||
dnssdDeregisterAllPrinters(0);
|
||
|
||
/*
|
||
* Shut down browsing sockets...
|
||
*/
|
||
|
||
if ((BrowseLocalProtocols & BROWSE_DNSSD) && DNSSDMaster)
|
||
dnssdStop();
|
||
#endif /* HAVE_DNSSD || HAVE_AVAHI */
|
||
|
||
/*
|
||
* Disable LPD and SMB printer sharing as needed through external programs...
|
||
*/
|
||
|
||
if (BrowseLocalProtocols & BROWSE_LPD)
|
||
update_lpd(0);
|
||
|
||
if (BrowseLocalProtocols & BROWSE_SMB)
|
||
update_smb(0);
|
||
}
|
||
|
||
|
||
#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
|
||
/*
|
||
* 'cupsdUpdateDNSSDName()' - Update the computer name we use for browsing...
|
||
*/
|
||
|
||
void
|
||
cupsdUpdateDNSSDName(void)
|
||
{
|
||
dnssdUpdateDNSSDName(0);
|
||
}
|
||
|
||
|
||
# ifdef __APPLE__
|
||
/*
|
||
* 'dnssdAddAlias()' - Add a DNS-SD alias name.
|
||
*/
|
||
|
||
static void
|
||
dnssdAddAlias(const void *key, /* I - Key */
|
||
const void *value, /* I - Value (domain) */
|
||
void *context) /* I - Unused */
|
||
{
|
||
char valueStr[1024], /* Domain string */
|
||
hostname[1024], /* Complete hostname */
|
||
*hostptr; /* Pointer into hostname */
|
||
|
||
|
||
(void)key;
|
||
(void)context;
|
||
|
||
if (CFGetTypeID((CFStringRef)value) == CFStringGetTypeID() &&
|
||
CFStringGetCString((CFStringRef)value, valueStr, sizeof(valueStr),
|
||
kCFStringEncodingUTF8))
|
||
{
|
||
snprintf(hostname, sizeof(hostname), "%s.%s", DNSSDHostName, valueStr);
|
||
hostptr = hostname + strlen(hostname) - 1;
|
||
if (*hostptr == '.')
|
||
*hostptr = '\0'; /* Strip trailing dot */
|
||
|
||
if (!DNSSDAlias)
|
||
DNSSDAlias = cupsArrayNew(NULL, NULL);
|
||
|
||
cupsdAddAlias(DNSSDAlias, hostname);
|
||
cupsdLogMessage(CUPSD_LOG_DEBUG, "Added Back to My Mac ServerAlias %s",
|
||
hostname);
|
||
}
|
||
else
|
||
cupsdLogMessage(CUPSD_LOG_ERROR,
|
||
"Bad Back to My Mac domain in dynamic store!");
|
||
}
|
||
# endif /* __APPLE__ */
|
||
|
||
|
||
/*
|
||
* 'dnssdBuildTxtRecord()' - Build a TXT record from printer info.
|
||
*/
|
||
|
||
static cupsd_txt_t /* O - TXT record */
|
||
dnssdBuildTxtRecord(
|
||
cupsd_printer_t *p, /* I - Printer information */
|
||
int for_lpd) /* I - 1 = LPD, 0 = IPP */
|
||
{
|
||
int i, /* Looping var */
|
||
count; /* Count of key/value pairs */
|
||
char admin_hostname[256], /* Hostname for admin page */
|
||
adminurl_str[256], /* URL for the admin page */
|
||
type_str[32], /* Type to string buffer */
|
||
state_str[32], /* State to string buffer */
|
||
rp_str[1024], /* Queue name string buffer */
|
||
air_str[1024], /* auth-info-required string buffer */
|
||
*keyvalue[32][2], /* Table of key/value pairs */
|
||
*ptr; /* Pointer in string */
|
||
cupsd_txt_t txt; /* TXT record */
|
||
cupsd_listener_t *lis; /* Current listener */
|
||
const char *admin_scheme = "http"; /* Admin page URL scheme */
|
||
|
||
|
||
/*
|
||
* Load up the key value pairs...
|
||
*/
|
||
|
||
count = 0;
|
||
|
||
if (!for_lpd || (BrowseLocalProtocols & BROWSE_LPD))
|
||
{
|
||
keyvalue[count ][0] = "txtvers";
|
||
keyvalue[count++][1] = "1";
|
||
|
||
keyvalue[count ][0] = "qtotal";
|
||
keyvalue[count++][1] = "1";
|
||
|
||
keyvalue[count ][0] = "rp";
|
||
keyvalue[count++][1] = rp_str;
|
||
if (for_lpd)
|
||
strlcpy(rp_str, p->name, sizeof(rp_str));
|
||
else
|
||
snprintf(rp_str, sizeof(rp_str), "%s/%s",
|
||
(p->type & CUPS_PRINTER_CLASS) ? "classes" : "printers",
|
||
p->name);
|
||
|
||
keyvalue[count ][0] = "ty";
|
||
keyvalue[count++][1] = p->make_model ? p->make_model : "Unknown";
|
||
|
||
/*
|
||
* Get the hostname for the admin page...
|
||
*/
|
||
|
||
if (strchr(DNSSDHostName, '.'))
|
||
{
|
||
/*
|
||
* Use the provided hostname, but make sure it ends with a period...
|
||
*/
|
||
|
||
if ((ptr = DNSSDHostName + strlen(DNSSDHostName) - 1) >= DNSSDHostName && *ptr == '.')
|
||
strlcpy(admin_hostname, DNSSDHostName, sizeof(admin_hostname));
|
||
else
|
||
snprintf(admin_hostname, sizeof(admin_hostname), "%s.", DNSSDHostName);
|
||
}
|
||
else
|
||
{
|
||
/*
|
||
* Unqualified hostname gets ".local." added to it...
|
||
*/
|
||
|
||
snprintf(admin_hostname, sizeof(admin_hostname), "%s.local.", DNSSDHostName);
|
||
}
|
||
|
||
/*
|
||
* Get the URL scheme for the admin page...
|
||
*/
|
||
|
||
# ifdef HAVE_SSL
|
||
for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners); lis; lis = (cupsd_listener_t *)cupsArrayNext(Listeners))
|
||
{
|
||
if (lis->encryption != HTTP_ENCRYPTION_NEVER)
|
||
{
|
||
admin_scheme = "https";
|
||
break;
|
||
}
|
||
}
|
||
# endif /* HAVE_SSL */
|
||
|
||
httpAssembleURIf(HTTP_URI_CODING_ALL, adminurl_str, sizeof(adminurl_str), admin_scheme, NULL, admin_hostname, DNSSDPort, "/%s/%s", (p->type & CUPS_PRINTER_CLASS) ? "classes" : "printers", p->name);
|
||
keyvalue[count ][0] = "adminurl";
|
||
keyvalue[count++][1] = adminurl_str;
|
||
|
||
if (p->location)
|
||
{
|
||
keyvalue[count ][0] = "note";
|
||
keyvalue[count++][1] = p->location;
|
||
}
|
||
|
||
keyvalue[count ][0] = "priority";
|
||
keyvalue[count++][1] = for_lpd ? "100" : "0";
|
||
|
||
keyvalue[count ][0] = "product";
|
||
keyvalue[count++][1] = p->pc && p->pc->product ? p->pc->product : "Unknown";
|
||
|
||
keyvalue[count ][0] = "pdl";
|
||
keyvalue[count++][1] = p->pdl ? p->pdl : "application/postscript";
|
||
|
||
/* iOS 6 does not accept this printer as AirPrint printer if there is
|
||
no URF txt record or "URF=none", "DM3" is the minimum needed found
|
||
by try and error */
|
||
keyvalue[count ][0] = "URF";
|
||
keyvalue[count++][1] = "DM3";
|
||
|
||
if (get_auth_info_required(p, air_str, sizeof(air_str)))
|
||
{
|
||
keyvalue[count ][0] = "air";
|
||
keyvalue[count++][1] = air_str;
|
||
}
|
||
|
||
keyvalue[count ][0] = "UUID";
|
||
keyvalue[count++][1] = p->uuid + 9;
|
||
|
||
#ifdef HAVE_SSL
|
||
keyvalue[count ][0] = "TLS";
|
||
keyvalue[count++][1] = "1.2";
|
||
#endif /* HAVE_SSL */
|
||
|
||
if (p->type & CUPS_PRINTER_FAX)
|
||
{
|
||
keyvalue[count ][0] = "Fax";
|
||
keyvalue[count++][1] = "T";
|
||
keyvalue[count ][0] = "rfo";
|
||
keyvalue[count++][1] = rp_str;
|
||
}
|
||
|
||
if (p->type & CUPS_PRINTER_COLOR)
|
||
{
|
||
keyvalue[count ][0] = "Color";
|
||
keyvalue[count++][1] = (p->type & CUPS_PRINTER_COLOR) ? "T" : "F";
|
||
}
|
||
|
||
if (p->type & CUPS_PRINTER_DUPLEX)
|
||
{
|
||
keyvalue[count ][0] = "Duplex";
|
||
keyvalue[count++][1] = (p->type & CUPS_PRINTER_DUPLEX) ? "T" : "F";
|
||
}
|
||
|
||
if (p->type & CUPS_PRINTER_STAPLE)
|
||
{
|
||
keyvalue[count ][0] = "Staple";
|
||
keyvalue[count++][1] = (p->type & CUPS_PRINTER_STAPLE) ? "T" : "F";
|
||
}
|
||
|
||
if (p->type & CUPS_PRINTER_COPIES)
|
||
{
|
||
keyvalue[count ][0] = "Copies";
|
||
keyvalue[count++][1] = (p->type & CUPS_PRINTER_COPIES) ? "T" : "F";
|
||
}
|
||
|
||
if (p->type & CUPS_PRINTER_COLLATE)
|
||
{
|
||
keyvalue[count ][0] = "Collate";
|
||
keyvalue[count++][1] = (p->type & CUPS_PRINTER_COLLATE) ? "T" : "F";
|
||
}
|
||
|
||
if (p->type & CUPS_PRINTER_PUNCH)
|
||
{
|
||
keyvalue[count ][0] = "Punch";
|
||
keyvalue[count++][1] = (p->type & CUPS_PRINTER_PUNCH) ? "T" : "F";
|
||
}
|
||
|
||
if (p->type & CUPS_PRINTER_BIND)
|
||
{
|
||
keyvalue[count ][0] = "Bind";
|
||
keyvalue[count++][1] = (p->type & CUPS_PRINTER_BIND) ? "T" : "F";
|
||
}
|
||
|
||
if (p->type & CUPS_PRINTER_SORT)
|
||
{
|
||
keyvalue[count ][0] = "Sort";
|
||
keyvalue[count++][1] = (p->type & CUPS_PRINTER_SORT) ? "T" : "F";
|
||
}
|
||
|
||
if (p->type & CUPS_PRINTER_MFP)
|
||
{
|
||
keyvalue[count ][0] = "Scan";
|
||
keyvalue[count++][1] = (p->type & CUPS_PRINTER_MFP) ? "T" : "F";
|
||
}
|
||
|
||
snprintf(type_str, sizeof(type_str), "0x%X", p->type | CUPS_PRINTER_REMOTE);
|
||
snprintf(state_str, sizeof(state_str), "%d", p->state);
|
||
|
||
keyvalue[count ][0] = "printer-state";
|
||
keyvalue[count++][1] = state_str;
|
||
|
||
keyvalue[count ][0] = "printer-type";
|
||
keyvalue[count++][1] = type_str;
|
||
}
|
||
|
||
/*
|
||
* Then pack them into a proper txt record...
|
||
*/
|
||
|
||
# ifdef HAVE_DNSSD
|
||
TXTRecordCreate(&txt, 0, NULL);
|
||
|
||
for (i = 0; i < count; i ++)
|
||
{
|
||
size_t len = strlen(keyvalue[i][1]);
|
||
|
||
if (len < 256)
|
||
TXTRecordSetValue(&txt, keyvalue[i][0], (uint8_t)len, keyvalue[i][1]);
|
||
}
|
||
|
||
# else
|
||
for (i = 0, txt = NULL; i < count; i ++)
|
||
txt = avahi_string_list_add_printf(txt, "%s=%s", keyvalue[i][0],
|
||
keyvalue[i][1]);
|
||
# endif /* HAVE_DNSSD */
|
||
|
||
return (txt);
|
||
}
|
||
|
||
|
||
# ifdef HAVE_AVAHI
|
||
/*
|
||
* 'dnssdClientCallback()' - Client callback for Avahi.
|
||
*
|
||
* Called whenever the client or server state changes...
|
||
*/
|
||
|
||
static void
|
||
dnssdClientCallback(
|
||
AvahiClient *c, /* I - Client */
|
||
AvahiClientState state, /* I - Current state */
|
||
void *userdata) /* I - User data (unused) */
|
||
{
|
||
int error; /* Error code, if any */
|
||
|
||
|
||
(void)userdata;
|
||
|
||
if (!c)
|
||
return;
|
||
|
||
/*
|
||
* Make sure DNSSDClient is already set also if this callback function is
|
||
* already running before avahi_client_new() in dnssdStartBrowsing()
|
||
* finishes.
|
||
*/
|
||
|
||
if (!DNSSDClient)
|
||
DNSSDClient = c;
|
||
|
||
switch (state)
|
||
{
|
||
case AVAHI_CLIENT_S_REGISTERING:
|
||
case AVAHI_CLIENT_S_RUNNING:
|
||
case AVAHI_CLIENT_S_COLLISION:
|
||
cupsdLogMessage(CUPSD_LOG_DEBUG, "Avahi server connection now available, registering printers for Bonjour broadcasting.");
|
||
|
||
/*
|
||
* Mark that Avahi server is running...
|
||
*/
|
||
|
||
avahi_running = 1;
|
||
|
||
/*
|
||
* Set the computer name and register the web interface...
|
||
*/
|
||
|
||
DNSSDPort = 0;
|
||
dnssdUpdateDNSSDName(1);
|
||
|
||
/*
|
||
* Register the individual printers
|
||
*/
|
||
|
||
dnssdRegisterAllPrinters(1);
|
||
break;
|
||
|
||
case AVAHI_CLIENT_FAILURE:
|
||
if (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED)
|
||
{
|
||
cupsdLogMessage(CUPSD_LOG_DEBUG, "Avahi server disappeared, unregistering printers for Bonjour broadcasting.");
|
||
|
||
/*
|
||
* Unregister everything and close the client...
|
||
*/
|
||
|
||
dnssdDeregisterAllPrinters(1);
|
||
dnssdDeregisterInstance(&WebIFSrv, 1);
|
||
avahi_client_free(DNSSDClient);
|
||
DNSSDClient = NULL;
|
||
|
||
/*
|
||
* Mark that Avahi server is not running...
|
||
*/
|
||
|
||
avahi_running = 0;
|
||
|
||
/*
|
||
* Renew Avahi client...
|
||
*/
|
||
|
||
DNSSDClient = avahi_client_new(avahi_threaded_poll_get(DNSSDMaster), AVAHI_CLIENT_NO_FAIL, dnssdClientCallback, NULL, &error);
|
||
|
||
if (!DNSSDClient)
|
||
{
|
||
cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to communicate with avahi-daemon: %s", dnssdErrorString(error));
|
||
if (FatalErrors & CUPSD_FATAL_BROWSE)
|
||
cupsdEndProcess(getpid(), 0);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
cupsdLogMessage(CUPSD_LOG_ERROR, "Communication with avahi-daemon has failed: %s", avahi_strerror(avahi_client_errno(c)));
|
||
if (FatalErrors & CUPSD_FATAL_BROWSE)
|
||
cupsdEndProcess(getpid(), 0);
|
||
}
|
||
break;
|
||
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
# endif /* HAVE_AVAHI */
|
||
|
||
|
||
/*
|
||
* 'dnssdDeregisterAllPrinters()' - Deregister all printers.
|
||
*/
|
||
|
||
static void
|
||
dnssdDeregisterAllPrinters(
|
||
int from_callback) /* I - Deregistering because of callback? */
|
||
{
|
||
cupsd_printer_t *p; /* Current printer */
|
||
|
||
|
||
if (!DNSSDMaster)
|
||
return;
|
||
|
||
for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
|
||
p;
|
||
p = (cupsd_printer_t *)cupsArrayNext(Printers))
|
||
if (!(p->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_SCANNER)))
|
||
dnssdDeregisterPrinter(p, 1, from_callback);
|
||
}
|
||
|
||
|
||
/*
|
||
* 'dnssdDeregisterInstance()' - Deregister a DNS-SD service instance.
|
||
*/
|
||
|
||
static void
|
||
dnssdDeregisterInstance(
|
||
cupsd_srv_t *srv, /* I - Service */
|
||
int from_callback) /* I - Called from callback? */
|
||
{
|
||
if (!srv || !*srv)
|
||
return;
|
||
|
||
# ifdef HAVE_DNSSD
|
||
(void)from_callback;
|
||
|
||
DNSServiceRefDeallocate(*srv);
|
||
|
||
*srv = NULL;
|
||
|
||
# else /* HAVE_AVAHI */
|
||
if (!from_callback)
|
||
avahi_threaded_poll_lock(DNSSDMaster);
|
||
|
||
if (*srv)
|
||
{
|
||
avahi_entry_group_free(*srv);
|
||
*srv = NULL;
|
||
}
|
||
|
||
if (!from_callback)
|
||
avahi_threaded_poll_unlock(DNSSDMaster);
|
||
# endif /* HAVE_DNSSD */
|
||
}
|
||
|
||
|
||
/*
|
||
* 'dnssdDeregisterPrinter()' - Deregister all services for a printer.
|
||
*/
|
||
|
||
static void
|
||
dnssdDeregisterPrinter(
|
||
cupsd_printer_t *p, /* I - Printer */
|
||
int clear_name, /* I - Clear the name? */
|
||
int from_callback) /* I - Called from callback? */
|
||
|
||
{
|
||
cupsdLogMessage(CUPSD_LOG_DEBUG2,
|
||
"dnssdDeregisterPrinter(p=%p(%s), clear_name=%d)", p, p->name,
|
||
clear_name);
|
||
|
||
if (p->ipp_srv)
|
||
{
|
||
dnssdDeregisterInstance(&p->ipp_srv, from_callback);
|
||
|
||
# ifdef HAVE_DNSSD
|
||
# ifdef HAVE_SSL
|
||
dnssdDeregisterInstance(&p->ipps_srv, from_callback);
|
||
# endif /* HAVE_SSL */
|
||
dnssdDeregisterInstance(&p->printer_srv, from_callback);
|
||
# endif /* HAVE_DNSSD */
|
||
}
|
||
|
||
/*
|
||
* Remove the printer from the array of DNS-SD printers but keep the
|
||
* registered name...
|
||
*/
|
||
|
||
cupsArrayRemove(DNSSDPrinters, p);
|
||
|
||
/*
|
||
* Optionally clear the service name...
|
||
*/
|
||
|
||
if (clear_name)
|
||
cupsdClearString(&p->reg_name);
|
||
}
|
||
|
||
|
||
/*
|
||
* 'dnssdErrorString()' - Return an error string for an error code.
|
||
*/
|
||
|
||
static const char * /* O - Error message */
|
||
dnssdErrorString(int error) /* I - Error number */
|
||
{
|
||
# ifdef HAVE_DNSSD
|
||
switch (error)
|
||
{
|
||
case kDNSServiceErr_NoError :
|
||
return ("OK.");
|
||
|
||
default :
|
||
case kDNSServiceErr_Unknown :
|
||
return ("Unknown error.");
|
||
|
||
case kDNSServiceErr_NoSuchName :
|
||
return ("Service not found.");
|
||
|
||
case kDNSServiceErr_NoMemory :
|
||
return ("Out of memory.");
|
||
|
||
case kDNSServiceErr_BadParam :
|
||
return ("Bad parameter.");
|
||
|
||
case kDNSServiceErr_BadReference :
|
||
return ("Bad service reference.");
|
||
|
||
case kDNSServiceErr_BadState :
|
||
return ("Bad state.");
|
||
|
||
case kDNSServiceErr_BadFlags :
|
||
return ("Bad flags.");
|
||
|
||
case kDNSServiceErr_Unsupported :
|
||
return ("Unsupported.");
|
||
|
||
case kDNSServiceErr_NotInitialized :
|
||
return ("Not initialized.");
|
||
|
||
case kDNSServiceErr_AlreadyRegistered :
|
||
return ("Already registered.");
|
||
|
||
case kDNSServiceErr_NameConflict :
|
||
return ("Name conflict.");
|
||
|
||
case kDNSServiceErr_Invalid :
|
||
return ("Invalid name.");
|
||
|
||
case kDNSServiceErr_Firewall :
|
||
return ("Firewall prevents registration.");
|
||
|
||
case kDNSServiceErr_Incompatible :
|
||
return ("Client library incompatible.");
|
||
|
||
case kDNSServiceErr_BadInterfaceIndex :
|
||
return ("Bad interface index.");
|
||
|
||
case kDNSServiceErr_Refused :
|
||
return ("Server prevents registration.");
|
||
|
||
case kDNSServiceErr_NoSuchRecord :
|
||
return ("Record not found.");
|
||
|
||
case kDNSServiceErr_NoAuth :
|
||
return ("Authentication required.");
|
||
|
||
case kDNSServiceErr_NoSuchKey :
|
||
return ("Encryption key not found.");
|
||
|
||
case kDNSServiceErr_NATTraversal :
|
||
return ("Unable to traverse NAT boundary.");
|
||
|
||
case kDNSServiceErr_DoubleNAT :
|
||
return ("Unable to traverse double-NAT boundary.");
|
||
|
||
case kDNSServiceErr_BadTime :
|
||
return ("Bad system time.");
|
||
|
||
case kDNSServiceErr_BadSig :
|
||
return ("Bad signature.");
|
||
|
||
case kDNSServiceErr_BadKey :
|
||
return ("Bad encryption key.");
|
||
|
||
case kDNSServiceErr_Transient :
|
||
return ("Transient error occurred - please try again.");
|
||
|
||
case kDNSServiceErr_ServiceNotRunning :
|
||
return ("Server not running.");
|
||
|
||
case kDNSServiceErr_NATPortMappingUnsupported :
|
||
return ("NAT doesn't support NAT-PMP or UPnP.");
|
||
|
||
case kDNSServiceErr_NATPortMappingDisabled :
|
||
return ("NAT supports NAT-PNP or UPnP but it is disabled.");
|
||
|
||
case kDNSServiceErr_NoRouter :
|
||
return ("No Internet/default router configured.");
|
||
|
||
case kDNSServiceErr_PollingMode :
|
||
return ("Service polling mode error.");
|
||
|
||
case kDNSServiceErr_Timeout :
|
||
return ("Service timeout.");
|
||
}
|
||
|
||
# else /* HAVE_AVAHI */
|
||
return (avahi_strerror(error));
|
||
# endif /* HAVE_DNSSD */
|
||
}
|
||
|
||
|
||
/*
|
||
* 'dnssdRegisterCallback()' - Free a TXT record.
|
||
*/
|
||
|
||
static void
|
||
dnssdFreeTxtRecord(cupsd_txt_t *txt) /* I - TXT record */
|
||
{
|
||
# ifdef HAVE_DNSSD
|
||
TXTRecordDeallocate(txt);
|
||
|
||
# else /* HAVE_AVAHI */
|
||
avahi_string_list_free(*txt);
|
||
*txt = NULL;
|
||
# endif /* HAVE_DNSSD */
|
||
}
|
||
|
||
|
||
/*
|
||
* 'dnssdRegisterAllPrinters()' - Register all printers.
|
||
*/
|
||
|
||
static void
|
||
dnssdRegisterAllPrinters(int from_callback) /* I - Called from callback? */
|
||
{
|
||
cupsd_printer_t *p; /* Current printer */
|
||
|
||
|
||
if (!DNSSDMaster)
|
||
return;
|
||
|
||
for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
|
||
p;
|
||
p = (cupsd_printer_t *)cupsArrayNext(Printers))
|
||
if (!(p->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_SCANNER)))
|
||
dnssdRegisterPrinter(p, from_callback);
|
||
}
|
||
|
||
|
||
/*
|
||
* 'dnssdRegisterCallback()' - DNSServiceRegister callback.
|
||
*/
|
||
|
||
# ifdef HAVE_DNSSD
|
||
static void
|
||
dnssdRegisterCallback(
|
||
DNSServiceRef sdRef, /* I - DNS Service reference */
|
||
DNSServiceFlags flags, /* I - Reserved for future use */
|
||
DNSServiceErrorType errorCode, /* I - Error code */
|
||
const char *name, /* I - Service name */
|
||
const char *regtype, /* I - Service type */
|
||
const char *domain, /* I - Domain. ".local" for now */
|
||
void *context) /* I - Printer */
|
||
{
|
||
cupsd_printer_t *p = (cupsd_printer_t *)context;
|
||
/* Current printer */
|
||
|
||
|
||
(void)sdRef;
|
||
(void)flags;
|
||
(void)domain;
|
||
|
||
cupsdLogMessage(CUPSD_LOG_DEBUG2, "dnssdRegisterCallback(%s, %s) for %s (%s)",
|
||
name, regtype, p ? p->name : "Web Interface",
|
||
p ? (p->reg_name ? p->reg_name : "(null)") : "NA");
|
||
|
||
if (errorCode)
|
||
{
|
||
cupsdLogMessage(CUPSD_LOG_ERROR,
|
||
"DNSServiceRegister failed with error %d", (int)errorCode);
|
||
return;
|
||
}
|
||
else if (p && (!p->reg_name || _cups_strcasecmp(name, p->reg_name)))
|
||
{
|
||
cupsdLogMessage(CUPSD_LOG_INFO, "Using service name \"%s\" for \"%s\"",
|
||
name, p->name);
|
||
|
||
cupsArrayRemove(DNSSDPrinters, p);
|
||
cupsdSetString(&p->reg_name, name);
|
||
cupsArrayAdd(DNSSDPrinters, p);
|
||
|
||
LastEvent |= CUPSD_EVENT_PRINTER_MODIFIED;
|
||
}
|
||
}
|
||
|
||
# else /* HAVE_AVAHI */
|
||
static void
|
||
dnssdRegisterCallback(
|
||
AvahiEntryGroup *srv, /* I - Service */
|
||
AvahiEntryGroupState state, /* I - Registration state */
|
||
void *context) /* I - Printer */
|
||
{
|
||
cupsd_printer_t *p = (cupsd_printer_t *)context;
|
||
/* Current printer */
|
||
|
||
cupsdLogMessage(CUPSD_LOG_DEBUG2,
|
||
"dnssdRegisterCallback(srv=%p, state=%d, context=%p) "
|
||
"for %s (%s)", srv, state, context,
|
||
p ? p->name : "Web Interface",
|
||
p ? (p->reg_name ? p->reg_name : "(null)") : "NA");
|
||
|
||
/* TODO: Handle collisions with avahi_alternate_service_name(p->reg_name)? */
|
||
}
|
||
# endif /* HAVE_DNSSD */
|
||
|
||
|
||
/*
|
||
* 'dnssdRegisterInstance()' - Register an instance of a printer service.
|
||
*/
|
||
|
||
static int /* O - 1 on success, 0 on failure */
|
||
dnssdRegisterInstance(
|
||
cupsd_srv_t *srv, /* O - Service */
|
||
cupsd_printer_t *p, /* I - Printer */
|
||
char *name, /* I - DNS-SD service name */
|
||
const char *type, /* I - DNS-SD service type */
|
||
const char *subtypes, /* I - Subtypes to register or NULL */
|
||
int port, /* I - Port number or 0 */
|
||
cupsd_txt_t *txt, /* I - TXT record */
|
||
int commit, /* I - Commit registration? */
|
||
int from_callback) /* I - Called from callback? */
|
||
{
|
||
char temp[256], /* Temporary string */
|
||
*ptr; /* Pointer into string */
|
||
int error; /* Any error */
|
||
|
||
|
||
# ifdef HAVE_DNSSD
|
||
(void)from_callback;
|
||
# endif /* HAVE_DNSSD */
|
||
|
||
cupsdLogMessage(CUPSD_LOG_DEBUG, "Registering \"%s\" with DNS-SD type \"%s\".", name, type);
|
||
|
||
if (p && !srv)
|
||
{
|
||
/*
|
||
* Assign the correct pointer for "srv"...
|
||
*/
|
||
|
||
# ifdef HAVE_DNSSD
|
||
if (!strcmp(type, "_printer._tcp"))
|
||
srv = &p->printer_srv; /* Target LPD service */
|
||
# ifdef HAVE_SSL
|
||
else if (!strcmp(type, "_ipps._tcp"))
|
||
srv = &p->ipps_srv; /* Target IPPS service */
|
||
# endif /* HAVE_SSL */
|
||
else
|
||
srv = &p->ipp_srv; /* Target IPP service */
|
||
|
||
# else /* HAVE_AVAHI */
|
||
srv = &p->ipp_srv; /* Target service group */
|
||
# endif /* HAVE_DNSSD */
|
||
}
|
||
|
||
# ifdef HAVE_DNSSD
|
||
(void)commit;
|
||
|
||
# else /* HAVE_AVAHI */
|
||
if (!from_callback)
|
||
avahi_threaded_poll_lock(DNSSDMaster);
|
||
|
||
if (!*srv)
|
||
*srv = avahi_entry_group_new(DNSSDClient, dnssdRegisterCallback, NULL);
|
||
if (!*srv)
|
||
{
|
||
if (!from_callback)
|
||
avahi_threaded_poll_unlock(DNSSDMaster);
|
||
|
||
cupsdLogMessage(CUPSD_LOG_WARN, "DNS-SD registration of \"%s\" failed: %s",
|
||
name, dnssdErrorString(avahi_client_errno(DNSSDClient)));
|
||
return (0);
|
||
}
|
||
# endif /* HAVE_DNSSD */
|
||
|
||
/*
|
||
* Make sure the name is <= 63 octets, and when we truncate be sure to
|
||
* properly truncate any UTF-8 characters...
|
||
*/
|
||
|
||
ptr = name + strlen(name);
|
||
while ((ptr - name) > 63)
|
||
{
|
||
do
|
||
{
|
||
ptr --;
|
||
}
|
||
while (ptr > name && (*ptr & 0xc0) == 0x80);
|
||
|
||
if (ptr > name)
|
||
*ptr = '\0';
|
||
}
|
||
|
||
/*
|
||
* Register the service...
|
||
*/
|
||
|
||
# ifdef HAVE_DNSSD
|
||
if (subtypes)
|
||
snprintf(temp, sizeof(temp), "%s,%s", type, subtypes);
|
||
else
|
||
strlcpy(temp, type, sizeof(temp));
|
||
|
||
*srv = DNSSDMaster;
|
||
error = DNSServiceRegister(srv, kDNSServiceFlagsShareConnection,
|
||
0, name, temp, NULL, DNSSDHostName, htons(port),
|
||
txt ? TXTRecordGetLength(txt) : 0,
|
||
txt ? TXTRecordGetBytesPtr(txt) : NULL,
|
||
dnssdRegisterCallback, p);
|
||
|
||
# else /* HAVE_AVAHI */
|
||
if (txt)
|
||
{
|
||
AvahiStringList *temptxt;
|
||
for (temptxt = *txt; temptxt; temptxt = temptxt->next)
|
||
cupsdLogMessage(CUPSD_LOG_DEBUG, "DNS_SD \"%s\" %s", name, temptxt->text);
|
||
}
|
||
|
||
error = avahi_entry_group_add_service_strlst(*srv, AVAHI_IF_UNSPEC,
|
||
AVAHI_PROTO_UNSPEC, 0, name,
|
||
type, NULL, DNSSDHostName, port,
|
||
txt ? *txt : NULL);
|
||
if (error)
|
||
cupsdLogMessage(CUPSD_LOG_DEBUG, "DNS-SD service add for \"%s\" failed.",
|
||
name);
|
||
|
||
if (!error && subtypes)
|
||
{
|
||
/*
|
||
* Register all of the subtypes...
|
||
*/
|
||
|
||
char *start, /* Start of subtype */
|
||
subtype[256]; /* Subtype string */
|
||
|
||
strlcpy(temp, subtypes, sizeof(temp));
|
||
|
||
for (start = temp; *start; start = ptr)
|
||
{
|
||
/*
|
||
* Skip leading whitespace...
|
||
*/
|
||
|
||
while (*start && isspace(*start & 255))
|
||
start ++;
|
||
|
||
/*
|
||
* Grab everything up to the next comma or the end of the string...
|
||
*/
|
||
|
||
for (ptr = start; *ptr && *ptr != ','; ptr ++);
|
||
|
||
if (*ptr)
|
||
*ptr++ = '\0';
|
||
|
||
if (!*start)
|
||
break;
|
||
|
||
/*
|
||
* Register the subtype...
|
||
*/
|
||
|
||
snprintf(subtype, sizeof(subtype), "%s._sub.%s", start, type);
|
||
|
||
error = avahi_entry_group_add_service_subtype(*srv, AVAHI_IF_UNSPEC,
|
||
AVAHI_PROTO_UNSPEC, 0,
|
||
name, type, NULL, subtype);
|
||
if (error)
|
||
{
|
||
cupsdLogMessage(CUPSD_LOG_DEBUG,
|
||
"DNS-SD subtype %s registration for \"%s\" failed." ,
|
||
subtype, name);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!error && commit)
|
||
{
|
||
if ((error = avahi_entry_group_commit(*srv)) != 0)
|
||
cupsdLogMessage(CUPSD_LOG_DEBUG, "DNS-SD commit of \"%s\" failed.",
|
||
name);
|
||
}
|
||
|
||
if (!from_callback)
|
||
avahi_threaded_poll_unlock(DNSSDMaster);
|
||
# endif /* HAVE_DNSSD */
|
||
|
||
if (error)
|
||
{
|
||
cupsdLogMessage(CUPSD_LOG_WARN, "DNS-SD registration of \"%s\" failed: %s",
|
||
name, dnssdErrorString(error));
|
||
cupsdLogMessage(CUPSD_LOG_DEBUG, "DNS-SD type: %s", type);
|
||
if (subtypes)
|
||
cupsdLogMessage(CUPSD_LOG_DEBUG, "DNS-SD sub-types: %s", subtypes);
|
||
}
|
||
|
||
return (!error);
|
||
}
|
||
|
||
|
||
/*
|
||
* 'dnssdRegisterPrinter()' - Start sending broadcast information for a printer
|
||
* or update the broadcast contents.
|
||
*/
|
||
|
||
static void
|
||
dnssdRegisterPrinter(
|
||
cupsd_printer_t *p, /* I - Printer */
|
||
int from_callback) /* I - Called from callback? */
|
||
{
|
||
char name[256]; /* Service name */
|
||
int printer_port; /* LPD port number */
|
||
int status; /* Registration status */
|
||
cupsd_txt_t ipp_txt, /* IPP(S) TXT record */
|
||
printer_txt; /* LPD TXT record */
|
||
|
||
|
||
cupsdLogMessage(CUPSD_LOG_DEBUG2, "dnssdRegisterPrinter(%s) %s", p->name,
|
||
!p->ipp_srv ? "new" : "update");
|
||
|
||
# ifdef HAVE_AVAHI
|
||
if (!avahi_running)
|
||
return;
|
||
# endif /* HAVE_AVAHI */
|
||
|
||
/*
|
||
* Remove the current registrations if we have them and then return if
|
||
* per-printer sharing was just disabled...
|
||
*/
|
||
|
||
dnssdDeregisterPrinter(p, 0, from_callback);
|
||
|
||
if (!p->shared)
|
||
return;
|
||
|
||
/*
|
||
* Set the registered name as needed; the registered name takes the form of
|
||
* "<printer-info> @ <computer name>"...
|
||
*/
|
||
|
||
if (!p->reg_name)
|
||
{
|
||
if (p->info && strlen(p->info) > 0)
|
||
{
|
||
if (DNSSDComputerName)
|
||
snprintf(name, sizeof(name), "%s @ %s", p->info, DNSSDComputerName);
|
||
else
|
||
strlcpy(name, p->info, sizeof(name));
|
||
}
|
||
else if (DNSSDComputerName)
|
||
snprintf(name, sizeof(name), "%s @ %s", p->name, DNSSDComputerName);
|
||
else
|
||
strlcpy(name, p->name, sizeof(name));
|
||
}
|
||
else
|
||
strlcpy(name, p->reg_name, sizeof(name));
|
||
|
||
/*
|
||
* Register IPP and LPD...
|
||
*
|
||
* We always must register the "_printer" service type in order to reserve
|
||
* our name, but use port number 0 if we haven't actually configured cups-lpd
|
||
* to share via LPD...
|
||
*/
|
||
|
||
ipp_txt = dnssdBuildTxtRecord(p, 0);
|
||
printer_txt = dnssdBuildTxtRecord(p, 1);
|
||
|
||
if (BrowseLocalProtocols & BROWSE_LPD)
|
||
printer_port = 515;
|
||
else
|
||
printer_port = 0;
|
||
|
||
status = dnssdRegisterInstance(NULL, p, name, "_printer._tcp", NULL, printer_port, &printer_txt, 0, from_callback);
|
||
|
||
# ifdef HAVE_SSL
|
||
if (status)
|
||
dnssdRegisterInstance(NULL, p, name, "_ipps._tcp", DNSSDSubTypes, DNSSDPort, &ipp_txt, 0, from_callback);
|
||
# endif /* HAVE_SSL */
|
||
|
||
if (status)
|
||
{
|
||
/*
|
||
* Use the "_fax-ipp" service type for fax queues, otherwise use "_ipp"...
|
||
*/
|
||
|
||
if (p->type & CUPS_PRINTER_FAX)
|
||
status = dnssdRegisterInstance(NULL, p, name, "_fax-ipp._tcp", DNSSDSubTypes, DNSSDPort, &ipp_txt, 1, from_callback);
|
||
else
|
||
status = dnssdRegisterInstance(NULL, p, name, "_ipp._tcp", DNSSDSubTypes, DNSSDPort, &ipp_txt, 1, from_callback);
|
||
}
|
||
|
||
dnssdFreeTxtRecord(&ipp_txt);
|
||
dnssdFreeTxtRecord(&printer_txt);
|
||
|
||
if (status)
|
||
{
|
||
/*
|
||
* Save the registered name and add the printer to the array of DNS-SD
|
||
* printers...
|
||
*/
|
||
|
||
cupsdSetString(&p->reg_name, name);
|
||
cupsArrayAdd(DNSSDPrinters, p);
|
||
}
|
||
else
|
||
{
|
||
/*
|
||
* Registration failed for this printer...
|
||
*/
|
||
|
||
dnssdDeregisterInstance(&p->ipp_srv, from_callback);
|
||
|
||
# ifdef HAVE_DNSSD
|
||
# ifdef HAVE_SSL
|
||
dnssdDeregisterInstance(&p->ipps_srv, from_callback);
|
||
# endif /* HAVE_SSL */
|
||
dnssdDeregisterInstance(&p->printer_srv, from_callback);
|
||
# endif /* HAVE_DNSSD */
|
||
}
|
||
}
|
||
|
||
|
||
/*
|
||
* 'dnssdStop()' - Stop all DNS-SD registrations.
|
||
*/
|
||
|
||
static void
|
||
dnssdStop(void)
|
||
{
|
||
cupsd_printer_t *p; /* Current printer */
|
||
|
||
|
||
/*
|
||
* De-register the individual printers
|
||
*/
|
||
|
||
for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
|
||
p;
|
||
p = (cupsd_printer_t *)cupsArrayNext(Printers))
|
||
dnssdDeregisterPrinter(p, 1, 0);
|
||
|
||
/*
|
||
* Shutdown the rest of the service refs...
|
||
*/
|
||
|
||
dnssdDeregisterInstance(&WebIFSrv, 0);
|
||
|
||
# ifdef HAVE_DNSSD
|
||
cupsdRemoveSelect(DNSServiceRefSockFD(DNSSDMaster));
|
||
|
||
DNSServiceRefDeallocate(DNSSDMaster);
|
||
DNSSDMaster = NULL;
|
||
|
||
# else /* HAVE_AVAHI */
|
||
if (DNSSDMaster)
|
||
avahi_threaded_poll_stop(DNSSDMaster);
|
||
|
||
if (DNSSDClient)
|
||
{
|
||
avahi_client_free(DNSSDClient);
|
||
DNSSDClient = NULL;
|
||
}
|
||
|
||
if (DNSSDMaster)
|
||
{
|
||
avahi_threaded_poll_free(DNSSDMaster);
|
||
DNSSDMaster = NULL;
|
||
}
|
||
# endif /* HAVE_DNSSD */
|
||
|
||
cupsArrayDelete(DNSSDPrinters);
|
||
DNSSDPrinters = NULL;
|
||
|
||
DNSSDPort = 0;
|
||
}
|
||
|
||
|
||
# ifdef HAVE_DNSSD
|
||
/*
|
||
* 'dnssdUpdate()' - Handle DNS-SD queries.
|
||
*/
|
||
|
||
static void
|
||
dnssdUpdate(void)
|
||
{
|
||
DNSServiceErrorType sdErr; /* Service discovery error */
|
||
|
||
|
||
if ((sdErr = DNSServiceProcessResult(DNSSDMaster)) != kDNSServiceErr_NoError)
|
||
{
|
||
cupsdLogMessage(CUPSD_LOG_ERROR,
|
||
"DNS Service Discovery registration error %d!",
|
||
sdErr);
|
||
dnssdStop();
|
||
}
|
||
}
|
||
# endif /* HAVE_DNSSD */
|
||
|
||
|
||
/*
|
||
* 'dnssdUpdateDNSSDName()' - Update the listen port, computer name, and web interface registration.
|
||
*/
|
||
|
||
static void
|
||
dnssdUpdateDNSSDName(int from_callback) /* I - Called from callback? */
|
||
{
|
||
char webif[1024]; /* Web interface share name */
|
||
# ifdef __APPLE__
|
||
SCDynamicStoreRef sc; /* Context for dynamic store */
|
||
CFDictionaryRef btmm; /* Back-to-My-Mac domains */
|
||
CFStringEncoding nameEncoding; /* Encoding of computer name */
|
||
CFStringRef nameRef; /* Host name CFString */
|
||
char nameBuffer[1024]; /* C-string buffer */
|
||
# endif /* __APPLE__ */
|
||
|
||
|
||
/*
|
||
* Only share the web interface and printers when non-local listening is
|
||
* enabled...
|
||
*/
|
||
|
||
if (!DNSSDPort)
|
||
{
|
||
/*
|
||
* Get the port we use for registrations. If we are not listening on any
|
||
* non-local ports, there is no sense sharing local printers via Bonjour...
|
||
*/
|
||
|
||
cupsd_listener_t *lis; /* Current listening socket */
|
||
|
||
for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners);
|
||
lis;
|
||
lis = (cupsd_listener_t *)cupsArrayNext(Listeners))
|
||
{
|
||
if (httpAddrLocalhost(&(lis->address)))
|
||
continue;
|
||
|
||
DNSSDPort = httpAddrPort(&(lis->address));
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (!DNSSDPort)
|
||
return;
|
||
|
||
/*
|
||
* Get the computer name as a c-string...
|
||
*/
|
||
|
||
# ifdef __APPLE__
|
||
sc = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("cupsd"), NULL, NULL);
|
||
|
||
if (sc)
|
||
{
|
||
/*
|
||
* Get the computer name from the dynamic store...
|
||
*/
|
||
|
||
cupsdClearString(&DNSSDComputerName);
|
||
|
||
if ((nameRef = SCDynamicStoreCopyComputerName(sc, &nameEncoding)) != NULL)
|
||
{
|
||
if (CFStringGetCString(nameRef, nameBuffer, sizeof(nameBuffer),
|
||
kCFStringEncodingUTF8))
|
||
{
|
||
cupsdLogMessage(CUPSD_LOG_DEBUG,
|
||
"Dynamic store computer name is \"%s\".", nameBuffer);
|
||
cupsdSetString(&DNSSDComputerName, nameBuffer);
|
||
}
|
||
|
||
CFRelease(nameRef);
|
||
}
|
||
|
||
if (!DNSSDComputerName)
|
||
{
|
||
/*
|
||
* Use the ServerName instead...
|
||
*/
|
||
|
||
cupsdLogMessage(CUPSD_LOG_DEBUG,
|
||
"Using ServerName \"%s\" as computer name.", ServerName);
|
||
cupsdSetString(&DNSSDComputerName, ServerName);
|
||
}
|
||
|
||
if (!DNSSDHostName)
|
||
{
|
||
/*
|
||
* Get the local hostname from the dynamic store...
|
||
*/
|
||
|
||
if ((nameRef = SCDynamicStoreCopyLocalHostName(sc)) != NULL)
|
||
{
|
||
if (CFStringGetCString(nameRef, nameBuffer, sizeof(nameBuffer),
|
||
kCFStringEncodingUTF8))
|
||
{
|
||
cupsdLogMessage(CUPSD_LOG_DEBUG, "Dynamic store host name is \"%s\".", nameBuffer);
|
||
|
||
if (strchr(nameBuffer, '.'))
|
||
cupsdSetString(&DNSSDHostName, nameBuffer);
|
||
else
|
||
cupsdSetStringf(&DNSSDHostName, "%s.local", nameBuffer);
|
||
|
||
cupsdLogMessage(CUPSD_LOG_INFO, "Defaulting to \"DNSSDHostName %s\".", DNSSDHostName);
|
||
}
|
||
|
||
CFRelease(nameRef);
|
||
}
|
||
}
|
||
|
||
if (!DNSSDHostName)
|
||
{
|
||
/*
|
||
* Use the ServerName instead...
|
||
*/
|
||
|
||
cupsdLogMessage(CUPSD_LOG_DEBUG, "Using ServerName \"%s\" as host name.", ServerName);
|
||
cupsdSetString(&DNSSDHostName, ServerName);
|
||
|
||
cupsdLogMessage(CUPSD_LOG_INFO, "Defaulting to \"DNSSDHostName %s\".", DNSSDHostName);
|
||
}
|
||
|
||
/*
|
||
* Get any Back-to-My-Mac domains and add them as aliases...
|
||
*/
|
||
|
||
cupsdFreeAliases(DNSSDAlias);
|
||
DNSSDAlias = NULL;
|
||
|
||
btmm = SCDynamicStoreCopyValue(sc, CFSTR("Setup:/Network/BackToMyMac"));
|
||
if (btmm && CFGetTypeID(btmm) == CFDictionaryGetTypeID())
|
||
{
|
||
cupsdLogMessage(CUPSD_LOG_DEBUG, "%d Back to My Mac aliases to add.",
|
||
(int)CFDictionaryGetCount(btmm));
|
||
CFDictionaryApplyFunction(btmm, dnssdAddAlias, NULL);
|
||
}
|
||
else if (btmm)
|
||
cupsdLogMessage(CUPSD_LOG_ERROR,
|
||
"Bad Back to My Mac data in dynamic store!");
|
||
else
|
||
cupsdLogMessage(CUPSD_LOG_DEBUG, "No Back to My Mac aliases to add.");
|
||
|
||
if (btmm)
|
||
CFRelease(btmm);
|
||
|
||
CFRelease(sc);
|
||
}
|
||
else
|
||
# endif /* __APPLE__ */
|
||
# ifdef HAVE_AVAHI
|
||
if (DNSSDClient)
|
||
{
|
||
const char *host_name = avahi_client_get_host_name(DNSSDClient);
|
||
|
||
cupsdSetString(&DNSSDComputerName, host_name ? host_name : ServerName);
|
||
|
||
if (!DNSSDHostName)
|
||
{
|
||
const char *host_fqdn = avahi_client_get_host_name_fqdn(DNSSDClient);
|
||
|
||
if (host_fqdn)
|
||
cupsdSetString(&DNSSDHostName, host_fqdn);
|
||
else if (strchr(ServerName, '.'))
|
||
cupsdSetString(&DNSSDHostName, ServerName);
|
||
else
|
||
cupsdSetStringf(&DNSSDHostName, "%s.local", ServerName);
|
||
|
||
cupsdLogMessage(CUPSD_LOG_INFO, "Defaulting to \"DNSSDHostName %s\".", DNSSDHostName);
|
||
}
|
||
}
|
||
else
|
||
# endif /* HAVE_AVAHI */
|
||
{
|
||
cupsdSetString(&DNSSDComputerName, ServerName);
|
||
|
||
if (!DNSSDHostName)
|
||
{
|
||
if (strchr(ServerName, '.'))
|
||
cupsdSetString(&DNSSDHostName, ServerName);
|
||
else
|
||
cupsdSetStringf(&DNSSDHostName, "%s.local", ServerName);
|
||
|
||
cupsdLogMessage(CUPSD_LOG_INFO, "Defaulting to \"DNSSDHostName %s\".", DNSSDHostName);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Then (re)register the web interface if enabled...
|
||
*/
|
||
|
||
if (BrowseWebIF)
|
||
{
|
||
if (DNSSDComputerName)
|
||
snprintf(webif, sizeof(webif), "CUPS @ %s", DNSSDComputerName);
|
||
else
|
||
strlcpy(webif, "CUPS", sizeof(webif));
|
||
|
||
dnssdDeregisterInstance(&WebIFSrv, from_callback);
|
||
dnssdRegisterInstance(&WebIFSrv, NULL, webif, "_http._tcp", "_printer", DNSSDPort, NULL, 1, from_callback);
|
||
}
|
||
}
|
||
|
||
|
||
/*
|
||
* 'get_auth_info_required()' - Get the auth-info-required value to advertise.
|
||
*/
|
||
|
||
static char * /* O - String or NULL if none */
|
||
get_auth_info_required(
|
||
cupsd_printer_t *p, /* I - Printer */
|
||
char *buffer, /* I - Value buffer */
|
||
size_t bufsize) /* I - Size of value buffer */
|
||
{
|
||
cupsd_location_t *auth; /* Pointer to authentication element */
|
||
char resource[1024]; /* Printer/class resource path */
|
||
|
||
|
||
/*
|
||
* If auth-info-required is set for this printer, return that...
|
||
*/
|
||
|
||
if (p->num_auth_info_required > 0 && strcmp(p->auth_info_required[0], "none"))
|
||
{
|
||
int i; /* Looping var */
|
||
char *bufptr; /* Pointer into buffer */
|
||
|
||
for (i = 0, bufptr = buffer; i < p->num_auth_info_required; i ++)
|
||
{
|
||
if (bufptr >= (buffer + bufsize - 2))
|
||
break;
|
||
|
||
if (i)
|
||
*bufptr++ = ',';
|
||
|
||
strlcpy(bufptr, p->auth_info_required[i], bufsize - (size_t)(bufptr - buffer));
|
||
bufptr += strlen(bufptr);
|
||
}
|
||
|
||
return (buffer);
|
||
}
|
||
|
||
/*
|
||
* Figure out the authentication data requirements to advertise...
|
||
*/
|
||
|
||
if (p->type & CUPS_PRINTER_CLASS)
|
||
snprintf(resource, sizeof(resource), "/classes/%s", p->name);
|
||
else
|
||
snprintf(resource, sizeof(resource), "/printers/%s", p->name);
|
||
|
||
if ((auth = cupsdFindBest(resource, HTTP_POST)) == NULL ||
|
||
auth->type == CUPSD_AUTH_NONE)
|
||
auth = cupsdFindPolicyOp(p->op_policy_ptr, IPP_PRINT_JOB);
|
||
|
||
if (auth)
|
||
{
|
||
int auth_type; /* Authentication type */
|
||
|
||
if ((auth_type = auth->type) == CUPSD_AUTH_DEFAULT)
|
||
auth_type = cupsdDefaultAuthType();
|
||
|
||
switch (auth_type)
|
||
{
|
||
case CUPSD_AUTH_NONE :
|
||
return (NULL);
|
||
|
||
case CUPSD_AUTH_NEGOTIATE :
|
||
strlcpy(buffer, "negotiate", bufsize);
|
||
break;
|
||
|
||
default :
|
||
strlcpy(buffer, "username,password", bufsize);
|
||
break;
|
||
}
|
||
|
||
return (buffer);
|
||
}
|
||
|
||
return ("none");
|
||
}
|
||
#endif /* HAVE_DNSSD || HAVE_AVAHI */
|
||
|
||
|
||
#ifdef __APPLE__
|
||
/*
|
||
* 'get_hostconfig()' - Get an /etc/hostconfig service setting.
|
||
*/
|
||
|
||
static int /* O - 1 for YES or AUTOMATIC, 0 for NO */
|
||
get_hostconfig(const char *name) /* I - Name of service */
|
||
{
|
||
cups_file_t *fp; /* Hostconfig file */
|
||
char line[1024], /* Line from file */
|
||
*ptr; /* Pointer to value */
|
||
int state = 1; /* State of service */
|
||
|
||
|
||
/*
|
||
* Try opening the /etc/hostconfig file; if we can't open it, assume that
|
||
* the service is enabled/auto.
|
||
*/
|
||
|
||
if ((fp = cupsFileOpen("/etc/hostconfig", "r")) != NULL)
|
||
{
|
||
/*
|
||
* Read lines from the file until we find the service...
|
||
*/
|
||
|
||
while (cupsFileGets(fp, line, sizeof(line)))
|
||
{
|
||
if (line[0] == '#' || (ptr = strchr(line, '=')) == NULL)
|
||
continue;
|
||
|
||
*ptr++ = '\0';
|
||
|
||
if (!_cups_strcasecmp(line, name))
|
||
{
|
||
/*
|
||
* Found the service, see if it is set to "-NO-"...
|
||
*/
|
||
|
||
if (!_cups_strncasecmp(ptr, "-NO-", 4))
|
||
state = 0;
|
||
break;
|
||
}
|
||
}
|
||
|
||
cupsFileClose(fp);
|
||
}
|
||
|
||
return (state);
|
||
}
|
||
#endif /* __APPLE__ */
|
||
|
||
|
||
/*
|
||
* 'update_lpd()' - Update the LPD configuration as needed.
|
||
*/
|
||
|
||
static void
|
||
update_lpd(int onoff) /* - 1 = turn on, 0 = turn off */
|
||
{
|
||
if (!LPDConfigFile)
|
||
return;
|
||
|
||
#ifdef __APPLE__
|
||
/*
|
||
* Allow /etc/hostconfig CUPS_LPD service setting to override cupsd.conf
|
||
* setting for backwards-compatibility.
|
||
*/
|
||
|
||
if (onoff && !get_hostconfig("CUPS_LPD"))
|
||
onoff = 0;
|
||
#endif /* __APPLE__ */
|
||
|
||
if (!strncmp(LPDConfigFile, "xinetd:///", 10))
|
||
{
|
||
/*
|
||
* Enable/disable LPD via the xinetd.d config file for cups-lpd...
|
||
*/
|
||
|
||
char newfile[1024]; /* New cups-lpd.N file */
|
||
cups_file_t *ofp, /* Original file pointer */
|
||
*nfp; /* New file pointer */
|
||
char line[1024]; /* Line from file */
|
||
|
||
|
||
snprintf(newfile, sizeof(newfile), "%s.N", LPDConfigFile + 9);
|
||
|
||
if ((ofp = cupsFileOpen(LPDConfigFile + 9, "r")) == NULL)
|
||
{
|
||
cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open \"%s\" - %s",
|
||
LPDConfigFile + 9, strerror(errno));
|
||
return;
|
||
}
|
||
|
||
if ((nfp = cupsFileOpen(newfile, "w")) == NULL)
|
||
{
|
||
cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create \"%s\" - %s",
|
||
newfile, strerror(errno));
|
||
cupsFileClose(ofp);
|
||
return;
|
||
}
|
||
|
||
/*
|
||
* Copy all of the lines from the cups-lpd file...
|
||
*/
|
||
|
||
while (cupsFileGets(ofp, line, sizeof(line)))
|
||
{
|
||
if (line[0] == '{')
|
||
{
|
||
cupsFilePrintf(nfp, "%s\n", line);
|
||
snprintf(line, sizeof(line), "\tdisable = %s",
|
||
onoff ? "no" : "yes");
|
||
}
|
||
else if (!strstr(line, "disable ="))
|
||
cupsFilePrintf(nfp, "%s\n", line);
|
||
}
|
||
|
||
cupsFileClose(nfp);
|
||
cupsFileClose(ofp);
|
||
rename(newfile, LPDConfigFile + 9);
|
||
}
|
||
#ifdef __APPLE__
|
||
else if (!strncmp(LPDConfigFile, "launchd:///", 11))
|
||
{
|
||
/*
|
||
* Enable/disable LPD via the launchctl command...
|
||
*/
|
||
|
||
char *argv[5], /* Arguments for command */
|
||
*envp[MAX_ENV]; /* Environment for command */
|
||
int pid; /* Process ID */
|
||
|
||
|
||
cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
|
||
argv[0] = (char *)"launchctl";
|
||
argv[1] = (char *)(onoff ? "load" : "unload");
|
||
argv[2] = (char *)"-w";
|
||
argv[3] = LPDConfigFile + 10;
|
||
argv[4] = NULL;
|
||
|
||
cupsdStartProcess("/bin/launchctl", argv, envp, -1, -1, -1, -1, -1, 1,
|
||
NULL, NULL, &pid);
|
||
}
|
||
#endif /* __APPLE__ */
|
||
else
|
||
cupsdLogMessage(CUPSD_LOG_INFO, "Unknown LPDConfigFile scheme!");
|
||
}
|
||
|
||
|
||
/*
|
||
* 'update_smb()' - Update the SMB configuration as needed.
|
||
*/
|
||
|
||
static void
|
||
update_smb(int onoff) /* I - 1 = turn on, 0 = turn off */
|
||
{
|
||
if (!SMBConfigFile)
|
||
return;
|
||
|
||
if (!strncmp(SMBConfigFile, "samba:///", 9))
|
||
{
|
||
/*
|
||
* Enable/disable SMB via the specified smb.conf config file...
|
||
*/
|
||
|
||
char newfile[1024]; /* New smb.conf.N file */
|
||
cups_file_t *ofp, /* Original file pointer */
|
||
*nfp; /* New file pointer */
|
||
char line[1024]; /* Line from file */
|
||
int in_printers; /* In [printers] section? */
|
||
|
||
|
||
snprintf(newfile, sizeof(newfile), "%s.N", SMBConfigFile + 8);
|
||
|
||
if ((ofp = cupsFileOpen(SMBConfigFile + 8, "r")) == NULL)
|
||
{
|
||
cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open \"%s\" - %s",
|
||
SMBConfigFile + 8, strerror(errno));
|
||
return;
|
||
}
|
||
|
||
if ((nfp = cupsFileOpen(newfile, "w")) == NULL)
|
||
{
|
||
cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create \"%s\" - %s",
|
||
newfile, strerror(errno));
|
||
cupsFileClose(ofp);
|
||
return;
|
||
}
|
||
|
||
/*
|
||
* Copy all of the lines from the smb.conf file...
|
||
*/
|
||
|
||
in_printers = 0;
|
||
|
||
while (cupsFileGets(ofp, line, sizeof(line)))
|
||
{
|
||
if (in_printers && strstr(line, "printable ="))
|
||
snprintf(line, sizeof(line), " printable = %s",
|
||
onoff ? "yes" : "no");
|
||
|
||
cupsFilePrintf(nfp, "%s\n", line);
|
||
|
||
if (line[0] == '[')
|
||
in_printers = !strcmp(line, "[printers]");
|
||
}
|
||
|
||
cupsFileClose(nfp);
|
||
cupsFileClose(ofp);
|
||
rename(newfile, SMBConfigFile + 8);
|
||
}
|
||
else
|
||
cupsdLogMessage(CUPSD_LOG_INFO, "Unknown SMBConfigFile scheme!");
|
||
}
|