X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=scheduler%2Fdirsvc.c;h=2e8303ec6fdc6c7e5c6807e1c0bd4a196e994239;hb=27453efb8716d3d28f2d79ab0895eae484868168;hp=cb751549bc698d57b668e4fb2efe7cbb09a13d00;hpb=d09495fadd23469999a64b1812ffb880bf4372de;p=thirdparty%2Fcups.git diff --git a/scheduler/dirsvc.c b/scheduler/dirsvc.c index cb751549b..2e8303ec6 100644 --- a/scheduler/dirsvc.c +++ b/scheduler/dirsvc.c @@ -1,56 +1,14 @@ /* - * "$Id: dirsvc.c 5889 2006-08-24 21:44:35Z mike $" + * Directory services routines for the CUPS scheduler. * - * Directory services routines for the Common UNIX Printing System (CUPS). + * Copyright 2007-2015 by Apple Inc. + * Copyright 1997-2007 by Easy Software Products, all rights reserved. * - * Copyright 1997-2006 by Easy Software Products, all rights reserved. - * - * These coded instructions, statements, and computer programs are the - * property of Easy Software Products and are protected by Federal - * copyright law. Distribution and use rights are outlined in the file - * "LICENSE.txt" which should have been included with this file. If this - * file is missing or damaged please contact Easy Software Products - * at: - * - * Attn: CUPS Licensing Information - * Easy Software Products - * 44141 Airport View Drive, Suite 204 - * Hollywood, Maryland 20636 USA - * - * Voice: (301) 373-9600 - * EMail: cups-info@cups.org - * WWW: http://www.cups.org - * - * Contents: - * - * cupsdLoadRemoteCache() - Load the remote printer cache. - * cupsdSaveRemoteCache() - Save the remote printer cache. - * cupsdSendBrowseDelete() - Send a "browse delete" message for a - * printer. - * cupsdSendBrowseList() - Send new browsing information as necessary. - * cupsdStartBrowsing() - Start sending and receiving broadcast - * information. - * cupsdStartPolling() - Start polling servers as needed. - * cupsdStopBrowsing() - Stop sending and receiving broadcast - * information. - * cupsdStopPolling() - Stop polling servers as needed. - * cupsdUpdateCUPSBrowse() - Update the browse lists using the CUPS - * protocol. - * cupsdUpdateLDAPBrowse() - Scan for new printers via LDAP... - * cupsdUpdatePolling() - Read status messages from the poll daemons. - * cupsdUpdateSLPBrowse() - Get browsing information via SLP. - * dequote() - Remote quotes from a string. - * process_browse_data() - Process new browse data. - * process_implicit_classes() - Create/update implicit classes as needed. - * send_cups_browse() - Send new browsing information using the - * CUPS protocol. - * send_ldap_browse() - Send LDAP printer registrations. - * send_slp_browse() - Register the specified printer with SLP. - * slp_attr_callback() - SLP attribute callback - * slp_dereg_printer() - SLPDereg() the specified printer - * slp_get_attr() - Get an attribute from an SLP registration. - * slp_reg_callback() - Empty SLPRegReport. - * slp_url_callback() - SLP service url callback + * These coded instructions, statements, and computer programs are the + * property of Apple Inc. and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "LICENSE.txt" + * which should have been included with this file. If this file is + * file is missing or damaged, see the license at "http://www.cups.org/". */ /* @@ -60,3126 +18,1803 @@ #include "cupsd.h" #include +#if defined(HAVE_DNSSD) && defined(__APPLE__) +# include +# include +# include +#endif /* HAVE_DNSSD && __APPLE__ */ -/* - * Local functions... - */ - -static char *dequote(char *d, const char *s, int dlen); -static int is_local_queue(const char *uri, char *host, int hostlen, - char *resource, int resourcelen); -static void process_browse_data(const char *uri, const char *host, - const char *resource, cups_ptype_t type, - ipp_pstate_t state, const char *location, - const char *info, const char *make_model, - int num_attrs, cups_option_t *attrs); -static void process_implicit_classes(void); -static void send_cups_browse(cupsd_printer_t *p); -#ifdef HAVE_LDAP -static void send_ldap_browse(cupsd_printer_t *p); -#endif /* HAVE_LDAP */ -#ifdef HAVE_LIBSLP -static void send_slp_browse(cupsd_printer_t *p); -#endif /* HAVE_LIBSLP */ - -#ifdef HAVE_OPENLDAP -static const char * const ldap_attrs[] =/* CUPS LDAP attributes */ - { - "printerDescription", - "printerLocation", - "printerMakeAndModel", - "printerType", - "printerURI", - NULL - }; -#endif /* HAVE_OPENLDAP */ - -#ifdef HAVE_LIBSLP -/* - * SLP definitions... - */ /* - * SLP service name for CUPS... - */ - -# define SLP_CUPS_SRVTYPE "service:printer" -# define SLP_CUPS_SRVLEN 15 - - -/* - * Printer service URL structure + * Local globals... */ -typedef struct _slpsrvurl_s /**** SLP URL list ****/ -{ - struct _slpsrvurl_s *next; /* Next URL in list */ - char url[HTTP_MAX_URI]; - /* URL */ -} slpsrvurl_t; +#ifdef HAVE_AVAHI +static int avahi_running = 0; +#endif /* HAVE_AVAHI */ /* * Local functions... */ -static SLPBoolean slp_attr_callback(SLPHandle hslp, const char *attrlist, - SLPError errcode, void *cookie); -static void slp_dereg_printer(cupsd_printer_t *p); -static int slp_get_attr(const char *attrlist, const char *tag, - char **valbuf); -static void slp_reg_callback(SLPHandle hslp, SLPError errcode, - void *cookie); -static SLPBoolean slp_url_callback(SLPHandle hslp, const char *srvurl, - unsigned short lifetime, - SLPError errcode, void *cookie); -#endif /* HAVE_LIBSLP */ +#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 */ /* - * 'cupsdLoadRemoteCache()' - Load the remote printer cache. + * 'cupsdDeregisterPrinter()' - Stop sending broadcast information for a + * local printer and remove any pending + * references to remote printers. */ void -cupsdLoadRemoteCache(void) +cupsdDeregisterPrinter( + cupsd_printer_t *p, /* I - Printer to register */ + int removeit) /* I - Printer being permanently removed */ { - cups_file_t *fp; /* remote.cache file */ - int linenum; /* Current line number */ - char line[1024], /* Line from file */ - *value, /* Pointer to value */ - *valueptr, /* Pointer into value */ - scheme[32], /* Scheme portion of URI */ - username[64], /* Username portion of URI */ - host[HTTP_MAX_HOST], - /* Hostname portion of URI */ - resource[HTTP_MAX_URI]; - /* Resource portion of URI */ - int port; /* Port number */ - cupsd_printer_t *p; /* Current printer */ - time_t now; /* Current time */ - - /* - * Open the remote.cache file... + * Only deregister if browsing is enabled and it's a local printer... */ - snprintf(line, sizeof(line), "%s/remote.cache", CacheDir); - if ((fp = cupsFileOpen(line, "r")) == NULL) + 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; /* - * Read printer configurations until we hit EOF... + * Announce the deletion... */ - linenum = 0; - p = NULL; - now = time(NULL); - - while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum)) - { - /* - * Decode the directive... - */ - - if (!strcasecmp(line, " or - */ - - if (p == NULL && value) - { - /* - * Add the printer and a base file type... - */ - - cupsdLogMessage(CUPSD_LOG_DEBUG, - "cupsdLoadRemoteCache: Loading printer %s...", value); - - if ((p = cupsdFindDest(value)) != NULL) - { - if (p->type & CUPS_PRINTER_CLASS) - { - cupsdLogMessage(CUPSD_LOG_WARN, - "Cached remote printer \"%s\" conflicts with " - "existing class!", - value); - p = NULL; - continue; - } - } - else - p = cupsdAddPrinter(value); +#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI) + if (removeit && (BrowseLocalProtocols & BROWSE_DNSSD) && DNSSDMaster) + dnssdDeregisterPrinter(p, 1, 0); +#endif /* HAVE_DNSSD || HAVE_AVAHI */ +} - p->accepting = 1; - p->state = IPP_PRINTER_IDLE; - p->type |= CUPS_PRINTER_REMOTE; - p->browse_time = now; - p->browse_expire = now + BrowseTimeout; - /* - * Set the default printer as needed... - */ +/* + * 'cupsdRegisterPrinter()' - Start sending broadcast information for a + * printer or update the broadcast contents. + */ - if (!strcasecmp(line, " or - */ +void +cupsdRegisterPrinter(cupsd_printer_t *p)/* I - Printer */ +{ + cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdRegisterPrinter(p=%p(%s))", p, + p->name); - if (p == NULL && value) - { - /* - * Add the printer and a base file type... - */ + if (!Browsing || !BrowseLocalProtocols || + (p->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_SCANNER))) + return; - cupsdLogMessage(CUPSD_LOG_DEBUG, - "cupsdLoadRemoteCache: Loading class %s...", value); +#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI) + if ((BrowseLocalProtocols & BROWSE_DNSSD) && DNSSDMaster) + dnssdRegisterPrinter(p, 0); +#endif /* HAVE_DNSSD || HAVE_AVAHI */ +} - if ((p = cupsdFindDest(value)) != NULL) - p->type = CUPS_PRINTER_CLASS; - else - p = cupsdAddClass(value); - p->accepting = 1; - p->state = IPP_PRINTER_IDLE; - p->type |= CUPS_PRINTER_REMOTE; - p->browse_time = now; - p->browse_expire = now + BrowseTimeout; +/* + * 'cupsdStartBrowsing()' - Start sending and receiving broadcast information. + */ - /* - * Set the default printer as needed... - */ +void +cupsdStartBrowsing(void) +{ + if (!Browsing || !BrowseLocalProtocols) + return; - if (!strcasecmp(line, "") || - !strcasecmp(line, "")) - { - if (p != NULL) - { - /* - * Close out the current printer... - */ +#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI) + if (BrowseLocalProtocols & BROWSE_DNSSD) + { +# ifdef HAVE_DNSSD + DNSServiceErrorType error; /* Error from service creation */ - cupsdSetPrinterAttrs(p); + /* + * First create a "master" connection for all registrations... + */ - p = NULL; - } - else - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "Syntax error on line %d of remote.cache.", linenum); - return; - } - } - else if (!p) + if ((error = DNSServiceCreateConnection(&DNSSDMaster)) + != kDNSServiceErr_NoError) { cupsdLogMessage(CUPSD_LOG_ERROR, - "Syntax error on line %d of remote.cache.", linenum); - return; - } - else if (!strcasecmp(line, "Info")) - { - if (value) - cupsdSetString(&p->info, value); - } - else if (!strcasecmp(line, "MakeModel")) - { - if (value) - cupsdSetString(&p->make_model, value); - } - else if (!strcasecmp(line, "Location")) - { - if (value) - cupsdSetString(&p->location, value); - } - else if (!strcasecmp(line, "DeviceURI")) - { - if (value) - { - httpSeparateURI(HTTP_URI_CODING_ALL, value, scheme, sizeof(scheme), - username, sizeof(username), host, sizeof(host), &port, - resource, sizeof(resource)); + "Unable to create master DNS-SD reference: %d", error); - cupsdSetString(&p->hostname, host); - cupsdSetString(&p->uri, value); - cupsdSetString(&p->device_uri, value); - } - else - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "Syntax error on line %d of remote.cache.", linenum); - return; - } + if (FatalErrors & CUPSD_FATAL_BROWSE) + cupsdEndProcess(getpid(), 0); } - else if (!strcasecmp(line, "Option") && value) + else { /* - * Option name value + * Add the master connection to the select list... */ - for (valueptr = value; *valueptr && !isspace(*valueptr & 255); valueptr ++); + int fd = DNSServiceRefSockFD(DNSSDMaster); - if (!*valueptr) - cupsdLogMessage(CUPSD_LOG_ERROR, - "Syntax error on line %d of remote.cache.", linenum); - else - { - for (; *valueptr && isspace(*valueptr & 255); *valueptr++ = '\0'); + fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); - p->num_options = cupsAddOption(value, valueptr, p->num_options, - &(p->options)); - } + cupsdAddSelect(fd, (cupsd_selfunc_t)dnssdUpdate, NULL, NULL); } - else if (!strcasecmp(line, "State")) - { - /* - * Set the initial queue state... - */ - if (value && !strcasecmp(value, "idle")) - p->state = IPP_PRINTER_IDLE; - else if (value && !strcasecmp(value, "stopped")) - p->state = IPP_PRINTER_STOPPED; - else - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "Syntax error on line %d of remote.cache.", linenum); - return; - } - } - else if (!strcasecmp(line, "StateMessage")) - { - /* - * Set the initial queue state message... - */ + /* + * Set the computer name and register the web interface... + */ - if (value) - strlcpy(p->state_message, value, sizeof(p->state_message)); - } - else if (!strcasecmp(line, "Accepting")) - { - /* - * Set the initial accepting state... - */ + DNSSDPort = 0; + cupsdUpdateDNSSDName(); - if (value && - (!strcasecmp(value, "yes") || - !strcasecmp(value, "on") || - !strcasecmp(value, "true"))) - p->accepting = 1; - else if (value && - (!strcasecmp(value, "no") || - !strcasecmp(value, "off") || - !strcasecmp(value, "false"))) - p->accepting = 0; - else - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "Syntax error on line %d of remote.cache.", linenum); - return; - } - } - else if (!strcasecmp(line, "Type")) - { - if (value) - p->type = atoi(value); - else - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "Syntax error on line %d of remote.cache.", linenum); - return; - } - } - else if (!strcasecmp(line, "BrowseTime")) +# else /* HAVE_AVAHI */ + if ((DNSSDMaster = avahi_threaded_poll_new()) == NULL) { - if (value) - { - time_t t = atoi(value); + cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create DNS-SD thread."); - if (t > p->browse_expire) - p->browse_expire = t; - } - else - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "Syntax error on line %d of remote.cache.", linenum); - return; - } + if (FatalErrors & CUPSD_FATAL_BROWSE) + cupsdEndProcess(getpid(), 0); } - else if (!strcasecmp(line, "JobSheets")) + else { - /* - * Set the initial job sheets... - */ - - if (value) - { - for (valueptr = value; *valueptr && !isspace(*valueptr & 255); valueptr ++); + int error; /* Error code, if any */ - if (*valueptr) - *valueptr++ = '\0'; + DNSSDClient = avahi_client_new(avahi_threaded_poll_get(DNSSDMaster), AVAHI_CLIENT_NO_FAIL, dnssdClientCallback, NULL, &error); - cupsdSetString(&p->job_sheets[0], value); - - while (isspace(*valueptr & 255)) - valueptr ++; - - if (*valueptr) - { - for (value = valueptr; *valueptr && !isspace(*valueptr & 255); valueptr ++); + if (DNSSDClient == NULL) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "Unable to communicate with avahi-daemon: %s", + dnssdErrorString(error)); - if (*valueptr) - *valueptr++ = '\0'; + if (FatalErrors & CUPSD_FATAL_BROWSE) + cupsdEndProcess(getpid(), 0); - cupsdSetString(&p->job_sheets[1], value); - } - } - else - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "Syntax error on line %d of remote.cache.", linenum); - return; - } - } - else if (!strcasecmp(line, "AllowUser")) - { - if (value) - { - p->deny_users = 0; - cupsdAddPrinterUser(p, value); - } - else - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "Syntax error on line %d of remote.cache.", linenum); - return; - } - } - else if (!strcasecmp(line, "DenyUser")) - { - if (value) - { - p->deny_users = 1; - cupsdAddPrinterUser(p, value); + avahi_threaded_poll_free(DNSSDMaster); + DNSSDMaster = NULL; } else - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "Syntax error on line %d of remote.cache.", linenum); - return; - } - } - else - { - /* - * Something else we don't understand... - */ - - cupsdLogMessage(CUPSD_LOG_ERROR, - "Unknown configuration directive %s on line %d of remote.cache.", - line, linenum); + avahi_threaded_poll_start(DNSSDMaster); } +# endif /* HAVE_DNSSD */ } - - cupsFileClose(fp); +#endif /* HAVE_DNSSD || HAVE_AVAHI */ /* - * Do auto-classing if needed... + * Enable LPD and SMB printer sharing as needed through external programs... */ - process_implicit_classes(); -} - + if (BrowseLocalProtocols & BROWSE_LPD) + update_lpd(1); -/* - * 'cupsdRestartPolling()' - Restart polling servers as needed. - */ - -void -cupsdRestartPolling(void) -{ - int i; /* Looping var */ - cupsd_dirsvc_poll_t *pollp; /* Current polling server */ + if (BrowseLocalProtocols & BROWSE_SMB) + update_smb(1); +#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI) + /* + * Register the individual printers + */ - for (i = 0, pollp = Polled; i < NumPolled; i ++, pollp ++) - if (pollp->pid) - kill(pollp->pid, SIGHUP); + dnssdRegisterAllPrinters(0); +#endif /* HAVE_DNSSD || HAVE_AVAHI */ } /* - * 'cupsdSaveRemoteCache()' - Save the remote printer cache. + * 'cupsdStopBrowsing()' - Stop sending and receiving broadcast information. */ void -cupsdSaveRemoteCache(void) +cupsdStopBrowsing(void) { - int i; /* Looping var */ - cups_file_t *fp; /* printers.conf file */ - char temp[1024]; /* Temporary string */ - cupsd_printer_t *printer; /* Current printer class */ - time_t curtime; /* Current time */ - struct tm *curdate; /* Current date */ - cups_option_t *option; /* Current option */ - - - /* - * Create the remote.cache file... - */ - - snprintf(temp, sizeof(temp), "%s/remote.cache", CacheDir); - - if ((fp = cupsFileOpen(temp, "w")) == NULL) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "Unable to save remote.cache - %s", strerror(errno)); + if (!Browsing || !BrowseLocalProtocols) return; - } - else - cupsdLogMessage(CUPSD_LOG_INFO, "Saving remote.cache..."); +#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI) /* - * Restrict access to the file... + * De-register the individual printers */ - fchown(cupsFileNumber(fp), getuid(), Group); - fchmod(cupsFileNumber(fp), ConfigFilePerm); + dnssdDeregisterAllPrinters(0); /* - * Write a small header to the file... + * Shut down browsing sockets... */ - curtime = time(NULL); - curdate = localtime(&curtime); - strftime(temp, sizeof(temp) - 1, "%Y-%m-%d %H:%M", curdate); - - cupsFilePuts(fp, "# Remote cache file for " CUPS_SVERSION "\n"); - cupsFilePrintf(fp, "# Written by cupsd on %s\n", temp); + if ((BrowseLocalProtocols & BROWSE_DNSSD) && DNSSDMaster) + dnssdStop(); +#endif /* HAVE_DNSSD || HAVE_AVAHI */ /* - * Write each local printer known to the system... + * Disable LPD and SMB printer sharing as needed through external programs... */ - for (printer = (cupsd_printer_t *)cupsArrayFirst(Printers); - printer; - printer = (cupsd_printer_t *)cupsArrayNext(Printers)) - { - /* - * Skip local destinations... - */ - - if (!(printer->type & CUPS_PRINTER_REMOTE)) - continue; - - /* - * Write printers as needed... - */ - - if (printer == DefaultPrinter) - cupsFilePuts(fp, "type & CUPS_PRINTER_CLASS) - cupsFilePrintf(fp, "Class %s>\n", printer->name); - else - cupsFilePrintf(fp, "Printer %s>\n", printer->name); + if (BrowseLocalProtocols & BROWSE_LPD) + update_lpd(0); - cupsFilePrintf(fp, "Type %d\n", printer->type); + if (BrowseLocalProtocols & BROWSE_SMB) + update_smb(0); +} - cupsFilePrintf(fp, "BrowseTime %d\n", (int)printer->browse_expire); - if (printer->info) - cupsFilePrintf(fp, "Info %s\n", printer->info); +#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI) +/* + * 'cupsdUpdateDNSSDName()' - Update the computer name we use for browsing... + */ - if (printer->make_model) - cupsFilePrintf(fp, "MakeModel %s\n", printer->make_model); +void +cupsdUpdateDNSSDName(void) +{ + dnssdUpdateDNSSDName(0); +} - if (printer->location) - cupsFilePrintf(fp, "Location %s\n", printer->location); - if (printer->device_uri) - cupsFilePrintf(fp, "DeviceURI %s\n", printer->device_uri); +# ifdef __APPLE__ +/* + * 'dnssdAddAlias()' - Add a DNS-SD alias name. + */ - if (printer->state == IPP_PRINTER_STOPPED) - { - cupsFilePuts(fp, "State Stopped\n"); - cupsFilePrintf(fp, "StateMessage %s\n", printer->state_message); - } - else - cupsFilePuts(fp, "State Idle\n"); +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 */ - if (printer->accepting) - cupsFilePuts(fp, "Accepting Yes\n"); - else - cupsFilePuts(fp, "Accepting No\n"); - cupsFilePrintf(fp, "JobSheets %s %s\n", printer->job_sheets[0], - printer->job_sheets[1]); + (void)key; + (void)context; - for (i = 0; i < printer->num_users; i ++) - cupsFilePrintf(fp, "%sUser %s\n", printer->deny_users ? "Deny" : "Allow", - printer->users[i]); + 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 */ - for (i = printer->num_options, option = printer->options; - i > 0; - i --, option ++) - cupsFilePrintf(fp, "Option %s %s\n", option->name, option->value); + if (!DNSSDAlias) + DNSSDAlias = cupsArrayNew(NULL, NULL); - if (printer->type & CUPS_PRINTER_CLASS) - cupsFilePuts(fp, "\n"); - else - cupsFilePuts(fp, "\n"); + cupsdAddAlias(DNSSDAlias, hostname); + cupsdLogMessage(CUPSD_LOG_DEBUG, "Added Back to My Mac ServerAlias %s", + hostname); } - - cupsFileClose(fp); + else + cupsdLogMessage(CUPSD_LOG_ERROR, + "Bad Back to My Mac domain in dynamic store!"); } +# endif /* __APPLE__ */ /* - * 'cupsdSendBrowseDelete()' - Send a "browse delete" message for a printer. + * 'dnssdBuildTxtRecord()' - Build a TXT record from printer info. */ -void -cupsdSendBrowseDelete( - cupsd_printer_t *p) /* I - Printer to delete */ +static cupsd_txt_t /* O - TXT record */ +dnssdBuildTxtRecord( + cupsd_printer_t *p, /* I - Printer information */ + int for_lpd) /* I - 1 = LPD, 0 = IPP */ { - /* - * Only announce if browsing is enabled and this is a local queue... - */ + int i, /* Looping var */ + count; /* Count of key/value pairs */ + char admin_hostname[256], /* .local 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 */ + cupsd_txt_t txt; /* TXT record */ - if (!Browsing || !p->shared || - (p->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT))) - return; /* - * First mark the printer for deletion... + * Load up the key value pairs... */ - p->type |= CUPS_PRINTER_DELETE; + count = 0; - /* - * Announce the deletion... - */ + if (!for_lpd || (BrowseLocalProtocols & BROWSE_LPD)) + { + keyvalue[count ][0] = "txtvers"; + keyvalue[count++][1] = "1"; - if ((BrowseLocalProtocols & BROWSE_CUPS) && BrowseSocket >= 0) - send_cups_browse(p); -#ifdef HAVE_LIBSLP - if ((BrowseLocalProtocols & BROWSE_SLP) && BrowseSLPHandle) - slp_dereg_printer(p); -#endif /* HAVE_LIBSLP */ -} + 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); -/* - * 'cupsdSendBrowseList()' - Send new browsing information as necessary. - */ + keyvalue[count ][0] = "ty"; + keyvalue[count++][1] = p->make_model ? p->make_model : "Unknown"; -void -cupsdSendBrowseList(void) -{ - int count; /* Number of dests to update */ - cupsd_printer_t *p; /* Current printer */ - time_t ut, /* Minimum update time */ - to; /* Timeout time */ + 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 /* HAVE_SSL */ + 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"; - if (!Browsing || !BrowseLocalProtocols || !Printers) - return; + keyvalue[count ][0] = "product"; + keyvalue[count++][1] = p->pc && p->pc->product ? p->pc->product : "Unknown"; - /* - * Compute the update and timeout times... - */ + keyvalue[count ][0] = "pdl"; + keyvalue[count++][1] = p->pdl ? p->pdl : "application/postscript"; - to = time(NULL); - ut = to - BrowseInterval; + if (get_auth_info_required(p, air_str, sizeof(air_str))) + { + keyvalue[count ][0] = "air"; + keyvalue[count++][1] = air_str; + } - /* - * Figure out how many printers need an update... - */ + keyvalue[count ][0] = "UUID"; + keyvalue[count++][1] = p->uuid + 9; - if (BrowseInterval > 0) - { - int max_count; /* Maximum number to update */ - - - /* - * Throttle the number of printers we'll be updating this time - * around based on the number of queues that need updating and - * the maximum number of queues to update each second... - */ - - max_count = 2 * cupsArrayCount(Printers) / BrowseInterval + 1; + #ifdef HAVE_SSL + keyvalue[count ][0] = "TLS"; + keyvalue[count++][1] = "1.2"; + #endif /* HAVE_SSL */ - for (count = 0, p = (cupsd_printer_t *)cupsArrayFirst(Printers); - count < max_count && p != NULL; - p = (cupsd_printer_t *)cupsArrayNext(Printers)) - if (!(p->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)) && - p->shared && p->browse_time < ut) - count ++; - - /* - * Loop through all of the printers and send local updates as needed... - */ + if (p->type & CUPS_PRINTER_FAX) + { + keyvalue[count ][0] = "Fax"; + keyvalue[count++][1] = "T"; + keyvalue[count ][0] = "rfo"; + keyvalue[count++][1] = rp_str; + } - if (BrowseNext) - p = (cupsd_printer_t *)cupsArrayFind(Printers, BrowseNext); - else - p = (cupsd_printer_t *)cupsArrayFirst(Printers); + if (p->type & CUPS_PRINTER_COLOR) + { + keyvalue[count ][0] = "Color"; + keyvalue[count++][1] = (p->type & CUPS_PRINTER_COLOR) ? "T" : "F"; + } - for (; - count > 0; - p = (cupsd_printer_t *)cupsArrayNext(Printers)) + if (p->type & CUPS_PRINTER_DUPLEX) { - /* - * Check for wraparound... - */ + keyvalue[count ][0] = "Duplex"; + keyvalue[count++][1] = (p->type & CUPS_PRINTER_DUPLEX) ? "T" : "F"; + } - if (!p) - p = (cupsd_printer_t *)cupsArrayFirst(Printers); + if (p->type & CUPS_PRINTER_STAPLE) + { + keyvalue[count ][0] = "Staple"; + keyvalue[count++][1] = (p->type & CUPS_PRINTER_STAPLE) ? "T" : "F"; + } - if (!p) - break; - else if ((p->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)) || - !p->shared) - continue; - else if (p->browse_time < ut) - { - /* - * Need to send an update... - */ + if (p->type & CUPS_PRINTER_COPIES) + { + keyvalue[count ][0] = "Copies"; + keyvalue[count++][1] = (p->type & CUPS_PRINTER_COPIES) ? "T" : "F"; + } - count --; + if (p->type & CUPS_PRINTER_COLLATE) + { + keyvalue[count ][0] = "Collate"; + keyvalue[count++][1] = (p->type & CUPS_PRINTER_COLLATE) ? "T" : "F"; + } - p->browse_time = time(NULL); + if (p->type & CUPS_PRINTER_PUNCH) + { + keyvalue[count ][0] = "Punch"; + keyvalue[count++][1] = (p->type & CUPS_PRINTER_PUNCH) ? "T" : "F"; + } - if (BrowseLocalProtocols & BROWSE_CUPS) - send_cups_browse(p); + if (p->type & CUPS_PRINTER_BIND) + { + keyvalue[count ][0] = "Bind"; + keyvalue[count++][1] = (p->type & CUPS_PRINTER_BIND) ? "T" : "F"; + } -#ifdef HAVE_LIBSLP - if (BrowseLocalProtocols & BROWSE_SLP) - send_slp_browse(p); -#endif /* HAVE_LIBSLP */ + if (p->type & CUPS_PRINTER_SORT) + { + keyvalue[count ][0] = "Sort"; + keyvalue[count++][1] = (p->type & CUPS_PRINTER_SORT) ? "T" : "F"; + } -#ifdef HAVE_LDAP - if (BrowseLocalProtocols & BROWSE_LDAP) - send_ldap_browse(p); -#endif /* HAVE_LDAP */ - } + if (p->type & CUPS_PRINTER_MFP) + { + keyvalue[count ][0] = "Scan"; + keyvalue[count++][1] = (p->type & CUPS_PRINTER_MFP) ? "T" : "F"; } - /* - * Save where we left off so that all printers get updated... - */ + snprintf(type_str, sizeof(type_str), "0x%X", p->type | CUPS_PRINTER_REMOTE); + snprintf(state_str, sizeof(state_str), "%d", p->state); - BrowseNext = p; + keyvalue[count ][0] = "printer-state"; + keyvalue[count++][1] = state_str; + + keyvalue[count ][0] = "printer-type"; + keyvalue[count++][1] = type_str; } /* - * Loop through all of the printers and send local updates as needed... + * Then pack them into a proper txt record... */ - for (p = (cupsd_printer_t *)cupsArrayFirst(Printers); - p; - p = (cupsd_printer_t *)cupsArrayNext(Printers)) +# ifdef HAVE_DNSSD + TXTRecordCreate(&txt, 0, NULL); + + for (i = 0; i < count; i ++) { - /* - * If this is a remote queue, see if it needs to be timed out... - */ + size_t len = strlen(keyvalue[i][1]); - if (p->type & CUPS_PRINTER_REMOTE) - { - if (p->browse_expire < to) - { - cupsdAddEvent(CUPSD_EVENT_PRINTER_DELETED, p, NULL, - "%s \'%s\' deleted by directory services (timeout).", - (p->type & CUPS_PRINTER_CLASS) ? "Class" : "Printer", - p->name); + if (len < 256) + TXTRecordSetValue(&txt, keyvalue[i][0], (uint8_t)len, keyvalue[i][1]); + } - cupsdLogMessage(CUPSD_LOG_DEBUG, - "Remote destination \"%s\" has timed out; " - "deleting it...", - p->name); +# 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 */ - cupsArraySave(Printers); - cupsdDeletePrinter(p, 1); - cupsArrayRestore(Printers); - } - } - } + return (txt); } +# ifdef HAVE_AVAHI /* - * 'cupsdStartBrowsing()' - Start sending and receiving broadcast information. + * 'dnssdClientCallback()' - Client callback for Avahi. + * + * Called whenever the client or server state changes... */ -void -cupsdStartBrowsing(void) +static void +dnssdClientCallback( + AvahiClient *c, /* I - Client */ + AvahiClientState state, /* I - Current state */ + void *userdata) /* I - User data (unused) */ { - int val; /* Socket option value */ - struct sockaddr_in addr; /* Broadcast address */ + int error; /* Error code, if any */ - BrowseNext = NULL; + (void)userdata; - if (!Browsing || !(BrowseLocalProtocols | BrowseRemoteProtocols)) + if (!c) return; - if ((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_CUPS) - { - if (BrowseSocket < 0) - { - /* - * Create the broadcast socket... - */ + /* + * Make sure DNSSDClient is already set also if this callback function is + * already running before avahi_client_new() in dnssdStartBrowsing() + * finishes. + */ - if ((BrowseSocket = socket(AF_INET, SOCK_DGRAM, 0)) < 0) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "cupsdStartBrowsing: Unable to create broadcast " - "socket - %s.", strerror(errno)); - BrowseLocalProtocols &= ~BROWSE_CUPS; - BrowseRemoteProtocols &= ~BROWSE_CUPS; - return; - } + if (!DNSSDClient) + DNSSDClient = c; - /* - * Bind the socket to browse port... - */ + 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."); - memset(&addr, 0, sizeof(addr)); - addr.sin_addr.s_addr = htonl(INADDR_ANY); - addr.sin_family = AF_INET; - addr.sin_port = htons(BrowsePort); + /* + * Mark that Avahi server is running... + */ - if (bind(BrowseSocket, (struct sockaddr *)&addr, sizeof(addr))) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "cupsdStartBrowsing: Unable to bind broadcast " - "socket - %s.", strerror(errno)); - -#ifdef WIN32 - closesocket(BrowseSocket); -#else - close(BrowseSocket); -#endif /* WIN32 */ - - BrowseSocket = -1; - BrowseLocalProtocols &= ~BROWSE_CUPS; - BrowseRemoteProtocols &= ~BROWSE_CUPS; - return; - } - } + avahi_running = 1; - /* - * Set the "broadcast" flag... - */ + /* + * Set the computer name and register the web interface... + */ - val = 1; - if (setsockopt(BrowseSocket, SOL_SOCKET, SO_BROADCAST, &val, sizeof(val))) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "cupsdStartBrowsing: Unable to set broadcast mode - %s.", - strerror(errno)); - -#ifdef WIN32 - closesocket(BrowseSocket); -#else - close(BrowseSocket); -#endif /* WIN32 */ - - BrowseSocket = -1; - BrowseLocalProtocols &= ~BROWSE_CUPS; - BrowseRemoteProtocols &= ~BROWSE_CUPS; - return; - } + DNSSDPort = 0; + dnssdUpdateDNSSDName(1); - /* - * Close the socket on exec... - */ + /* + * Register the individual printers + */ - fcntl(BrowseSocket, F_SETFD, fcntl(BrowseSocket, F_GETFD) | FD_CLOEXEC); + dnssdRegisterAllPrinters(1); + break; - /* - * Finally, add the socket to the input selection set as needed... - */ + case AVAHI_CLIENT_FAILURE: + if (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED) + { + cupsdLogMessage(CUPSD_LOG_DEBUG, "Avahi server disappeared, unregistering printers for Bonjour broadcasting."); - if (BrowseRemoteProtocols & BROWSE_CUPS) - { - /* - * We only listen if we want remote printers... - */ + /* + * Unregister everything and close the client... + */ - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdStartBrowsing: Adding fd %d to InputSet...", - BrowseSocket); + dnssdDeregisterAllPrinters(1); + dnssdDeregisterInstance(&WebIFSrv, 1); + avahi_client_free(DNSSDClient); + DNSSDClient = NULL; - FD_SET(BrowseSocket, InputSet); - } - } - else - BrowseSocket = -1; + /* + * Mark that Avahi server is not running... + */ -#ifdef HAVE_LIBSLP - if ((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_SLP) - { - /* - * Open SLP handle... - */ + avahi_running = 0; - if (SLPOpen("en", SLP_FALSE, &BrowseSLPHandle) != SLP_OK) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "Unable to open an SLP handle; disabling SLP browsing!"); - BrowseLocalProtocols &= ~BROWSE_SLP; - BrowseRemoteProtocols &= ~BROWSE_SLP; - } + /* + * Renew Avahi client... + */ - BrowseSLPRefresh = 0; - } - else - BrowseSLPHandle = NULL; -#endif /* HAVE_LIBSLP */ + DNSSDClient = avahi_client_new(avahi_threaded_poll_get(DNSSDMaster), AVAHI_CLIENT_NO_FAIL, dnssdClientCallback, NULL, &error); -#ifdef HAVE_OPENLDAP - if ((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_LDAP) - { - if (!BrowseLDAPDN) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "Need to set BrowseLDAPDN to use LDAP browsing!"); - BrowseLocalProtocols &= ~BROWSE_LDAP; - BrowseRemoteProtocols &= ~BROWSE_LDAP; - } - else - { - /* - * Open LDAP handle... - */ + 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; - int rc; /* LDAP API status */ - int version = 3; /* LDAP version */ - struct berval bv = {0, ""}; /* SASL bind value */ + default: + break; + } +} +# endif /* HAVE_AVAHI */ - /* - * LDAP stuff currently only supports ldapi EXTERNAL SASL binds... - */ +/* + * 'dnssdDeregisterAllPrinters()' - Deregister all printers. + */ - if (!BrowseLDAPServer || !strcasecmp(BrowseLDAPServer, "localhost")) - rc = ldap_initialize(&BrowseLDAPHandle, "ldapi:///"); - else - rc = ldap_initialize(&BrowseLDAPHandle, BrowseLDAPServer); +static void +dnssdDeregisterAllPrinters( + int from_callback) /* I - Deregistering because of callback? */ +{ + cupsd_printer_t *p; /* Current printer */ - if (rc != LDAP_SUCCESS) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "Unable to initialize LDAP; disabling LDAP browsing!"); - BrowseLocalProtocols &= ~BROWSE_LDAP; - BrowseRemoteProtocols &= ~BROWSE_LDAP; - } - else if (ldap_set_option(BrowseLDAPHandle, LDAP_OPT_PROTOCOL_VERSION, - (const void *)&version) != LDAP_SUCCESS) - { - ldap_unbind_ext(BrowseLDAPHandle, NULL, NULL); - BrowseLDAPHandle = NULL; - cupsdLogMessage(CUPSD_LOG_ERROR, - "Unable to set LDAP protocol version; " - "disabling LDAP browsing!"); - BrowseLocalProtocols &= ~BROWSE_LDAP; - BrowseRemoteProtocols &= ~BROWSE_LDAP; - } - else - { - if (!BrowseLDAPServer || !strcasecmp(BrowseLDAPServer, "localhost")) - rc = ldap_sasl_bind_s(BrowseLDAPHandle, NULL, "EXTERNAL", &bv, NULL, - NULL, NULL); - else - rc = ldap_bind_s(BrowseLDAPHandle, BrowseLDAPBindDN, - BrowseLDAPPassword, LDAP_AUTH_SIMPLE); - if (rc != LDAP_SUCCESS) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "Unable to bind to LDAP server; " - "disabling LDAP browsing!"); - ldap_unbind_ext(BrowseLDAPHandle, NULL, NULL); - BrowseLocalProtocols &= ~BROWSE_LDAP; - BrowseRemoteProtocols &= ~BROWSE_LDAP; - } - } - } + if (!DNSSDMaster) + return; - BrowseLDAPRefresh = 0; - } -#endif /* HAVE_OPENLDAP */ + 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); } /* - * 'cupsdStartPolling()' - Start polling servers as needed. + * 'dnssdDeregisterInstance()' - Deregister a DNS-SD service instance. */ -void -cupsdStartPolling(void) +static void +dnssdDeregisterInstance( + cupsd_srv_t *srv, /* I - Service */ + int from_callback) /* I - Called from callback? */ { - int i; /* Looping var */ - cupsd_dirsvc_poll_t *pollp; /* Current polling server */ - char polld[1024]; /* Poll daemon path */ - char sport[10]; /* Server port */ - char bport[10]; /* Browser port */ - char interval[10]; /* Poll interval */ - int statusfds[2]; /* Status pipe */ - char *argv[6]; /* Arguments */ - char *envp[100]; /* Environment */ + if (!srv || !*srv) + return; +# ifdef HAVE_DNSSD + (void)from_callback; - /* - * Don't do anything if we aren't polling... - */ + DNSServiceRefDeallocate(*srv); - if (NumPolled == 0) - { - PollPipe = -1; - PollStatusBuffer = NULL; - return; - } +# else /* HAVE_AVAHI */ + if (!from_callback) + avahi_threaded_poll_lock(DNSSDMaster); - /* - * Setup string arguments for polld, port and interval options. - */ + avahi_entry_group_free(*srv); - snprintf(polld, sizeof(polld), "%s/daemon/cups-polld", ServerBin); + if (!from_callback) + avahi_threaded_poll_unlock(DNSSDMaster); +# endif /* HAVE_DNSSD */ - sprintf(bport, "%d", BrowsePort); + *srv = NULL; +} - if (BrowseInterval) - sprintf(interval, "%d", BrowseInterval); - else - strcpy(interval, "30"); - argv[0] = "cups-polld"; - argv[2] = sport; - argv[3] = interval; - argv[4] = bport; - argv[5] = NULL; +/* + * 'dnssdDeregisterPrinter()' - Deregister all services for a printer. + */ - cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0]))); +static void +dnssdDeregisterPrinter( + cupsd_printer_t *p, /* I - Printer */ + int clear_name, /* I - Clear the name? */ + int from_callback) /* I - Called from callback? */ - /* - * Create a pipe that receives the status messages from each - * polling daemon... - */ +{ + cupsdLogMessage(CUPSD_LOG_DEBUG2, + "dnssdDeregisterPrinter(p=%p(%s), clear_name=%d)", p, p->name, + clear_name); - if (cupsdOpenPipe(statusfds)) + if (p->ipp_srv) { - cupsdLogMessage(CUPSD_LOG_ERROR, - "Unable to create polling status pipes - %s.", - strerror(errno)); - PollPipe = -1; - PollStatusBuffer = NULL; - return; + 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 */ } - PollPipe = statusfds[0]; - PollStatusBuffer = cupsdStatBufNew(PollPipe, "[Poll]"); - /* - * Run each polling daemon, redirecting stderr to the polling pipe... + * Remove the printer from the array of DNS-SD printers but keep the + * registered name... */ - for (i = 0, pollp = Polled; i < NumPolled; i ++, pollp ++) - { - sprintf(sport, "%d", pollp->port); - - argv[1] = pollp->hostname; - - if (cupsdStartProcess(polld, argv, envp, -1, -1, statusfds[1], -1, - 0, &(pollp->pid)) < 0) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "cupsdStartPolling: Unable to fork polling daemon - %s", - strerror(errno)); - pollp->pid = 0; - break; - } - else - cupsdLogMessage(CUPSD_LOG_DEBUG, - "cupsdStartPolling: Started polling daemon for %s:%d, pid = %d", - pollp->hostname, pollp->port, pollp->pid); - } - - close(statusfds[1]); + cupsArrayRemove(DNSSDPrinters, p); /* - * Finally, add the pipe to the input selection set... + * Optionally clear the service name... */ - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdStartPolling: Adding fd %d to InputSet...", PollPipe); - - FD_SET(PollPipe, InputSet); + if (clear_name) + cupsdClearString(&p->reg_name); } /* - * 'cupsdStopBrowsing()' - Stop sending and receiving broadcast information. + * 'dnssdErrorString()' - Return an error string for an error code. */ -void -cupsdStopBrowsing(void) +static const char * /* O - Error message */ +dnssdErrorString(int error) /* I - Error number */ { - if (!Browsing || !(BrowseLocalProtocols | BrowseRemoteProtocols)) - return; - - if (((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_CUPS) && - BrowseSocket >= 0) +# ifdef HAVE_DNSSD + switch (error) { - /* - * Close the socket and remove it from the input selection set. - */ + case kDNSServiceErr_NoError : + return ("OK."); -#ifdef WIN32 - closesocket(BrowseSocket); -#else - close(BrowseSocket); -#endif /* WIN32 */ + default : + case kDNSServiceErr_Unknown : + return ("Unknown error."); - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdStopBrowsing: Removing fd %d from InputSet...", - BrowseSocket); + case kDNSServiceErr_NoSuchName : + return ("Service not found."); - FD_CLR(BrowseSocket, InputSet); - BrowseSocket = -1; - } + case kDNSServiceErr_NoMemory : + return ("Out of memory."); -#ifdef HAVE_LIBSLP - if (((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_SLP) && - BrowseSLPHandle) - { - /* - * Close SLP handle... - */ + case kDNSServiceErr_BadParam : + return ("Bad parameter."); - SLPClose(BrowseSLPHandle); - BrowseSLPHandle = NULL; - } -#endif /* HAVE_LIBSLP */ + case kDNSServiceErr_BadReference : + return ("Bad service reference."); -#ifdef HAVE_OPENLDAP - if (((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_LDAP) && - BrowseLDAPHandle) - { - ldap_unbind(BrowseLDAPHandle); - BrowseLDAPHandle = NULL; - } -#endif /* HAVE_OPENLDAP */ -} + case kDNSServiceErr_BadState : + return ("Bad state."); + case kDNSServiceErr_BadFlags : + return ("Bad flags."); -/* - * 'cupsdStopPolling()' - Stop polling servers as needed. - */ + case kDNSServiceErr_Unsupported : + return ("Unsupported."); -void -cupsdStopPolling(void) -{ - int i; /* Looping var */ - cupsd_dirsvc_poll_t *pollp; /* Current polling server */ + case kDNSServiceErr_NotInitialized : + return ("Not initialized."); + case kDNSServiceErr_AlreadyRegistered : + return ("Already registered."); - if (PollPipe >= 0) - { - cupsdStatBufDelete(PollStatusBuffer); - close(PollPipe); + case kDNSServiceErr_NameConflict : + return ("Name conflict."); - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdStopPolling: removing fd %d from InputSet.", PollPipe); - FD_CLR(PollPipe, InputSet); + case kDNSServiceErr_Invalid : + return ("Invalid name."); - PollPipe = -1; - PollStatusBuffer = NULL; - } + case kDNSServiceErr_Firewall : + return ("Firewall prevents registration."); - for (i = 0, pollp = Polled; i < NumPolled; i ++, pollp ++) - if (pollp->pid) - cupsdEndProcess(pollp->pid, 0); -} + case kDNSServiceErr_Incompatible : + return ("Client library incompatible."); + case kDNSServiceErr_BadInterfaceIndex : + return ("Bad interface index."); -/* - * 'cupsdUpdateCUPSBrowse()' - Update the browse lists using the CUPS protocol. - */ + case kDNSServiceErr_Refused : + return ("Server prevents registration."); -void -cupsdUpdateCUPSBrowse(void) -{ - int i; /* Looping var */ - int auth; /* Authorization status */ - int len; /* Length of name string */ - int bytes; /* Number of bytes left */ - char packet[1541], /* Broadcast packet */ - *pptr; /* Pointer into packet */ - socklen_t srclen; /* Length of source address */ - http_addr_t srcaddr; /* Source address */ - char srcname[1024]; /* Source hostname */ - unsigned address[4]; /* Source address */ - unsigned type; /* Printer type */ - unsigned state; /* Printer state */ - char uri[HTTP_MAX_URI], /* Printer URI */ - host[HTTP_MAX_URI], /* Host portion of URI */ - resource[HTTP_MAX_URI], /* Resource portion of URI */ - info[IPP_MAX_NAME], /* Information string */ - location[IPP_MAX_NAME], /* Location string */ - make_model[IPP_MAX_NAME];/* Make and model string */ - int num_attrs; /* Number of attributes */ - cups_option_t *attrs; /* Attributes */ + case kDNSServiceErr_NoSuchRecord : + return ("Record not found."); + case kDNSServiceErr_NoAuth : + return ("Authentication required."); - /* - * Read a packet from the browse socket... - */ + case kDNSServiceErr_NoSuchKey : + return ("Encryption key not found."); - srclen = sizeof(srcaddr); - if ((bytes = recvfrom(BrowseSocket, packet, sizeof(packet) - 1, 0, - (struct sockaddr *)&srcaddr, &srclen)) < 0) - { - /* - * "Connection refused" is returned under Linux if the destination port - * or address is unreachable from a previous sendto(); check for the - * error here and ignore it for now... - */ + case kDNSServiceErr_NATTraversal : + return ("Unable to traverse NAT boundary."); - if (errno != ECONNREFUSED && errno != EAGAIN) - { - cupsdLogMessage(CUPSD_LOG_ERROR, "Browse recv failed - %s.", - strerror(errno)); - cupsdLogMessage(CUPSD_LOG_ERROR, "Browsing turned off."); + case kDNSServiceErr_DoubleNAT : + return ("Unable to traverse double-NAT boundary."); - cupsdStopBrowsing(); - Browsing = 0; - } + case kDNSServiceErr_BadTime : + return ("Bad system time."); - return; - } + case kDNSServiceErr_BadSig : + return ("Bad signature."); - packet[bytes] = '\0'; + case kDNSServiceErr_BadKey : + return ("Bad encryption key."); - /* - * If we're about to sleep, ignore incoming browse packets. - */ + case kDNSServiceErr_Transient : + return ("Transient error occurred - please try again."); - if (Sleeping) - return; + case kDNSServiceErr_ServiceNotRunning : + return ("Server not running."); - /* - * Figure out where it came from... - */ + case kDNSServiceErr_NATPortMappingUnsupported : + return ("NAT doesn't support NAT-PMP or UPnP."); -#ifdef AF_INET6 - if (srcaddr.addr.sa_family == AF_INET6) - { - address[0] = ntohl(srcaddr.ipv6.sin6_addr.s6_addr32[0]); - address[1] = ntohl(srcaddr.ipv6.sin6_addr.s6_addr32[1]); - address[2] = ntohl(srcaddr.ipv6.sin6_addr.s6_addr32[2]); - address[3] = ntohl(srcaddr.ipv6.sin6_addr.s6_addr32[3]); - } - else -#endif /* AF_INET6 */ - { - address[0] = 0; - address[1] = 0; - address[2] = 0; - address[3] = ntohl(srcaddr.ipv4.sin_addr.s_addr); - } + case kDNSServiceErr_NATPortMappingDisabled : + return ("NAT supports NAT-PNP or UPnP but it is disabled."); - if (HostNameLookups) - httpAddrLookup(&srcaddr, srcname, sizeof(srcname)); - else - httpAddrString(&srcaddr, srcname, sizeof(srcname)); + case kDNSServiceErr_NoRouter : + return ("No Internet/default router configured."); - len = strlen(srcname); + case kDNSServiceErr_PollingMode : + return ("Service polling mode error."); - /* - * Do ACL stuff... - */ + case kDNSServiceErr_Timeout : + return ("Service timeout."); + } - if (BrowseACL) - { - if (httpAddrLocalhost(&srcaddr) || !strcasecmp(srcname, "localhost")) - { - /* - * Access from localhost (127.0.0.1) is always allowed... - */ +# else /* HAVE_AVAHI */ + return (avahi_strerror(error)); +# endif /* HAVE_DNSSD */ +} - auth = AUTH_ALLOW; - } - else - { - /* - * Do authorization checks on the domain/address... - */ - switch (BrowseACL->order_type) - { - default : - auth = AUTH_DENY; /* anti-compiler-warning-code */ - break; +/* + * 'dnssdRegisterCallback()' - Free a TXT record. + */ - case AUTH_ALLOW : /* Order Deny,Allow */ - auth = AUTH_ALLOW; +static void +dnssdFreeTxtRecord(cupsd_txt_t *txt) /* I - TXT record */ +{ +# ifdef HAVE_DNSSD + TXTRecordDeallocate(txt); - if (cupsdCheckAuth(address, srcname, len, - BrowseACL->num_deny, BrowseACL->deny)) - auth = AUTH_DENY; +# else /* HAVE_AVAHI */ + avahi_string_list_free(*txt); + *txt = NULL; +# endif /* HAVE_DNSSD */ +} - if (cupsdCheckAuth(address, srcname, len, - BrowseACL->num_allow, BrowseACL->allow)) - auth = AUTH_ALLOW; - break; - case AUTH_DENY : /* Order Allow,Deny */ - auth = AUTH_DENY; +/* + * 'dnssdRegisterAllPrinters()' - Register all printers. + */ - if (cupsdCheckAuth(address, srcname, len, - BrowseACL->num_allow, BrowseACL->allow)) - auth = AUTH_ALLOW; +static void +dnssdRegisterAllPrinters(int from_callback) /* I - Called from callback? */ +{ + cupsd_printer_t *p; /* Current printer */ - if (cupsdCheckAuth(address, srcname, len, - BrowseACL->num_deny, BrowseACL->deny)) - auth = AUTH_DENY; - break; - } - } - } - else - auth = AUTH_ALLOW; - if (auth == AUTH_DENY) - { - cupsdLogMessage(CUPSD_LOG_DEBUG, - "cupsdUpdateCUPSBrowse: Refused %d bytes from %s", bytes, - srcname); + if (!DNSSDMaster) return; - } - - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdUpdateCUPSBrowse: (%d bytes from %s) %s", bytes, - srcname, packet); - - /* - * Parse packet... - */ - - if (sscanf(packet, "%x%x%1023s", &type, &state, uri) < 3) - { - cupsdLogMessage(CUPSD_LOG_WARN, - "cupsdUpdateCUPSBrowse: Garbled browse packet - %s", packet); - return; - } - - strcpy(location, "Location Unknown"); - strcpy(info, "No Information Available"); - make_model[0] = '\0'; - num_attrs = 0; - attrs = NULL; - - if ((pptr = strchr(packet, '\"')) != NULL) - { - /* - * Have extended information; can't use sscanf for it because not all - * sscanf's allow empty strings with %[^\"]... - */ - - for (i = 0, pptr ++; - i < (sizeof(location) - 1) && *pptr && *pptr != '\"'; - i ++, pptr ++) - location[i] = *pptr; - - if (i) - location[i] = '\0'; - - if (*pptr == '\"') - pptr ++; - - while (*pptr && isspace(*pptr & 255)) - pptr ++; - - if (*pptr == '\"') - { - for (i = 0, pptr ++; - i < (sizeof(info) - 1) && *pptr && *pptr != '\"'; - i ++, pptr ++) - info[i] = *pptr; - - info[i] = '\0'; - - if (*pptr == '\"') - pptr ++; - - while (*pptr && isspace(*pptr & 255)) - pptr ++; - - if (*pptr == '\"') - { - for (i = 0, pptr ++; - i < (sizeof(make_model) - 1) && *pptr && *pptr != '\"'; - i ++, pptr ++) - make_model[i] = *pptr; - - if (*pptr == '\"') - pptr ++; - make_model[i] = '\0'; - - if (*pptr) - num_attrs = cupsParseOptions(pptr, num_attrs, &attrs); - } - } - } - - DEBUG_puts(packet); - DEBUG_printf(("type=%x, state=%x, uri=\"%s\"\n" - "location=\"%s\", info=\"%s\", make_model=\"%s\"\n", - type, state, uri, location, info, make_model)); - - /* - * Pull the URI apart to see if this is a local or remote printer... - */ - - if (is_local_queue(uri, host, sizeof(host), resource, sizeof(resource))) - { - cupsFreeOptions(num_attrs, attrs); - return; - } - - /* - * Do relaying... - */ - - for (i = 0; i < NumRelays; i ++) - if (cupsdCheckAuth(address, srcname, len, 1, &(Relays[i].from))) - if (sendto(BrowseSocket, packet, bytes, 0, - (struct sockaddr *)&(Relays[i].to), - httpAddrLength(&(Relays[i].to))) <= 0) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "cupsdUpdateCUPSBrowse: sendto failed for relay %d - %s.", - i + 1, strerror(errno)); - cupsFreeOptions(num_attrs, attrs); - return; - } - - /* - * Process the browse data... - */ - - process_browse_data(uri, host, resource, (cups_ptype_t)type, - (ipp_pstate_t)state, location, info, make_model, - num_attrs, attrs); + 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_OPENLDAP /* - * 'cupsdUpdateLDAPBrowse()' - Scan for new printers via LDAP... + * 'dnssdRegisterCallback()' - DNSServiceRegister callback. */ -void -cupsdUpdateLDAPBrowse(void) +# 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 */ { - char uri[HTTP_MAX_URI], /* Printer URI */ - host[HTTP_MAX_URI], /* Hostname */ - resource[HTTP_MAX_URI], /* Resource path */ - location[1024], /* Printer location */ - info[1024], /* Printer information */ - make_model[1024], /* Printer make and model */ - **value; /* Holds the returned data from LDAP */ - int type; /* Printer type */ - int rc; /* LDAP status */ - int limit; /* Size limit */ - LDAPMessage *res, /* LDAP search results */ - *e; /* Current entry from search */ - + cupsd_printer_t *p = (cupsd_printer_t *)context; + /* Current printer */ - /* - * Search for printers... - */ - cupsdLogMessage(CUPSD_LOG_DEBUG2, "UpdateLDAPBrowse: %s", ServerName); + (void)sdRef; + (void)flags; + (void)domain; - BrowseLDAPRefresh = time(NULL) + BrowseInterval; + 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"); - rc = ldap_search_s(BrowseLDAPHandle, BrowseLDAPDN, LDAP_SCOPE_SUBTREE, - "(objectclass=cupsPrinter)", (char **)ldap_attrs, 0, &res); - if (rc != LDAP_SUCCESS) + if (errorCode) { cupsdLogMessage(CUPSD_LOG_ERROR, - "LDAP search returned error %d: %s", rc, - ldap_err2string(rc)); + "DNSServiceRegister failed with error %d", (int)errorCode); return; } - - limit = ldap_count_entries(BrowseLDAPHandle, res); - cupsdLogMessage(CUPSD_LOG_DEBUG2, "LDAP search returned %d entries", limit); - if (limit < 1) - return; - - /* - * Loop through the available printers... - */ - - for (e = ldap_first_entry(BrowseLDAPHandle, res); - e; - e = ldap_next_entry(BrowseLDAPHandle, e)) + else if (p && (!p->reg_name || _cups_strcasecmp(name, p->reg_name))) { - /* - * Get the required values from this entry... - */ - - if ((value = ldap_get_values(BrowseLDAPHandle, e, - "printerDescription")) == NULL) - continue; - - strlcpy(info, *value, sizeof(info)); - ldap_value_free(value); + cupsdLogMessage(CUPSD_LOG_INFO, "Using service name \"%s\" for \"%s\"", + name, p->name); - if ((value = ldap_get_values(BrowseLDAPHandle, e, - "printerLocation")) == NULL) - continue; - - strlcpy(location, *value, sizeof(location)); - ldap_value_free(value); - - if ((value = ldap_get_values(BrowseLDAPHandle, e, - "printerMakeAndModel")) == NULL) - continue; - - strlcpy(make_model, *value, sizeof(make_model)); - ldap_value_free(value); - - if ((value = ldap_get_values(BrowseLDAPHandle, e, - "printerType")) == NULL) - continue; - - type = atoi(*value); - ldap_value_free(value); - - if ((value = ldap_get_values(BrowseLDAPHandle, e, - "printerURI")) == NULL) - continue; - - strlcpy(uri, *value, sizeof(uri)); - ldap_value_free(value); - - /* - * Process the entry as browse data... - */ - - if (!is_local_queue(uri, host, sizeof(host), resource, sizeof(resource))) - process_browse_data(uri, host, resource, type, IPP_PRINTER_IDLE, - location, info, make_model, 0, NULL); + cupsArrayRemove(DNSSDPrinters, p); + cupsdSetString(&p->reg_name, name); + cupsArrayAdd(DNSSDPrinters, p); + LastEvent |= CUPSD_EVENT_PRINTER_MODIFIED; } } -#endif /* HAVE_OPENLDAP */ - -/* - * 'cupsdUpdatePolling()' - Read status messages from the poll daemons. - */ - -void -cupsdUpdatePolling(void) +# else /* HAVE_AVAHI */ +static void +dnssdRegisterCallback( + AvahiEntryGroup *srv, /* I - Service */ + AvahiEntryGroupState state, /* I - Registration state */ + void *context) /* I - Printer */ { - char *ptr, /* Pointer to end of line in buffer */ - message[1024]; /* Pointer to message text */ - int loglevel; /* Log level for message */ - - - while ((ptr = cupsdStatBufUpdate(PollStatusBuffer, &loglevel, - message, sizeof(message))) != NULL) - if (!strchr(PollStatusBuffer->buffer, '\n')) - break; + cupsd_printer_t *p = (cupsd_printer_t *)context; + /* Current printer */ - if (ptr == NULL && !PollStatusBuffer->bufused) - { - /* - * All polling processes have died; stop polling... - */ + 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"); - cupsdLogMessage(CUPSD_LOG_ERROR, - "cupsdUpdatePolling: all polling processes have exited!"); - cupsdStopPolling(); - } + /* TODO: Handle collisions with avahi_alternate_service_name(p->reg_name)? */ } +# endif /* HAVE_DNSSD */ -#ifdef HAVE_LIBSLP /* - * 'cupsdUpdateSLPBrowse()' - Get browsing information via SLP. + * 'dnssdRegisterInstance()' - Register an instance of a printer service. */ -void -cupsdUpdateSLPBrowse(void) +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? */ { - slpsrvurl_t *s, /* Temporary list of service URLs */ - *next; /* Next service in list */ - cupsd_printer_t p; /* Printer information */ - const char *uri; /* Pointer to printer URI */ - char host[HTTP_MAX_URI], /* Host portion of URI */ - resource[HTTP_MAX_URI]; /* Resource portion of URI */ + char temp[256], /* Temporary string */ + *ptr; /* Pointer into string */ + int error; /* Any error */ - /* - * Reset the refresh time... - */ - - BrowseSLPRefresh = time(NULL) + BrowseInterval; - - /* - * Poll for remote printers using SLP... - */ +# ifdef HAVE_DNSSD + (void)from_callback; +# endif /* HAVE_DNSSD */ - s = NULL; + cupsdLogMessage(CUPSD_LOG_DEBUG, "Registering \"%s\" with DNS-SD type \"%s\".", name, type); - SLPFindSrvs(BrowseSLPHandle, SLP_CUPS_SRVTYPE, "", "", - slp_url_callback, &s); - - /* - * Loop through the list of available printers... - */ - - for (; s; s = next) + if (p && !srv) { /* - * Save the "next" pointer... + * Assign the correct pointer for "srv"... */ - next = s->next; - - /* - * Load a cupsd_printer_t structure with the SLP service attributes... - */ - - SLPFindAttrs(BrowseSLPHandle, s->url, "", "", slp_attr_callback, &p); - - /* - * Process this printer entry... - */ - - uri = s->url + SLP_CUPS_SRVLEN + 1; - - if (!strncmp(uri, "http://", 7) || !strncmp(uri, "ipp://", 6)) - { - /* - * Pull the URI apart to see if this is a local or remote printer... - */ - - if (!is_local_queue(uri, host, sizeof(host), resource, sizeof(resource))) - process_browse_data(uri, host, resource, p.type, IPP_PRINTER_IDLE, - p.location, p.info, p.make_model, 0, NULL); - } - - /* - * Free this listing... - */ - - cupsdClearString(&p.info); - cupsdClearString(&p.location); - cupsdClearString(&p.make_model); - - free(s); - } -} -#endif /* HAVE_LIBSLP */ - - -/* - * 'dequote()' - Remote quotes from a string. - */ - -static char * /* O - Dequoted string */ -dequote(char *d, /* I - Destination string */ - const char *s, /* I - Source string */ - int dlen) /* I - Destination length */ -{ - char *dptr; /* Pointer into destination */ - - - if (s) - { - for (dptr = d, dlen --; *s && dlen > 0; s ++) - if (*s != '\"') - { - *dptr++ = *s; - dlen --; - } +# 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 */ - *dptr = '\0'; +# else /* HAVE_AVAHI */ + srv = &p->ipp_srv; /* Target service group */ +# endif /* HAVE_DNSSD */ } - else - *d = '\0'; - - return (d); -} - - -/* - * 'is_local_queue()' - Determine whether the URI points at a local queue. - */ - -static int /* O - 1 = local, 0 = remote, -1 = bad URI */ -is_local_queue(const char *uri, /* I - Printer URI */ - char *host, /* O - Host string */ - int hostlen, /* I - Length of host buffer */ - char *resource, /* O - Resource string */ - int resourcelen) /* I - Length of resource buffer */ -{ - char scheme[32], /* Scheme portion of URI */ - username[HTTP_MAX_URI]; /* Username portion of URI */ - int port; /* Port portion of URI */ - cupsd_netif_t *iface; /* Network interface */ - - - /* - * Pull the URI apart to see if this is a local or remote printer... - */ - - if (httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, sizeof(scheme), - username, sizeof(username), host, hostlen, &port, - resource, resourcelen) < HTTP_URI_OK) - return (-1); - - DEBUG_printf(("host=\"%s\", ServerName=\"%s\"\n", host, ServerName)); - - /* - * Check for local server addresses... - */ - - if (!strcasecmp(host, ServerName) && port == LocalPort) - return (1); - - cupsdNetIFUpdate(); - - for (iface = (cupsd_netif_t *)cupsArrayFirst(NetIFList); - iface; - iface = (cupsd_netif_t *)cupsArrayNext(NetIFList)) - if (!strcasecmp(host, iface->hostname) && port == iface->port) - return (1); - - /* - * If we get here, the printer is remote... - */ - - return (0); -} - - -/* - * 'process_browse_data()' - Process new browse data. - */ - -static void -process_browse_data( - const char *uri, /* I - URI of printer/class */ - const char *host, /* I - Hostname */ - const char *resource, /* I - Resource path */ - cups_ptype_t type, /* I - Printer type */ - ipp_pstate_t state, /* I - Printer state */ - const char *location, /* I - Printer location */ - const char *info, /* I - Printer information */ - const char *make_model, /* I - Printer make and model */ - int num_attrs, /* I - Number of attributes */ - cups_option_t *attrs) /* I - Attributes */ -{ - int i; /* Looping var */ - int update; /* Update printer attributes? */ - char finaluri[HTTP_MAX_URI], /* Final URI for printer */ - name[IPP_MAX_NAME], /* Name of printer */ - newname[IPP_MAX_NAME], /* New name of printer */ - *hptr, /* Pointer into hostname */ - *sptr; /* Pointer into ServerName */ - char local_make_model[IPP_MAX_NAME]; - /* Local make and model */ - cupsd_printer_t *p; /* Printer information */ - const char *ipp_options, /* ipp-options value */ - *lease_duration; /* lease-duration value */ +# ifdef HAVE_DNSSD + (void)commit; - /* - * Determine if the URI contains any illegal characters in it... - */ +# else /* HAVE_AVAHI */ + if (!from_callback) + avahi_threaded_poll_lock(DNSSDMaster); - if (strncmp(uri, "ipp://", 6) || !host[0] || - (strncmp(resource, "/printers/", 10) && - strncmp(resource, "/classes/", 9))) + if (!*srv) + *srv = avahi_entry_group_new(DNSSDClient, dnssdRegisterCallback, NULL); + if (!*srv) { - cupsdLogMessage(CUPSD_LOG_ERROR, - "process_browse_data: Bad printer URI in browse data: %s", - uri); - return; - } + if (!from_callback) + avahi_threaded_poll_unlock(DNSSDMaster); - if (strchr(resource, '?') || - (!strncmp(resource, "/printers/", 10) && strchr(resource + 10, '/')) || - (!strncmp(resource, "/classes/", 9) && strchr(resource + 9, '/'))) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "process_browse_data: Bad resource in browse data: %s", - resource); - return; + cupsdLogMessage(CUPSD_LOG_WARN, "DNS-SD registration of \"%s\" failed: %s", + name, dnssdErrorString(avahi_client_errno(DNSSDClient))); + return (0); } +# endif /* HAVE_DNSSD */ /* - * OK, this isn't a local printer; add any remote options... + * Make sure the name is <= 63 octets, and when we truncate be sure to + * properly truncate any UTF-8 characters... */ - ipp_options = cupsGetOption("ipp-options", num_attrs, attrs); - - if (BrowseRemoteOptions) + ptr = name + strlen(name); + while ((ptr - name) > 63) { - if (BrowseRemoteOptions[0] == '?') + do { - /* - * Override server-supplied options... - */ - - snprintf(finaluri, sizeof(finaluri), "%s%s", uri, BrowseRemoteOptions); + ptr --; } - else if (ipp_options) - { - /* - * Combine the server and local options... - */ + while (ptr > name && (*ptr & 0xc0) == 0x80); - snprintf(finaluri, sizeof(finaluri), "%s?%s+%s", uri, ipp_options, - BrowseRemoteOptions); - } - else - { - /* - * Just use the local options... - */ - - snprintf(finaluri, sizeof(finaluri), "%s?%s", uri, BrowseRemoteOptions); - } - - uri = finaluri; - } - else if (ipp_options) - { - /* - * Just use the server-supplied options... - */ - - snprintf(finaluri, sizeof(finaluri), "%s?%s", uri, ipp_options); - uri = finaluri; + if (ptr > name) + *ptr = '\0'; } /* - * See if we already have it listed in the Printers list, and add it if not... + * Register the service... */ - type |= CUPS_PRINTER_REMOTE; - type &= ~CUPS_PRINTER_IMPLICIT; - update = 0; - hptr = strchr(host, '.'); - sptr = strchr(ServerName, '.'); +# ifdef HAVE_DNSSD + if (subtypes) + snprintf(temp, sizeof(temp), "%s,%s", type, subtypes); + else + strlcpy(temp, type, sizeof(temp)); - if (!ServerNameIsIP && sptr != NULL && hptr != NULL) - { - /* - * Strip the common domain name components... - */ + *srv = DNSSDMaster; + error = DNSServiceRegister(srv, kDNSServiceFlagsShareConnection, + 0, name, temp, NULL, NULL, htons(port), + txt ? TXTRecordGetLength(txt) : 0, + txt ? TXTRecordGetBytesPtr(txt) : NULL, + dnssdRegisterCallback, p); - while (hptr != NULL) - { - if (!strcasecmp(hptr, sptr)) - { - *hptr = '\0'; - break; - } - else - hptr = strchr(hptr + 1, '.'); - } +# 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); } - if (type & CUPS_PRINTER_CLASS) + 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) { /* - * Remote destination is a class... + * Register all of the subtypes... */ - if (!strncmp(resource, "/classes/", 9)) - snprintf(name, sizeof(name), "%s@%s", resource + 9, host); - else - return; - - if (hptr && !*hptr) - *hptr = '.'; /* Resource FQDN */ - - if ((p = cupsdFindClass(name)) == NULL && BrowseShortNames) - { - if ((p = cupsdFindClass(resource + 9)) != NULL) - { - if (p->hostname && strcasecmp(p->hostname, host)) - { - /* - * Nope, this isn't the same host; if the hostname isn't the local host, - * add it to the other class and then find a class using the full host - * name... - */ - - if (p->type & CUPS_PRINTER_REMOTE) - { - cupsdLogMessage(CUPSD_LOG_DEBUG, - "Renamed remote class \"%s\" to \"%s@%s\"...", - p->name, p->name, p->hostname); - cupsdAddEvent(CUPSD_EVENT_PRINTER_DELETED, p, NULL, - "Class \'%s\' deleted by directory services.", - p->name); - - snprintf(newname, sizeof(newname), "%s@%s", p->name, p->hostname); - cupsdRenamePrinter(p, newname); - - cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED, p, NULL, - "Class \'%s\' added by directory services.", - p->name); - } + char *start, /* Start of subtype */ + subtype[256]; /* Subtype string */ - p = NULL; - } - else if (!p->hostname) - { - /* - * Hostname not set, so this must be a cached remote printer - * that was created for a pending print job... - */ + strlcpy(temp, subtypes, sizeof(temp)); - cupsdSetString(&p->hostname, host); - cupsdSetString(&p->uri, uri); - cupsdSetString(&p->device_uri, uri); - update = 1; - } - } - else - { - /* - * Use the short name for this shared class. - */ - - strlcpy(name, resource + 9, sizeof(name)); - } - } - else if (p && !p->hostname) + for (start = temp; *start; start = ptr) { /* - * Hostname not set, so this must be a cached remote printer - * that was created for a pending print job... + * Skip leading whitespace... */ - cupsdSetString(&p->hostname, host); - cupsdSetString(&p->uri, uri); - cupsdSetString(&p->device_uri, uri); - update = 1; - } + while (*start && isspace(*start & 255)) + start ++; - if (!p) - { /* - * Class doesn't exist; add it... + * Grab everything up to the next comma or the end of the string... */ - p = cupsdAddClass(name); + for (ptr = start; *ptr && *ptr != ','; ptr ++); - cupsdLogMessage(CUPSD_LOG_DEBUG, "Added remote class \"%s\"...", name); + if (*ptr) + *ptr++ = '\0'; - cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED, p, NULL, - "Class \'%s\' added by directory services.", name); + if (!*start) + break; /* - * Force the URI to point to the real server... + * Register the subtype... */ - p->type = type & ~CUPS_PRINTER_REJECTING; - p->accepting = 1; - cupsdSetString(&p->uri, uri); - cupsdSetString(&p->device_uri, uri); - cupsdSetString(&p->hostname, host); - - update = 1; - } - } - else - { - /* - * Remote destination is a printer... - */ - - if (!strncmp(resource, "/printers/", 10)) - snprintf(name, sizeof(name), "%s@%s", resource + 10, host); - else - return; - - if (hptr && !*hptr) - *hptr = '.'; /* Resource FQDN */ + snprintf(subtype, sizeof(subtype), "%s._sub.%s", start, type); - if ((p = cupsdFindPrinter(name)) == NULL && BrowseShortNames) - { - if ((p = cupsdFindPrinter(resource + 10)) != NULL) + error = avahi_entry_group_add_service_subtype(*srv, AVAHI_IF_UNSPEC, + AVAHI_PROTO_UNSPEC, 0, + name, type, NULL, subtype); + if (error) { - if (p->hostname && strcasecmp(p->hostname, host)) - { - /* - * Nope, this isn't the same host; if the hostname isn't the local host, - * add it to the other printer and then find a printer using the full host - * name... - */ - - if (p->type & CUPS_PRINTER_REMOTE) - { - cupsdLogMessage(CUPSD_LOG_DEBUG, - "Renamed remote printer \"%s\" to \"%s@%s\"...", - p->name, p->name, p->hostname); - cupsdAddEvent(CUPSD_EVENT_PRINTER_DELETED, p, NULL, - "Printer \'%s\' deleted by directory services.", - p->name); - - snprintf(newname, sizeof(newname), "%s@%s", p->name, p->hostname); - cupsdRenamePrinter(p, newname); - - cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED, p, NULL, - "Printer \'%s\' added by directory services.", - p->name); - } - - p = NULL; - } - else if (!p->hostname) - { - /* - * Hostname not set, so this must be a cached remote printer - * that was created for a pending print job... - */ - - cupsdSetString(&p->hostname, host); - cupsdSetString(&p->uri, uri); - cupsdSetString(&p->device_uri, uri); - update = 1; - } - } - else - { - /* - * Use the short name for this shared printer. - */ - - strlcpy(name, resource + 10, sizeof(name)); + cupsdLogMessage(CUPSD_LOG_DEBUG, + "DNS-SD subtype %s registration for \"%s\" failed." , + subtype, name); + break; } } - else if (p && !p->hostname) - { - /* - * Hostname not set, so this must be a cached remote printer - * that was created for a pending print job... - */ - - cupsdSetString(&p->hostname, host); - cupsdSetString(&p->uri, uri); - cupsdSetString(&p->device_uri, uri); - update = 1; - } - - if (!p) - { - /* - * Printer doesn't exist; add it... - */ - - p = cupsdAddPrinter(name); - - cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED, p, NULL, - "Printer \'%s\' added by directory services.", name); - - cupsdLogMessage(CUPSD_LOG_DEBUG, "Added remote printer \"%s\"...", name); - - /* - * Force the URI to point to the real server... - */ - - p->type = type & ~CUPS_PRINTER_REJECTING; - p->accepting = 1; - cupsdSetString(&p->hostname, host); - cupsdSetString(&p->uri, uri); - cupsdSetString(&p->device_uri, uri); - - update = 1; - } } - /* - * Update the state... - */ - - p->state = state; - p->browse_time = time(NULL); - - if ((lease_duration = cupsGetOption("lease-duration", num_attrs, - attrs)) != NULL) + if (!error && commit) { - /* - * Grab the lease-duration for the browse data; anything less then 1 - * second or more than 1 week gets the default BrowseTimeout... - */ - - i = atoi(lease_duration); - if (i < 1 || i > 604800) - i = BrowseTimeout; - - p->browse_expire = p->browse_time + i; - } - else - p->browse_expire = p->browse_time + BrowseTimeout; - - if (type & CUPS_PRINTER_REJECTING) - { - type &= ~CUPS_PRINTER_REJECTING; - - if (p->accepting) - { - update = 1; - p->accepting = 0; - } - } - else if (!p->accepting) - { - update = 1; - p->accepting = 1; - } - - if (p->type != type) - { - p->type = type; - update = 1; - } - - if (location && (!p->location || strcmp(p->location, location))) - { - cupsdSetString(&p->location, location); - update = 1; + if ((error = avahi_entry_group_commit(*srv)) != 0) + cupsdLogMessage(CUPSD_LOG_DEBUG, "DNS-SD commit of \"%s\" failed.", + name); } - if (info && (!p->info || strcmp(p->info, info))) - { - cupsdSetString(&p->info, info); - update = 1; - } + if (!from_callback) + avahi_threaded_poll_unlock(DNSSDMaster); +# endif /* HAVE_DNSSD */ - if (!make_model || !make_model[0]) + if (error) { - if (type & CUPS_PRINTER_CLASS) - snprintf(local_make_model, sizeof(local_make_model), - "Remote Class on %s", host); - else - snprintf(local_make_model, sizeof(local_make_model), - "Remote Printer on %s", host); + 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); } - else - snprintf(local_make_model, sizeof(local_make_model), - "%s on %s", make_model, host); - if (!p->make_model || strcmp(p->make_model, local_make_model)) - { - cupsdSetString(&p->make_model, local_make_model); - update = 1; - } + return (!error); +} - if (p->num_options) - { - if (!update && !(type & CUPS_PRINTER_DELETE)) - { - /* - * See if we need to update the attributes... - */ - if (p->num_options != num_attrs) - update = 1; - else - { - for (i = 0; i < num_attrs; i ++) - if (strcmp(attrs[i].name, p->options[i].name) || - (!attrs[i].value != !p->options[i].value) || - (attrs[i].value && strcmp(attrs[i].value, p->options[i].value))) - { - update = 1; - break; - } - } - } +/* + * 'dnssdRegisterPrinter()' - Start sending broadcast information for a printer + * or update the broadcast contents. + */ - /* - * Free the old options... - */ +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 */ - cupsFreeOptions(p->num_options, p->options); - } - p->num_options = num_attrs; - p->options = attrs; + cupsdLogMessage(CUPSD_LOG_DEBUG2, "dnssdRegisterPrinter(%s) %s", p->name, + !p->ipp_srv ? "new" : "update"); - if (type & CUPS_PRINTER_DELETE) - { - cupsdAddEvent(CUPSD_EVENT_PRINTER_DELETED, p, NULL, - "%s \'%s\' deleted by directory services.", - (type & CUPS_PRINTER_CLASS) ? "Class" : "Printer", p->name); - - cupsdExpireSubscriptions(p, NULL); - - cupsdDeletePrinter(p, 1); - cupsdUpdateImplicitClasses(); - } - else if (update) - { - cupsdSetPrinterAttrs(p); - cupsdUpdateImplicitClasses(); - } +# ifdef HAVE_AVAHI + if (!avahi_running) + return; +# endif /* HAVE_AVAHI */ /* - * See if we have a default printer... If not, make the first network - * default printer the default. + * Remove the current registrations if we have them and then return if + * per-printer sharing was just disabled... */ - if (DefaultPrinter == NULL && Printers != NULL && UseNetworkDefault) - { - /* - * Find the first network default printer and use it... - */ + dnssdDeregisterPrinter(p, 0, from_callback); - for (p = (cupsd_printer_t *)cupsArrayFirst(Printers); - p; - p = (cupsd_printer_t *)cupsArrayNext(Printers)) - if (p->type & CUPS_PRINTER_DEFAULT) - { - DefaultPrinter = p; - break; - } - } + if (!p->shared) + return; /* - * Do auto-classing if needed... + * Set the registered name as needed; the registered name takes the form of + * " @ "... */ - process_implicit_classes(); + 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)); /* - * Update the printcap file... + * 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... */ - cupsdWritePrintcap(); -} - + ipp_txt = dnssdBuildTxtRecord(p, 0); + printer_txt = dnssdBuildTxtRecord(p, 1); -/* - * 'process_implicit_classes()' - Create/update implicit classes as needed. - */ + if (BrowseLocalProtocols & BROWSE_LPD) + printer_port = 515; + else + printer_port = 0; -static void -process_implicit_classes(void) -{ - int i; /* Looping var */ - int update; /* Update printer attributes? */ - char name[IPP_MAX_NAME], /* Name of printer */ - *hptr; /* Pointer into hostname */ - cupsd_printer_t *p, /* Printer information */ - *pclass, /* Printer class */ - *first; /* First printer in class */ - int offset, /* Offset of name */ - len; /* Length of name */ - - - if (!ImplicitClasses || !Printers) - return; + status = dnssdRegisterInstance(NULL, p, name, "_printer._tcp", NULL, printer_port, &printer_txt, 0, from_callback); - /* - * Loop through all available printers and create classes as needed... - */ +# ifdef HAVE_SSL + if (status) + dnssdRegisterInstance(NULL, p, name, "_ipps._tcp", DNSSDSubTypes, DNSSDPort, &ipp_txt, 0, from_callback); +# endif /* HAVE_SSL */ - for (p = (cupsd_printer_t *)cupsArrayFirst(Printers), len = 0, offset = 0, - update = 0, pclass = NULL, first = NULL; - p != NULL; - p = (cupsd_printer_t *)cupsArrayNext(Printers)) + if (status) { /* - * Skip implicit classes... + * Use the "_fax-ipp" service type for fax queues, otherwise use "_ipp"... */ - if (p->type & CUPS_PRINTER_IMPLICIT) - { - len = 0; - continue; - } - - /* - * If len == 0, get the length of this printer name up to the "@" - * sign (if any). - */ - - cupsArraySave(Printers); - - if (len > 0 && - !strncasecmp(p->name, name + offset, len) && - (p->name[len] == '\0' || p->name[len] == '@')) - { - /* - * We have more than one printer with the same name; see if - * we have a class, and if this printer is a member... - */ - - if (pclass && strcasecmp(pclass->name, name)) - { - if (update) - cupsdSetPrinterAttrs(pclass); - - update = 0; - pclass = NULL; - } - - if (!pclass && (pclass = cupsdFindDest(name)) == NULL) - { - /* - * Need to add the class... - */ - - pclass = cupsdAddPrinter(name); - cupsArrayAdd(ImplicitPrinters, pclass); - - pclass->type |= CUPS_PRINTER_IMPLICIT; - pclass->accepting = 1; - pclass->state = IPP_PRINTER_IDLE; - - cupsdSetString(&pclass->location, p->location); - cupsdSetString(&pclass->info, p->info); - - update = 1; - - cupsdLogMessage(CUPSD_LOG_DEBUG, "Added implicit class \"%s\"...", - name); - cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED, p, NULL, - "Implicit class \'%s\' added by directory services.", - name); - } - - if (first != NULL) - { - for (i = 0; i < pclass->num_printers; i ++) - if (pclass->printers[i] == first) - break; - - if (i >= pclass->num_printers) - { - first->in_implicit_class = 1; - cupsdAddPrinterToClass(pclass, first); - } - - first = NULL; - } - - for (i = 0; i < pclass->num_printers; i ++) - if (pclass->printers[i] == p) - break; - - if (i >= pclass->num_printers) - { - p->in_implicit_class = 1; - cupsdAddPrinterToClass(pclass, p); - update = 1; - } - } + if (p->type & CUPS_PRINTER_FAX) + status = dnssdRegisterInstance(NULL, p, name, "_fax-ipp._tcp", DNSSDSubTypes, DNSSDPort, &ipp_txt, 1, from_callback); else - { - /* - * First time around; just get name length and mark it as first - * in the list... - */ - - if ((hptr = strchr(p->name, '@')) != NULL) - len = hptr - p->name; - else - len = strlen(p->name); - - strncpy(name, p->name, len); - name[len] = '\0'; - offset = 0; - - if ((first = (hptr ? cupsdFindDest(name) : p)) != NULL && - !(first->type & CUPS_PRINTER_IMPLICIT)) - { - /* - * Can't use same name as a local printer; add "Any" to the - * front of the name, unless we have explicitly disabled - * the "ImplicitAnyClasses"... - */ + status = dnssdRegisterInstance(NULL, p, name, "_ipp._tcp", DNSSDSubTypes, DNSSDPort, &ipp_txt, 1, from_callback); + } - if (ImplicitAnyClasses && len < (sizeof(name) - 4)) - { - /* - * Add "Any" to the class name... - */ + dnssdFreeTxtRecord(&ipp_txt); + dnssdFreeTxtRecord(&printer_txt); - strcpy(name, "Any"); - strncpy(name + 3, p->name, len); - name[len + 3] = '\0'; - offset = 3; - } - else - { - /* - * Don't create an implicit class if we have a local printer - * with the same name... - */ + if (status) + { + /* + * Save the registered name and add the printer to the array of DNS-SD + * printers... + */ - len = 0; - cupsArrayRestore(Printers); - continue; - } - } + cupsdSetString(&p->reg_name, name); + cupsArrayAdd(DNSSDPrinters, p); + } + else + { + /* + * Registration failed for this printer... + */ - first = p; - } + dnssdDeregisterInstance(&p->ipp_srv, from_callback); - cupsArrayRestore(Printers); +# ifdef HAVE_DNSSD +# ifdef HAVE_SSL + dnssdDeregisterInstance(&p->ipps_srv, from_callback); +# endif /* HAVE_SSL */ + dnssdDeregisterInstance(&p->printer_srv, from_callback); +# endif /* HAVE_DNSSD */ } - - /* - * Update the last printer class as needed... - */ - - if (pclass && update) - cupsdSetPrinterAttrs(pclass); } /* - * 'send_cups_browse()' - Send new browsing information using the CUPS - * protocol. + * 'dnssdStop()' - Stop all DNS-SD registrations. */ static void -send_cups_browse(cupsd_printer_t *p) /* I - Printer to send */ +dnssdStop(void) { - int i; /* Looping var */ - cups_ptype_t type; /* Printer type */ - cupsd_dirsvc_addr_t *b; /* Browse address */ - int bytes; /* Length of packet */ - char packet[1453], /* Browse data packet */ - uri[1024], /* Printer URI */ - location[1024], /* printer-location */ - info[1024], /* printer-info */ - make_model[1024]; - /* printer-make-and-model */ - cupsd_netif_t *iface; /* Network interface */ - - - /* - * Figure out the printer type value... - */ - - type = p->type | CUPS_PRINTER_REMOTE; - - if (!p->accepting) - type |= CUPS_PRINTER_REJECTING; + cupsd_printer_t *p; /* Current printer */ - if (p == DefaultPrinter) - type |= CUPS_PRINTER_DEFAULT; /* - * Remove quotes from printer-info, printer-location, and - * printer-make-and-model attributes... + * De-register the individual printers */ - dequote(location, p->location, sizeof(location)); - dequote(info, p->info, sizeof(info)); - - if (p->make_model) - dequote(make_model, p->make_model, sizeof(make_model)); - else if (p->type & CUPS_PRINTER_CLASS) - { - if (p->num_printers > 0 && p->printers[0]->make_model) - strlcpy(make_model, p->printers[0]->make_model, sizeof(make_model)); - else - strlcpy(make_model, "Local Printer Class", sizeof(make_model)); - } - else if (p->raw) - strlcpy(make_model, "Local Raw Printer", sizeof(make_model)); - else - strlcpy(make_model, "Local System V Printer", sizeof(make_model)); + for (p = (cupsd_printer_t *)cupsArrayFirst(Printers); + p; + p = (cupsd_printer_t *)cupsArrayNext(Printers)) + dnssdDeregisterPrinter(p, 1, 0); /* - * Send a packet to each browse address... + * Shutdown the rest of the service refs... */ - for (i = NumBrowsers, b = Browsers; i > 0; i --, b ++) - if (b->iface[0]) - { - /* - * Send the browse packet to one or more interfaces... - */ - - if (!strcmp(b->iface, "*")) - { - /* - * Send to all local interfaces... - */ - - cupsdNetIFUpdate(); - - for (iface = (cupsd_netif_t *)cupsArrayFirst(NetIFList); - iface; - iface = (cupsd_netif_t *)cupsArrayNext(NetIFList)) - { - /* - * Only send to local, IPv4 interfaces... - */ - - if (!iface->is_local || !iface->port || - iface->address.addr.sa_family != AF_INET) - continue; + dnssdDeregisterInstance(&WebIFSrv, 0); - httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, - iface->hostname, iface->port, - (p->type & CUPS_PRINTER_CLASS) ? "/classes/%s" : - "/printers/%s", - p->name); - snprintf(packet, sizeof(packet), "%x %x %s \"%s\" \"%s\" \"%s\" %s\n", - type, p->state, uri, location, info, make_model, - p->browse_attrs ? p->browse_attrs : ""); +# ifdef HAVE_DNSSD + cupsdRemoveSelect(DNSServiceRefSockFD(DNSSDMaster)); - bytes = strlen(packet); + DNSServiceRefDeallocate(DNSSDMaster); + DNSSDMaster = NULL; - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdSendBrowseList: (%d bytes to \"%s\") %s", bytes, - iface->name, packet); +# else /* HAVE_AVAHI */ + if (DNSSDMaster) + avahi_threaded_poll_stop(DNSSDMaster); - iface->broadcast.ipv4.sin_port = htons(BrowsePort); - - sendto(BrowseSocket, packet, bytes, 0, - (struct sockaddr *)&(iface->broadcast), - httpAddrLength(&(iface->broadcast))); - } - } - else if ((iface = cupsdNetIFFind(b->iface)) != NULL) - { - /* - * Send to the named interface using the IPv4 address... - */ + if (DNSSDClient) + { + avahi_client_free(DNSSDClient); + DNSSDClient = NULL; + } - while (iface) - if (strcmp(b->iface, iface->name)) - { - iface = NULL; - break; - } - else if (iface->address.addr.sa_family == AF_INET && iface->port) - break; - else - iface = (cupsd_netif_t *)cupsArrayNext(NetIFList); + if (DNSSDMaster) + { + avahi_threaded_poll_free(DNSSDMaster); + DNSSDMaster = NULL; + } +# endif /* HAVE_DNSSD */ - if (iface) - { - httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, - iface->hostname, iface->port, - (p->type & CUPS_PRINTER_CLASS) ? "/classes/%s%s" : - "/printers/%s", - p->name); - snprintf(packet, sizeof(packet), "%x %x %s \"%s\" \"%s\" \"%s\" %s\n", - type, p->state, uri, location, info, make_model, - p->browse_attrs ? p->browse_attrs : ""); - - bytes = strlen(packet); - - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdSendBrowseList: (%d bytes to \"%s\") %s", bytes, - iface->name, packet); - - iface->broadcast.ipv4.sin_port = htons(BrowsePort); - - sendto(BrowseSocket, packet, bytes, 0, - (struct sockaddr *)&(iface->broadcast), - httpAddrLength(&(iface->broadcast))); - } - } - } - else - { - /* - * Send the browse packet to the indicated address using - * the default server name... - */ + cupsArrayDelete(DNSSDPrinters); + DNSSDPrinters = NULL; - snprintf(packet, sizeof(packet), "%x %x %s \"%s\" \"%s\" \"%s\" %s\n", - type, p->state, p->uri, location, info, make_model, - p->browse_attrs ? p->browse_attrs : ""); + DNSSDPort = 0; +} - bytes = strlen(packet); - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdSendBrowseList: (%d bytes) %s", bytes, packet); - if (sendto(BrowseSocket, packet, bytes, 0, - (struct sockaddr *)&(b->to), - httpAddrLength(&(b->to))) <= 0) - { - /* - * Unable to send browse packet, so remove this address from the - * list... - */ +# ifdef HAVE_DNSSD +/* + * 'dnssdUpdate()' - Handle DNS-SD queries. + */ - cupsdLogMessage(CUPSD_LOG_ERROR, - "cupsdSendBrowseList: sendto failed for browser " - "%d - %s.", - (int)(b - Browsers + 1), strerror(errno)); +static void +dnssdUpdate(void) +{ + DNSServiceErrorType sdErr; /* Service discovery error */ - if (i > 1) - memmove(b, b + 1, (i - 1) * sizeof(cupsd_dirsvc_addr_t)); - b --; - NumBrowsers --; - } - } + if ((sdErr = DNSServiceProcessResult(DNSSDMaster)) != kDNSServiceErr_NoError) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "DNS Service Discovery registration error %d!", + sdErr); + dnssdStop(); + } } +# endif /* HAVE_DNSSD */ -#ifdef HAVE_OPENLDAP /* - * 'send_ldap_browse()' - Send LDAP printer registrations. + * 'dnssdUpdateDNSSDName()' - Update the listen port, computer name, and web interface registration. */ static void -send_ldap_browse(cupsd_printer_t *p) /* I - Printer to register */ +dnssdUpdateDNSSDName(int from_callback) /* I - Called from callback? */ { - int i; /* Looping var... */ - LDAPMod mods[7]; /* The 7 attributes we will be adding */ - LDAPMod *pmods[8]; /* Pointers to the 7 attributes + NULL */ - LDAPMessage *res; /* Search result token */ - char *cn_value[2], /* Change records */ - *uri[2], - *info[2], - *location[2], - *make_model[2], - *type[2], - typestring[255], /* String to hold printer-type */ - filter[256], /* Search filter for possible UPDATEs */ - dn[1024]; /* DN of the printer we are adding */ - int rc; /* LDAP status */ - static const char * const objectClass_values[] = - { /* The 3 objectClass's we use in */ - "top", /* our LDAP entries */ - "device", - "cupsPrinter", - NULL - }; - - cupsdLogMessage(CUPSD_LOG_DEBUG2, "send_ldap_browse: %s\n", p->name); + 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__ */ + /* - * Everything in ldap is ** so we fudge around it... + * Only share the web interface and printers when non-local listening is + * enabled... */ - sprintf(typestring, "%u", p->type); - - cn_value[0] = p->name; - cn_value[1] = NULL; - info[0] = p->info ? p->info : "Unknown"; - info[1] = NULL; - location[0] = p->location ? p->location : "Unknown"; - location[1] = NULL; - make_model[0] = p->make_model ? p->make_model : "Unknown"; - make_model[1] = NULL; - type[0] = typestring; - type[1] = NULL; - uri[0] = p->uri; - uri[1] = NULL; - - snprintf(filter, sizeof(filter), - "(&(objectclass=cupsPrinter)(printerURI=%s))", p->uri); - - ldap_search_s(BrowseLDAPHandle, BrowseLDAPDN, LDAP_SCOPE_SUBTREE, - filter, (char **)ldap_attrs, 0, &res); - cupsdLogMessage(CUPSD_LOG_DEBUG2, "send_ldap_browse: Searching \"%s\"", - filter); - - mods[0].mod_type = "cn"; - mods[0].mod_values = cn_value; - mods[1].mod_type = "printerDescription"; - mods[1].mod_values = info; - mods[2].mod_type = "printerURI"; - mods[2].mod_values = uri; - mods[3].mod_type = "printerLocation"; - mods[3].mod_values = location; - mods[4].mod_type = "printerMakeAndModel"; - mods[4].mod_values = make_model; - mods[5].mod_type = "printerType"; - mods[5].mod_values = type; - mods[6].mod_type = "objectClass"; - mods[6].mod_values = (char **)objectClass_values; - - snprintf(dn, sizeof(dn), "cn=%s,ou=printers,%s", p->name, BrowseLDAPDN); - cupsdLogMessage(CUPSD_LOG_DEBUG2, "send_ldap_browse: dn=\"%s\"", dn); - - if (ldap_count_entries(BrowseLDAPHandle, res) > 0) + if (!DNSSDPort) { /* - * Printer has already been registered, modify the current - * registration... + * 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... */ - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "send_ldap_browse: Replacing entry..."); + cupsd_listener_t *lis; /* Current listening socket */ - for (i = 0; i < 7; i ++) + for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners); + lis; + lis = (cupsd_listener_t *)cupsArrayNext(Listeners)) { - pmods[i] = mods + i; - pmods[i]->mod_op = LDAP_MOD_REPLACE; - } - pmods[i] = NULL; + if (httpAddrLocalhost(&(lis->address))) + continue; - if ((rc = ldap_modify_s(BrowseLDAPHandle, dn, pmods)) != LDAP_SUCCESS) - cupsdLogMessage(CUPSD_LOG_ERROR, - "LDAP modify for %s failed with status %d: %s", - p->name, rc, ldap_err2string(rc)); + DNSSDPort = httpAddrPort(&(lis->address)); + break; + } } - else + + if (!DNSSDPort) + return; + + /* + * Get the computer name as a c-string... + */ + +# ifdef __APPLE__ + sc = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("cupsd"), NULL, NULL); + + if (sc) { /* - * Printer has never been registered, add the current - * registration... + * Get the computer name from the dynamic store... */ - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "send_ldap_browse: Adding entry..."); + cupsdClearString(&DNSSDComputerName); - for (i = 0; i < 7; i ++) + if ((nameRef = SCDynamicStoreCopyComputerName(sc, &nameEncoding)) != NULL) { - pmods[i] = mods + i; - pmods[i]->mod_op = LDAP_MOD_ADD; + if (CFStringGetCString(nameRef, nameBuffer, sizeof(nameBuffer), + kCFStringEncodingUTF8)) + { + cupsdLogMessage(CUPSD_LOG_DEBUG, + "Dynamic store computer name is \"%s\".", nameBuffer); + cupsdSetString(&DNSSDComputerName, nameBuffer); + } + + CFRelease(nameRef); } - pmods[i] = NULL; - if ((rc = ldap_add_s(BrowseLDAPHandle, dn, pmods)) != LDAP_SUCCESS) - cupsdLogMessage(CUPSD_LOG_ERROR, - "LDAP add for %s failed with status %d: %s", - p->name, rc, ldap_err2string(rc)); - } -} -#endif /* HAVE_OPENLDAP */ + if (!DNSSDComputerName) + { + /* + * Use the ServerName instead... + */ + cupsdLogMessage(CUPSD_LOG_DEBUG, + "Using ServerName \"%s\" as computer name.", ServerName); + cupsdSetString(&DNSSDComputerName, ServerName); + } -#ifdef HAVE_LIBSLP -/* - * 'send_slp_browse()' - Register the specified printer with SLP. - */ + /* + * Get the local hostname from the dynamic store... + */ -static void -send_slp_browse(cupsd_printer_t *p) /* I - Printer to register */ -{ - char srvurl[HTTP_MAX_URI], /* Printer service URI */ - attrs[8192], /* Printer attributes */ - finishings[1024], /* Finishings to support */ - make_model[IPP_MAX_NAME * 2], - /* Make and model, quoted */ - location[IPP_MAX_NAME * 2], - /* Location, quoted */ - info[IPP_MAX_NAME * 2], /* Info, quoted */ - *src, /* Pointer to original string */ - *dst; /* Pointer to destination string */ - ipp_attribute_t *authentication; /* uri-authentication-supported value */ - SLPError error; /* SLP error, if any */ - - - cupsdLogMessage(CUPSD_LOG_DEBUG, "send_slp_browse(%p = \"%s\")", p, - p->name); + cupsdClearString(&DNSSDHostName); - /* - * Make the SLP service URL that conforms to the IANA - * 'printer:' template. - */ + 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); + } - snprintf(srvurl, sizeof(srvurl), SLP_CUPS_SRVTYPE ":%s", p->uri); + CFRelease(nameRef); + } - cupsdLogMessage(CUPSD_LOG_DEBUG2, "Service URL = \"%s\"", srvurl); + if (!DNSSDHostName) + { + /* + * Use the ServerName instead... + */ - /* - * Figure out the finishings string... - */ + cupsdLogMessage(CUPSD_LOG_DEBUG, + "Using ServerName \"%s\" as host name.", ServerName); + cupsdSetString(&DNSSDHostName, ServerName); + } - if (p->type & CUPS_PRINTER_STAPLE) - strcpy(finishings, "staple"); - else - finishings[0] = '\0'; + /* + * Get any Back-to-My-Mac domains and add them as aliases... + */ - if (p->type & CUPS_PRINTER_BIND) - { - if (finishings[0]) - strlcat(finishings, ",bind", sizeof(finishings)); - else - strcpy(finishings, "bind"); - } + cupsdFreeAliases(DNSSDAlias); + DNSSDAlias = NULL; - if (p->type & CUPS_PRINTER_PUNCH) - { - if (finishings[0]) - strlcat(finishings, ",punch", sizeof(finishings)); + 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 - strcpy(finishings, "punch"); - } + cupsdLogMessage(CUPSD_LOG_DEBUG, "No Back to My Mac aliases to add."); + + if (btmm) + CFRelease(btmm); - if (p->type & CUPS_PRINTER_COVER) + CFRelease(sc); + } + else +# endif /* __APPLE__ */ +# ifdef HAVE_AVAHI + if (DNSSDClient) { - if (finishings[0]) - strlcat(finishings, ",cover", sizeof(finishings)); + 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 - strcpy(finishings, "cover"); + cupsdSetStringf(&DNSSDHostName, "%s.local", ServerName); } - - if (p->type & CUPS_PRINTER_SORT) + else +# endif /* HAVE_AVAHI */ { - if (finishings[0]) - strlcat(finishings, ",sort", sizeof(finishings)); + cupsdSetString(&DNSSDComputerName, ServerName); + + if (strchr(ServerName, '.')) + cupsdSetString(&DNSSDHostName, ServerName); else - strcpy(finishings, "sort"); + cupsdSetStringf(&DNSSDHostName, "%s.local", ServerName); } - if (!finishings[0]) - strcpy(finishings, "none"); - /* - * Quote any commas in the make and model, location, and info strings... + * Then (re)register the web interface if enabled... */ - for (src = p->make_model, dst = make_model; - src && *src && dst < (make_model + sizeof(make_model) - 2);) + if (BrowseWebIF) { - if (*src == ',' || *src == '\\' || *src == ')') - *dst++ = '\\'; + if (DNSSDComputerName) + snprintf(webif, sizeof(webif), "CUPS @ %s", DNSSDComputerName); + else + strlcpy(webif, "CUPS", sizeof(webif)); - *dst++ = *src++; + dnssdDeregisterInstance(&WebIFSrv, from_callback); + dnssdRegisterInstance(&WebIFSrv, NULL, webif, "_http._tcp", "_printer", DNSSDPort, NULL, 1, from_callback); } +} - *dst = '\0'; - - if (!make_model[0]) - strcpy(make_model, "Unknown"); - for (src = p->location, dst = location; - src && *src && dst < (location + sizeof(location) - 2);) - { - if (*src == ',' || *src == '\\' || *src == ')') - *dst++ = '\\'; +/* + * 'get_auth_info_required()' - Get the auth-info-required value to advertise. + */ - *dst++ = *src++; - } +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 */ - *dst = '\0'; - if (!location[0]) - strcpy(location, "Unknown"); + /* + * If auth-info-required is set for this printer, return that... + */ - for (src = p->info, dst = info; - src && *src && dst < (info + sizeof(info) - 2);) + if (p->num_auth_info_required > 0 && strcmp(p->auth_info_required[0], "none")) { - if (*src == ',' || *src == '\\' || *src == ')') - *dst++ = '\\'; + int i; /* Looping var */ + char *bufptr; /* Pointer into buffer */ - *dst++ = *src++; - } + for (i = 0, bufptr = buffer; i < p->num_auth_info_required; i ++) + { + if (bufptr >= (buffer + bufsize - 2)) + break; + + if (i) + *bufptr++ = ','; - *dst = '\0'; + strlcpy(bufptr, p->auth_info_required[i], bufsize - (size_t)(bufptr - buffer)); + bufptr += strlen(bufptr); + } - if (!info[0]) - strcpy(info, "Unknown"); + return (buffer); + } /* - * Get the authentication value... + * Figure out the authentication data requirements to advertise... */ - authentication = ippFindAttribute(p->attrs, "uri-authentication-supported", - IPP_TAG_KEYWORD); + if (p->type & CUPS_PRINTER_CLASS) + snprintf(resource, sizeof(resource), "/classes/%s", p->name); + else + snprintf(resource, sizeof(resource), "/printers/%s", p->name); - /* - * Make the SLP attribute string list that conforms to - * the IANA 'printer:' template. - */ + if ((auth = cupsdFindBest(resource, HTTP_POST)) == NULL || + auth->type == CUPSD_AUTH_NONE) + auth = cupsdFindPolicyOp(p->op_policy_ptr, IPP_PRINT_JOB); - snprintf(attrs, sizeof(attrs), - "(printer-uri-supported=%s)," - "(uri-authentication-supported=%s>)," -#ifdef HAVE_SSL - "(uri-security-supported=tls>)," -#else - "(uri-security-supported=none>)," -#endif /* HAVE_SSL */ - "(printer-name=%s)," - "(printer-location=%s)," - "(printer-info=%s)," - "(printer-more-info=%s)," - "(printer-make-and-model=%s)," - "(printer-type=%d)," - "(charset-supported=utf-8)," - "(natural-language-configured=%s)," - "(natural-language-supported=de,en,es,fr,it)," - "(color-supported=%s)," - "(finishings-supported=%s)," - "(sides-supported=one-sided%s)," - "(multiple-document-jobs-supported=true)" - "(ipp-versions-supported=1.0,1.1)", - p->uri, authentication->values[0].string.text, p->name, location, - info, p->uri, make_model, p->type, DefaultLanguage, - p->type & CUPS_PRINTER_COLOR ? "true" : "false", - finishings, - p->type & CUPS_PRINTER_DUPLEX ? - ",two-sided-long-edge,two-sided-short-edge" : ""); - - cupsdLogMessage(CUPSD_LOG_DEBUG2, "Attributes = \"%s\"", attrs); + if (auth) + { + int auth_type; /* Authentication type */ - /* - * Register the printer with the SLP server... - */ + if ((auth_type = auth->type) == CUPSD_AUTH_DEFAULT) + auth_type = cupsdDefaultAuthType(); - error = SLPReg(BrowseSLPHandle, srvurl, BrowseTimeout, - SLP_CUPS_SRVTYPE, attrs, SLP_TRUE, slp_reg_callback, 0); + 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); + } - if (error != SLP_OK) - cupsdLogMessage(CUPSD_LOG_ERROR, "SLPReg of \"%s\" failed with status %d!", p->name, - error); + return ("none"); } +#endif /* HAVE_DNSSD || HAVE_AVAHI */ +#ifdef __APPLE__ /* - * 'slp_attr_callback()' - SLP attribute callback + * 'get_hostconfig()' - Get an /etc/hostconfig service setting. */ -static SLPBoolean /* O - SLP_TRUE for success */ -slp_attr_callback( - SLPHandle hslp, /* I - SLP handle */ - const char *attrlist, /* I - Attribute list */ - SLPError errcode, /* I - Parsing status for this attr */ - void *cookie) /* I - Current printer */ +static int /* O - 1 for YES or AUTOMATIC, 0 for NO */ +get_hostconfig(const char *name) /* I - Name of service */ { - char *tmp = 0; /* Temporary string */ - cupsd_printer_t *p = (cupsd_printer_t*)cookie; - /* Current printer */ - + cups_file_t *fp; /* Hostconfig file */ + char line[1024], /* Line from file */ + *ptr; /* Pointer to value */ + int state = 1; /* State of service */ - (void)hslp; /* anti-compiler-warning-code */ /* - * Bail if there was an error + * Try opening the /etc/hostconfig file; if we can't open it, assume that + * the service is enabled/auto. */ - if (errcode != SLP_OK) - return (SLP_TRUE); + if ((fp = cupsFileOpen("/etc/hostconfig", "r")) != NULL) + { + /* + * Read lines from the file until we find the service... + */ - /* - * Parse the attrlist to obtain things needed to build CUPS browse packet - */ + while (cupsFileGets(fp, line, sizeof(line))) + { + if (line[0] == '#' || (ptr = strchr(line, '=')) == NULL) + continue; - memset(p, 0, sizeof(cupsd_printer_t)); + *ptr++ = '\0'; - if (slp_get_attr(attrlist, "(printer-location=", &(p->location))) - return (SLP_FALSE); - if (slp_get_attr(attrlist, "(printer-info=", &(p->info))) - return (SLP_FALSE); - if (slp_get_attr(attrlist, "(printer-make-and-model=", &(p->make_model))) - return (SLP_FALSE); - if (!slp_get_attr(attrlist, "(printer-type=", &tmp)) - p->type = atoi(tmp); - else - p->type = CUPS_PRINTER_REMOTE; + if (!_cups_strcasecmp(line, name)) + { + /* + * Found the service, see if it is set to "-NO-"... + */ + + if (!_cups_strncasecmp(ptr, "-NO-", 4)) + state = 0; + break; + } + } - cupsdClearString(&tmp); + cupsFileClose(fp); + } - return (SLP_TRUE); + return (state); } +#endif /* __APPLE__ */ /* - * 'slp_dereg_printer()' - SLPDereg() the specified printer + * 'update_lpd()' - Update the LPD configuration as needed. */ -static void -slp_dereg_printer(cupsd_printer_t *p) /* I - Printer */ +static void +update_lpd(int onoff) /* - 1 = turn on, 0 = turn off */ { - char srvurl[HTTP_MAX_URI]; /* Printer service URI */ + if (!LPDConfigFile) + return; +#ifdef __APPLE__ + /* + * Allow /etc/hostconfig CUPS_LPD service setting to override cupsd.conf + * setting for backwards-compatibility. + */ - cupsdLogMessage(CUPSD_LOG_DEBUG, "slp_dereg_printer: printer=\"%s\"", p->name); + if (onoff && !get_hostconfig("CUPS_LPD")) + onoff = 0; +#endif /* __APPLE__ */ - if (!(p->type & CUPS_PRINTER_REMOTE)) + if (!strncmp(LPDConfigFile, "xinetd:///", 10)) { /* - * Make the SLP service URL that conforms to the IANA - * 'printer:' template. + * Enable/disable LPD via the xinetd.d config file for cups-lpd... */ - snprintf(srvurl, sizeof(srvurl), SLP_CUPS_SRVTYPE ":%s", p->uri); - - /* - * Deregister the printer... - */ + 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 */ - SLPDereg(BrowseSLPHandle, srvurl, slp_reg_callback, 0); - } -} + snprintf(newfile, sizeof(newfile), "%s.N", LPDConfigFile + 9); -/* - * 'slp_get_attr()' - Get an attribute from an SLP registration. - */ + if ((ofp = cupsFileOpen(LPDConfigFile + 9, "r")) == NULL) + { + cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open \"%s\" - %s", + LPDConfigFile + 9, strerror(errno)); + return; + } -static int /* O - 0 on success */ -slp_get_attr(const char *attrlist, /* I - Attribute list string */ - const char *tag, /* I - Name of attribute */ - char **valbuf) /* O - Value */ -{ - char *ptr1, /* Pointer into string */ - *ptr2; /* ... */ + 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... + */ - cupsdClearString(valbuf); + 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); + } - if ((ptr1 = strstr(attrlist, tag)) != NULL) + cupsFileClose(nfp); + cupsFileClose(ofp); + rename(newfile, LPDConfigFile + 9); + } +#ifdef __APPLE__ + else if (!strncmp(LPDConfigFile, "launchd:///", 11)) { - ptr1 += strlen(tag); - - if ((ptr2 = strchr(ptr1,')')) != NULL) - { - /* - * Copy the value... - */ + /* + * Enable/disable LPD via the launchctl command... + */ - *valbuf = calloc(ptr2 - ptr1 + 1, 1); - strncpy(*valbuf, ptr1, ptr2 - ptr1); + char *argv[5], /* Arguments for command */ + *envp[MAX_ENV]; /* Environment for command */ + int pid; /* Process ID */ - /* - * Dequote the value... - */ - for (ptr1 = *valbuf; *ptr1; ptr1 ++) - if (*ptr1 == '\\' && ptr1[1]) - _cups_strcpy(ptr1, ptr1 + 1); + 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; - return (0); - } + cupsdStartProcess("/bin/launchctl", argv, envp, -1, -1, -1, -1, -1, 1, + NULL, NULL, &pid); } - - return (-1); +#endif /* __APPLE__ */ + else + cupsdLogMessage(CUPSD_LOG_INFO, "Unknown LPDConfigFile scheme!"); } /* - * 'slp_reg_callback()' - Empty SLPRegReport. + * 'update_smb()' - Update the SMB configuration as needed. */ static void -slp_reg_callback(SLPHandle hslp, /* I - SLP handle */ - SLPError errcode, /* I - Error code, if any */ - void *cookie) /* I - App data */ -{ - (void)hslp; - (void)errcode; - (void)cookie; - - return; -} - - -/* - * 'slp_url_callback()' - SLP service url callback - */ - -static SLPBoolean /* O - TRUE = OK, FALSE = error */ -slp_url_callback( - SLPHandle hslp, /* I - SLP handle */ - const char *srvurl, /* I - URL of service */ - unsigned short lifetime, /* I - Life of service */ - SLPError errcode, /* I - Existing error code */ - void *cookie) /* I - Pointer to service list */ +update_smb(int onoff) /* I - 1 = turn on, 0 = turn off */ { - slpsrvurl_t *s, /* New service entry */ - **head; /* Pointer to head of entry */ - - - /* - * Let the compiler know we won't be using these vars... - */ - - (void)hslp; - (void)lifetime; + if (!SMBConfigFile) + return; - /* - * Bail if there was an error - */ + if (!strncmp(SMBConfigFile, "samba:///", 9)) + { + /* + * Enable/disable SMB via the specified smb.conf config file... + */ - if (errcode != SLP_OK) - return (SLP_TRUE); + 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? */ - /* - * Grab the head of the list... - */ - head = (slpsrvurl_t**)cookie; + snprintf(newfile, sizeof(newfile), "%s.N", SMBConfigFile + 8); - /* - * Allocate a *temporary* slpsrvurl_t to hold this entry. - */ + if ((ofp = cupsFileOpen(SMBConfigFile + 8, "r")) == NULL) + { + cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open \"%s\" - %s", + SMBConfigFile + 8, strerror(errno)); + return; + } - if ((s = (slpsrvurl_t *)calloc(1, sizeof(slpsrvurl_t))) == NULL) - return (SLP_FALSE); + if ((nfp = cupsFileOpen(newfile, "w")) == NULL) + { + cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create \"%s\" - %s", + newfile, strerror(errno)); + cupsFileClose(ofp); + return; + } - /* - * Copy the SLP service URL... - */ + /* + * Copy all of the lines from the smb.conf file... + */ - strlcpy(s->url, srvurl, sizeof(s->url)); + in_printers = 0; - /* - * Link the SLP service URL into the head of the list - */ + while (cupsFileGets(ofp, line, sizeof(line))) + { + if (in_printers && strstr(line, "printable =")) + snprintf(line, sizeof(line), " printable = %s", + onoff ? "yes" : "no"); - if (*head) - s->next = *head; + cupsFilePrintf(nfp, "%s\n", line); - *head = s; + if (line[0] == '[') + in_printers = !strcmp(line, "[printers]"); + } - return (SLP_TRUE); + cupsFileClose(nfp); + cupsFileClose(ofp); + rename(newfile, SMBConfigFile + 8); + } + else + cupsdLogMessage(CUPSD_LOG_INFO, "Unknown SMBConfigFile scheme!"); } -#endif /* HAVE_LIBSLP */ - - -/* - * End of "$Id: dirsvc.c 5889 2006-08-24 21:44:35Z mike $". - */