#include "cupsd.h"
#include <grp.h>
#if defined(HAVE_DNSSD) && defined(__APPLE__)
# include <nameser.h>
# include <CoreFoundation/CoreFoundation.h>
# include <SystemConfiguration/SystemConfiguration.h>
#endif
#ifdef HAVE_AVAHI
static int avahi_running = 0;
#endif
#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
static char *get_auth_info_required(cupsd_printer_t *p,
char *buffer, size_t bufsize);
#endif
#ifdef __APPLE__
static int get_hostconfig(const char *name);
#endif
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
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
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
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
static void dnssdUpdateDNSSDName(int from_callback);
#endif
void
cupsdDeregisterPrinter(
cupsd_printer_t *p,
int removeit)
{
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;
#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
if (removeit && (BrowseLocalProtocols & BROWSE_DNSSD) && DNSSDMaster)
dnssdDeregisterPrinter(p, 1, 0);
#endif
}
void
cupsdRegisterPrinter(cupsd_printer_t *p)
{
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
}
void
cupsdStartBrowsing(void)
{
if (!Browsing || !BrowseLocalProtocols)
return;
#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
if (BrowseLocalProtocols & BROWSE_DNSSD)
{
# ifdef HAVE_DNSSD
DNSServiceErrorType error;
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
{
int fd = DNSServiceRefSockFD(DNSSDMaster);
fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
cupsdAddSelect(fd, (cupsd_selfunc_t)dnssdUpdate, NULL, NULL);
}
DNSSDPort = 0;
cupsdUpdateDNSSDName();
# else
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;
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
}
#endif
if (BrowseLocalProtocols & BROWSE_LPD)
update_lpd(1);
if (BrowseLocalProtocols & BROWSE_SMB)
update_smb(1);
#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
dnssdRegisterAllPrinters(0);
#endif
}
void
cupsdStopBrowsing(void)
{
if (!Browsing || !BrowseLocalProtocols)
return;
#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
dnssdDeregisterAllPrinters(0);
if ((BrowseLocalProtocols & BROWSE_DNSSD) && DNSSDMaster)
dnssdStop();
#endif
if (BrowseLocalProtocols & BROWSE_LPD)
update_lpd(0);
if (BrowseLocalProtocols & BROWSE_SMB)
update_smb(0);
}
#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
void
cupsdUpdateDNSSDName(void)
{
dnssdUpdateDNSSDName(0);
}
# ifdef __APPLE__
static void
dnssdAddAlias(const void *key,
const void *value,
void *context)
{
char valueStr[1024],
hostname[1024],
*hostptr;
(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';
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
static cupsd_txt_t
dnssdBuildTxtRecord(
cupsd_printer_t *p,
int for_lpd)
{
int i,
count;
char admin_hostname[256],
adminurl_str[256],
type_str[32],
state_str[32],
rp_str[1024],
air_str[1024],
*keyvalue[32][2];
cupsd_txt_t txt;
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";
if (strstr(DNSSDHostName, ".local"))
strlcpy(admin_hostname, DNSSDHostName, sizeof(admin_hostname));
else
snprintf(admin_hostname, sizeof(admin_hostname), "%s.local.",
DNSSDHostName);
httpAssembleURIf(HTTP_URI_CODING_ALL, adminurl_str, sizeof(adminurl_str),
# ifdef HAVE_SSL
"https",
# else
"http",
# endif
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";
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
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;
}
# 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
return (txt);
}
# ifdef HAVE_AVAHI
static void
dnssdClientCallback(
AvahiClient *c,
AvahiClientState state,
void *userdata)
{
int error;
(void)userdata;
if (!c)
return;
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.");
avahi_running = 1;
DNSSDPort = 0;
dnssdUpdateDNSSDName(1);
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.");
dnssdDeregisterAllPrinters(1);
dnssdDeregisterInstance(&WebIFSrv, 1);
avahi_client_free(DNSSDClient);
DNSSDClient = NULL;
avahi_running = 0;
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
static void
dnssdDeregisterAllPrinters(
int from_callback)
{
cupsd_printer_t *p;
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);
}
static void
dnssdDeregisterInstance(
cupsd_srv_t *srv,
int from_callback)
{
if (!srv || !*srv)
return;
# ifdef HAVE_DNSSD
(void)from_callback;
DNSServiceRefDeallocate(*srv);
# else
if (!from_callback)
avahi_threaded_poll_lock(DNSSDMaster);
avahi_entry_group_free(*srv);
if (!from_callback)
avahi_threaded_poll_unlock(DNSSDMaster);
# endif
*srv = NULL;
}
static void
dnssdDeregisterPrinter(
cupsd_printer_t *p,
int clear_name,
int 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
dnssdDeregisterInstance(&p->printer_srv, from_callback);
# endif
}
cupsArrayRemove(DNSSDPrinters, p);
if (clear_name)
cupsdClearString(&p->reg_name);
}
static const char *
dnssdErrorString(int error)
{
# 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
return (avahi_strerror(error));
# endif
}
static void
dnssdFreeTxtRecord(cupsd_txt_t *txt)
{
# ifdef HAVE_DNSSD
TXTRecordDeallocate(txt);
# else
avahi_string_list_free(*txt);
*txt = NULL;
# endif
}
static void
dnssdRegisterAllPrinters(int from_callback)
{
cupsd_printer_t *p;
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);
}
# ifdef HAVE_DNSSD
static void
dnssdRegisterCallback(
DNSServiceRef sdRef,
DNSServiceFlags flags,
DNSServiceErrorType errorCode,
const char *name,
const char *regtype,
const char *domain,
void *context)
{
cupsd_printer_t *p = (cupsd_printer_t *)context;
(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
static void
dnssdRegisterCallback(
AvahiEntryGroup *srv,
AvahiEntryGroupState state,
void *context)
{
cupsd_printer_t *p = (cupsd_printer_t *)context;
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");
}
# endif
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)
{
char temp[256],
*ptr;
int error;
# ifdef HAVE_DNSSD
(void)from_callback;
# endif
cupsdLogMessage(CUPSD_LOG_DEBUG, "Registering \"%s\" with DNS-SD type \"%s\".", name, type);
if (p && !srv)
{
# ifdef HAVE_DNSSD
if (!strcmp(type, "_printer._tcp"))
srv = &p->printer_srv;
# ifdef HAVE_SSL
else if (!strcmp(type, "_ipps._tcp"))
srv = &p->ipps_srv;
# endif
else
srv = &p->ipp_srv;
# else
srv = &p->ipp_srv;
# endif
}
# ifdef HAVE_DNSSD
(void)commit;
# else
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
ptr = name + strlen(name);
while ((ptr - name) > 63)
{
do
{
ptr --;
}
while (ptr > name && (*ptr & 0xc0) == 0x80);
if (ptr > name)
*ptr = '\0';
}
# 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, NULL, htons(port),
txt ? TXTRecordGetLength(txt) : 0,
txt ? TXTRecordGetBytesPtr(txt) : NULL,
dnssdRegisterCallback, p);
# else
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, NULL, port,
txt ? *txt : NULL);
if (error)
cupsdLogMessage(CUPSD_LOG_DEBUG, "DNS-SD service add for \"%s\" failed.",
name);
if (!error && subtypes)
{
char *start,
subtype[256];
strlcpy(temp, subtypes, sizeof(temp));
for (start = temp; *start; start = ptr)
{
while (*start && isspace(*start & 255))
start ++;
for (ptr = start; *ptr && *ptr != ','; ptr ++);
if (*ptr)
*ptr++ = '\0';
if (!*start)
break;
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
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);
}
static void
dnssdRegisterPrinter(
cupsd_printer_t *p,
int from_callback)
{
char name[256];
int printer_port;
int status;
cupsd_txt_t ipp_txt,
printer_txt;
cupsdLogMessage(CUPSD_LOG_DEBUG2, "dnssdRegisterPrinter(%s) %s", p->name,
!p->ipp_srv ? "new" : "update");
# ifdef HAVE_AVAHI
if (!avahi_running)
return;
# endif
dnssdDeregisterPrinter(p, 0, from_callback);
if (!p->shared)
return;
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));
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
if (status)
{
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)
{
cupsdSetString(&p->reg_name, name);
cupsArrayAdd(DNSSDPrinters, p);
}
else
{
dnssdDeregisterInstance(&p->ipp_srv, from_callback);
# ifdef HAVE_DNSSD
# ifdef HAVE_SSL
dnssdDeregisterInstance(&p->ipps_srv, from_callback);
# endif
dnssdDeregisterInstance(&p->printer_srv, from_callback);
# endif
}
}
static void
dnssdStop(void)
{
cupsd_printer_t *p;
for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
p;
p = (cupsd_printer_t *)cupsArrayNext(Printers))
dnssdDeregisterPrinter(p, 1, 0);
dnssdDeregisterInstance(&WebIFSrv, 0);
# ifdef HAVE_DNSSD
cupsdRemoveSelect(DNSServiceRefSockFD(DNSSDMaster));
DNSServiceRefDeallocate(DNSSDMaster);
DNSSDMaster = NULL;
# else
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
cupsArrayDelete(DNSSDPrinters);
DNSSDPrinters = NULL;
DNSSDPort = 0;
}
# ifdef HAVE_DNSSD
static void
dnssdUpdate(void)
{
DNSServiceErrorType sdErr;
if ((sdErr = DNSServiceProcessResult(DNSSDMaster)) != kDNSServiceErr_NoError)
{
cupsdLogMessage(CUPSD_LOG_ERROR,
"DNS Service Discovery registration error %d!",
sdErr);
dnssdStop();
}
}
# endif
static void
dnssdUpdateDNSSDName(int from_callback)
{
char webif[1024];
# ifdef __APPLE__
SCDynamicStoreRef sc;
CFDictionaryRef btmm;
CFStringEncoding nameEncoding;
CFStringRef nameRef;
char nameBuffer[1024];
# endif
if (!DNSSDPort)
{
cupsd_listener_t *lis;
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;
# ifdef __APPLE__
sc = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("cupsd"), NULL, NULL);
if (sc)
{
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)
{
cupsdLogMessage(CUPSD_LOG_DEBUG,
"Using ServerName \"%s\" as computer name.", ServerName);
cupsdSetString(&DNSSDComputerName, ServerName);
}
cupsdClearString(&DNSSDHostName);
if ((nameRef = SCDynamicStoreCopyLocalHostName(sc)) != NULL)
{
if (CFStringGetCString(nameRef, nameBuffer, sizeof(nameBuffer),
kCFStringEncodingUTF8))
{
cupsdLogMessage(CUPSD_LOG_DEBUG,
"Dynamic store host name is \"%s\".", nameBuffer);
cupsdSetString(&DNSSDHostName, nameBuffer);
}
CFRelease(nameRef);
}
if (!DNSSDHostName)
{
cupsdLogMessage(CUPSD_LOG_DEBUG,
"Using ServerName \"%s\" as host name.", ServerName);
cupsdSetString(&DNSSDHostName, ServerName);
}
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
# ifdef HAVE_AVAHI
if (DNSSDClient)
{
const char *host_name = avahi_client_get_host_name(DNSSDClient);
const char *host_fqdn = avahi_client_get_host_name_fqdn(DNSSDClient);
cupsdSetString(&DNSSDComputerName, host_name ? host_name : ServerName);
if (host_fqdn)
cupsdSetString(&DNSSDHostName, host_fqdn);
else if (strchr(ServerName, '.'))
cupsdSetString(&DNSSDHostName, ServerName);
else
cupsdSetStringf(&DNSSDHostName, "%s.local", ServerName);
}
else
# endif
{
cupsdSetString(&DNSSDComputerName, ServerName);
if (strchr(ServerName, '.'))
cupsdSetString(&DNSSDHostName, ServerName);
else
cupsdSetStringf(&DNSSDHostName, "%s.local", ServerName);
}
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);
}
}
static char *
get_auth_info_required(
cupsd_printer_t *p,
char *buffer,
size_t bufsize)
{
cupsd_location_t *auth;
char resource[1024];
if (p->num_auth_info_required > 0 && strcmp(p->auth_info_required[0], "none"))
{
int i;
char *bufptr;
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);
}
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;
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
#ifdef __APPLE__
static int
get_hostconfig(const char *name)
{
cups_file_t *fp;
char line[1024],
*ptr;
int state = 1;
if ((fp = cupsFileOpen("/etc/hostconfig", "r")) != NULL)
{
while (cupsFileGets(fp, line, sizeof(line)))
{
if (line[0] == '#' || (ptr = strchr(line, '=')) == NULL)
continue;
*ptr++ = '\0';
if (!_cups_strcasecmp(line, name))
{
if (!_cups_strncasecmp(ptr, "-NO-", 4))
state = 0;
break;
}
}
cupsFileClose(fp);
}
return (state);
}
#endif
static void
update_lpd(int onoff)
{
if (!LPDConfigFile)
return;
#ifdef __APPLE__
if (onoff && !get_hostconfig("CUPS_LPD"))
onoff = 0;
#endif
if (!strncmp(LPDConfigFile, "xinetd:///", 10))
{
char newfile[1024];
cups_file_t *ofp,
*nfp;
char line[1024];
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;
}
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))
{
char *argv[5],
*envp[MAX_ENV];
int pid;
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
else
cupsdLogMessage(CUPSD_LOG_INFO, "Unknown LPDConfigFile scheme!");
}
static void
update_smb(int onoff)
{
if (!SMBConfigFile)
return;
if (!strncmp(SMBConfigFile, "samba:///", 9))
{
char newfile[1024];
cups_file_t *ofp,
*nfp;
char line[1024];
int in_printers;
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;
}
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!");
}