| /* |
| * "$Id: dirsvc.c 7933 2008-09-11 00:44:58Z mike $" |
| * |
| * Directory services routines for the CUPS scheduler. |
| * |
| * Copyright 2007-2011 by Apple Inc. |
| * Copyright 1997-2007 by Easy Software Products, all rights reserved. |
| * |
| * 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/". |
| * |
| * Contents: |
| * |
| * cupsdDeregisterPrinter() - Stop sending broadcast information for a local |
| * printer and remove any pending references to |
| * remote printers. |
| * cupsdLoadRemoteCache() - Load the remote printer cache. |
| * cupsdRegisterPrinter() - Start sending broadcast information for a |
| * printer or update the broadcast contents. |
| * cupsdRestartPolling() - Restart polling servers as needed. |
| * cupsdSaveRemoteCache() - Save the remote printer cache. |
| * cupsdSendBrowseList() - Send new browsing information as necessary. |
| * ldap_rebind_proc() - Callback function for LDAP rebind |
| * ldap_connect() - Start new LDAP connection |
| * ldap_reconnect() - Reconnect to LDAP Server |
| * ldap_disconnect() - Disconnect from LDAP Server |
| * 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. |
| * cupsdUpdateDNSSDName() - Update the computer name we use for |
| * browsing... |
| * cupsdUpdateLDAPBrowse() - Scan for new printers via LDAP... |
| * cupsdUpdateSLPBrowse() - Get browsing information via SLP. |
| * dequote() - Remote quotes from a string. |
| * dnssdAddAlias() - Add a DNS-SD alias name. |
| * dnssdBuildTxtRecord() - Build a TXT record from printer info. |
| * dnssdComparePrinters() - Compare the registered names of two printers. |
| * dnssdDeregisterPrinter() - Stop sending broadcast information for a |
| * printer. |
| * dnssdPackTxtRecord() - Pack an array of key/value pairs into the TXT |
| * record format. |
| * dnssdRegisterCallback() - DNSServiceRegister callback. |
| * dnssdRegisterPrinter() - Start sending broadcast information for a |
| * printer or update the broadcast contents. |
| * dnssdStop() - Stop all DNS-SD registrations. |
| * dnssdUpdate() - Handle DNS-SD queries. |
| * get_auth_info_required() - Get the auth-info-required value to advertise. |
| * get_hostconfig() - Get an /etc/hostconfig service setting. |
| * is_local_queue() - Determine whether the URI points at a local |
| * queue. |
| * 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. |
| * ldap_search_rec() - LDAP Search with reconnect |
| * ldap_freeres() - Free LDAPMessage |
| * ldap_getval_char() - Get first LDAP value and convert to string |
| * send_ldap_ou() - Send LDAP ou registrations. |
| * send_ldap_browse() - Send LDAP printer registrations. |
| * ldap_dereg_printer() - Delete printer from directory |
| * ldap_dereg_ou() - Remove the organizational unit. |
| * 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 |
| * update_cups_browse() - Update the browse lists using the CUPS |
| * protocol. |
| * update_lpd() - Update the LPD configuration as needed. |
| * update_polling() - Read status messages from the poll daemons. |
| * update_smb() - Update the SMB configuration as needed. |
| */ |
| |
| /* |
| * Include necessary headers... |
| */ |
| |
| #include "cupsd.h" |
| #include <grp.h> |
| |
| #ifdef HAVE_DNSSD |
| # include <dns_sd.h> |
| # ifdef __APPLE__ |
| # include <nameser.h> |
| # ifdef HAVE_COREFOUNDATION |
| # include <CoreFoundation/CoreFoundation.h> |
| # endif /* HAVE_COREFOUNDATION */ |
| # ifdef HAVE_SYSTEMCONFIGURATION |
| # include <SystemConfiguration/SystemConfiguration.h> |
| # endif /* HAVE_SYSTEMCONFIGURATION */ |
| # endif /* __APPLE__ */ |
| #endif /* HAVE_DNSSD */ |
| |
| |
| /* |
| * Local functions... |
| */ |
| |
| static char *dequote(char *d, const char *s, int dlen); |
| static char *get_auth_info_required(cupsd_printer_t *p, char *buffer, |
| size_t bufsize); |
| #ifdef __APPLE__ |
| static int get_hostconfig(const char *name); |
| #endif /* __APPLE__ */ |
| 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 LDAP *ldap_connect(void); |
| static LDAP *ldap_reconnect(void); |
| static void ldap_disconnect(LDAP *ld); |
| static int ldap_search_rec(LDAP *ld, char *base, int scope, |
| char *filter, char *attrs[], |
| int attrsonly, LDAPMessage **res); |
| static int ldap_getval_firststring(LDAP *ld, LDAPMessage *entry, |
| char *attr, char *retval, |
| unsigned long maxsize); |
| static void ldap_freeres(LDAPMessage *entry); |
| static void send_ldap_ou(char *ou, char *basedn, char *descstring); |
| static void send_ldap_browse(cupsd_printer_t *p); |
| static void ldap_dereg_printer(cupsd_printer_t *p); |
| static void ldap_dereg_ou(char *ou, char *basedn); |
| # ifdef HAVE_LDAP_REBIND_PROC |
| # if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) |
| static int ldap_rebind_proc(LDAP *RebindLDAPHandle, |
| LDAP_CONST char *refsp, |
| ber_tag_t request, |
| ber_int_t msgid, |
| void *params); |
| # else |
| static int ldap_rebind_proc(LDAP *RebindLDAPHandle, |
| char **dnp, |
| char **passwdp, |
| int *authmethodp, |
| int freeit, |
| void *arg); |
| # endif /* defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) */ |
| # endif /* HAVE_LDAP_REBIND_PROC */ |
| #endif /* HAVE_LDAP */ |
| #ifdef HAVE_LIBSLP |
| static void send_slp_browse(cupsd_printer_t *p); |
| #endif /* HAVE_LIBSLP */ |
| static void update_cups_browse(void); |
| static void update_lpd(int onoff); |
| static void update_polling(void); |
| static void update_smb(int onoff); |
| |
| |
| #ifdef HAVE_DNSSD |
| # ifdef HAVE_COREFOUNDATION |
| static void dnssdAddAlias(const void *key, const void *value, |
| void *context); |
| # endif /* HAVE_COREFOUNDATION */ |
| static char *dnssdBuildTxtRecord(int *txt_len, cupsd_printer_t *p, |
| int for_lpd); |
| static int dnssdComparePrinters(cupsd_printer_t *a, cupsd_printer_t *b); |
| static void dnssdDeregisterPrinter(cupsd_printer_t *p); |
| static char *dnssdPackTxtRecord(int *txt_len, char *keyvalue[][2], |
| int count); |
| static void dnssdRegisterCallback(DNSServiceRef sdRef, |
| DNSServiceFlags flags, |
| DNSServiceErrorType errorCode, |
| const char *name, const char *regtype, |
| const char *domain, void *context); |
| static void dnssdRegisterPrinter(cupsd_printer_t *p); |
| static void dnssdStop(void); |
| static void dnssdUpdate(void); |
| #endif /* HAVE_DNSSD */ |
| |
| #ifdef HAVE_LDAP |
| static const char * const ldap_attrs[] =/* CUPS LDAP attributes */ |
| { |
| "printerDescription", |
| "printerLocation", |
| "printerMakeAndModel", |
| "printerType", |
| "printerURI", |
| NULL |
| }; |
| #endif /* HAVE_LDAP */ |
| |
| #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 |
| */ |
| |
| typedef struct _slpsrvurl_s /**** SLP URL list ****/ |
| { |
| struct _slpsrvurl_s *next; /* Next URL in list */ |
| char url[HTTP_MAX_URI]; |
| /* URL */ |
| } slpsrvurl_t; |
| |
| |
| /* |
| * 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 */ |
| |
| |
| /* |
| * 'cupsdDeregisterPrinter()' - Stop sending broadcast information for a |
| * local printer and remove any pending |
| * references to remote printers. |
| */ |
| |
| void |
| cupsdDeregisterPrinter( |
| cupsd_printer_t *p, /* I - Printer to register */ |
| int removeit) /* I - Printer being permanently removed */ |
| { |
| /* |
| * Only deregister if browsing is enabled and it's a local printer... |
| */ |
| |
| cupsdLogMessage(CUPSD_LOG_DEBUG, |
| "cupsdDeregisterPrinter(p=%p(%s), removeit=%d)", p, p->name, |
| removeit); |
| |
| if (!Browsing || !p->shared || |
| (p->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT | |
| CUPS_PRINTER_SCANNER))) |
| return; |
| |
| /* |
| * Announce the deletion... |
| */ |
| |
| if ((BrowseLocalProtocols & BROWSE_CUPS) && BrowseSocket >= 0) |
| { |
| cups_ptype_t savedtype = p->type; /* Saved printer type */ |
| |
| p->type |= CUPS_PRINTER_DELETE; |
| |
| send_cups_browse(p); |
| |
| p->type = savedtype; |
| } |
| |
| #ifdef HAVE_LIBSLP |
| if (BrowseLocalProtocols & BROWSE_SLP) |
| slp_dereg_printer(p); |
| #endif /* HAVE_LIBSLP */ |
| |
| #ifdef HAVE_LDAP |
| if (BrowseLocalProtocols & BROWSE_LDAP) |
| ldap_dereg_printer(p); |
| #endif /* HAVE_LDAP */ |
| |
| #ifdef HAVE_DNSSD |
| if (removeit && (BrowseLocalProtocols & BROWSE_DNSSD) && DNSSDRef) |
| dnssdDeregisterPrinter(p); |
| #endif /* HAVE_DNSSD */ |
| } |
| |
| |
| /* |
| * 'cupsdLoadRemoteCache()' - Load the remote printer cache. |
| */ |
| |
| void |
| cupsdLoadRemoteCache(void) |
| { |
| int i; /* Looping var */ |
| cups_file_t *fp; /* remote.cache file */ |
| int linenum; /* Current line number */ |
| char line[4096], /* 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 */ |
| |
| |
| /* |
| * Don't load the cache if the remote protocols are disabled... |
| */ |
| |
| if (!Browsing) |
| { |
| cupsdLogMessage(CUPSD_LOG_DEBUG, |
| "cupsdLoadRemoteCache: Not loading remote cache."); |
| return; |
| } |
| |
| /* |
| * Open the remote.cache file... |
| */ |
| |
| snprintf(line, sizeof(line), "%s/remote.cache", CacheDir); |
| if ((fp = cupsdOpenConfFile(line)) == NULL) |
| return; |
| |
| /* |
| * Read printer configurations until we hit EOF... |
| */ |
| |
| linenum = 0; |
| p = NULL; |
| now = time(NULL); |
| |
| while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum)) |
| { |
| /* |
| * Decode the directive... |
| */ |
| |
| if (!strcasecmp(line, "<Printer") || |
| !strcasecmp(line, "<DefaultPrinter")) |
| { |
| /* |
| * <Printer name> or <DefaultPrinter name> |
| */ |
| |
| 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); |
| |
| p->accepting = 1; |
| p->state = IPP_PRINTER_IDLE; |
| p->type |= CUPS_PRINTER_REMOTE | CUPS_PRINTER_DISCOVERED; |
| p->browse_time = now; |
| p->browse_expire = now + BrowseTimeout; |
| |
| /* |
| * Set the default printer as needed... |
| */ |
| |
| if (!strcasecmp(line, "<DefaultPrinter")) |
| DefaultPrinter = p; |
| } |
| else |
| { |
| cupsdLogMessage(CUPSD_LOG_ERROR, |
| "Syntax error on line %d of remote.cache.", linenum); |
| break; |
| } |
| } |
| else if (!strcasecmp(line, "<Class") || |
| !strcasecmp(line, "<DefaultClass")) |
| { |
| /* |
| * <Class name> or <DefaultClass name> |
| */ |
| |
| if (p == NULL && value) |
| { |
| /* |
| * Add the printer and a base file type... |
| */ |
| |
| cupsdLogMessage(CUPSD_LOG_DEBUG, |
| "cupsdLoadRemoteCache: Loading class %s...", value); |
| |
| 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 | CUPS_PRINTER_DISCOVERED; |
| p->browse_time = now; |
| p->browse_expire = now + BrowseTimeout; |
| |
| /* |
| * Set the default printer as needed... |
| */ |
| |
| if (!strcasecmp(line, "<DefaultClass")) |
| DefaultPrinter = p; |
| } |
| else |
| { |
| cupsdLogMessage(CUPSD_LOG_ERROR, |
| "Syntax error on line %d of remote.cache.", linenum); |
| break; |
| } |
| } |
| else if (!strcasecmp(line, "</Printer>") || |
| !strcasecmp(line, "</Class>")) |
| { |
| if (p != NULL) |
| { |
| /* |
| * Close out the current printer... |
| */ |
| |
| cupsdSetPrinterAttrs(p); |
| |
| p = NULL; |
| } |
| else |
| cupsdLogMessage(CUPSD_LOG_ERROR, |
| "Syntax error on line %d of remote.cache.", linenum); |
| } |
| else if (!p) |
| { |
| cupsdLogMessage(CUPSD_LOG_ERROR, |
| "Syntax error on line %d of remote.cache.", linenum); |
| } |
| else if (!strcasecmp(line, "UUID")) |
| { |
| if (value && !strncmp(value, "urn:uuid:", 9)) |
| cupsdSetString(&(p->uuid), value); |
| else |
| cupsdLogMessage(CUPSD_LOG_ERROR, |
| "Bad UUID on line %d of remote.cache.", linenum); |
| } |
| 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)); |
| |
| cupsdSetString(&p->hostname, host); |
| cupsdSetString(&p->uri, value); |
| cupsdSetDeviceURI(p, value); |
| } |
| else |
| cupsdLogMessage(CUPSD_LOG_ERROR, |
| "Syntax error on line %d of remote.cache.", linenum); |
| } |
| else if (!strcasecmp(line, "Option") && value) |
| { |
| /* |
| * Option name value |
| */ |
| |
| for (valueptr = value; *valueptr && !isspace(*valueptr & 255); valueptr ++); |
| |
| if (!*valueptr) |
| cupsdLogMessage(CUPSD_LOG_ERROR, |
| "Syntax error on line %d of remote.cache.", linenum); |
| else |
| { |
| for (; *valueptr && isspace(*valueptr & 255); *valueptr++ = '\0'); |
| |
| p->num_options = cupsAddOption(value, valueptr, p->num_options, |
| &(p->options)); |
| } |
| } |
| else if (!strcasecmp(line, "Reason")) |
| { |
| if (value) |
| { |
| for (i = 0 ; i < p->num_reasons; i ++) |
| if (!strcmp(value, p->reasons[i])) |
| break; |
| |
| if (i >= p->num_reasons && |
| p->num_reasons < (int)(sizeof(p->reasons) / sizeof(p->reasons[0]))) |
| { |
| p->reasons[p->num_reasons] = _cupsStrAlloc(value); |
| p->num_reasons ++; |
| } |
| } |
| else |
| cupsdLogMessage(CUPSD_LOG_ERROR, |
| "Syntax error on line %d of remote.cache.", linenum); |
| } |
| 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; |
| cupsdSetPrinterReasons(p, "+paused"); |
| } |
| else |
| cupsdLogMessage(CUPSD_LOG_ERROR, |
| "Syntax error on line %d of remote.cache.", linenum); |
| } |
| else if (!strcasecmp(line, "StateMessage")) |
| { |
| /* |
| * Set the initial queue state message... |
| */ |
| |
| if (value) |
| strlcpy(p->state_message, value, sizeof(p->state_message)); |
| } |
| else if (!strcasecmp(line, "Accepting")) |
| { |
| /* |
| * Set the initial accepting state... |
| */ |
| |
| 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); |
| } |
| 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); |
| } |
| else if (!strcasecmp(line, "BrowseTime")) |
| { |
| if (value) |
| { |
| time_t t = atoi(value); |
| |
| if (t > p->browse_expire) |
| p->browse_expire = t; |
| } |
| else |
| cupsdLogMessage(CUPSD_LOG_ERROR, |
| "Syntax error on line %d of remote.cache.", linenum); |
| } |
| else if (!strcasecmp(line, "JobSheets")) |
| { |
| /* |
| * Set the initial job sheets... |
| */ |
| |
| if (value) |
| { |
| for (valueptr = value; *valueptr && !isspace(*valueptr & 255); valueptr ++); |
| |
| if (*valueptr) |
| *valueptr++ = '\0'; |
| |
| cupsdSetString(&p->job_sheets[0], value); |
| |
| while (isspace(*valueptr & 255)) |
| valueptr ++; |
| |
| if (*valueptr) |
| { |
| for (value = valueptr; *valueptr && !isspace(*valueptr & 255); valueptr ++); |
| |
| if (*valueptr) |
| *valueptr = '\0'; |
| |
| cupsdSetString(&p->job_sheets[1], value); |
| } |
| } |
| else |
| cupsdLogMessage(CUPSD_LOG_ERROR, |
| "Syntax error on line %d of remote.cache.", linenum); |
| } |
| else if (!strcasecmp(line, "AllowUser")) |
| { |
| if (value) |
| { |
| p->deny_users = 0; |
| cupsdAddString(&(p->users), value); |
| } |
| else |
| cupsdLogMessage(CUPSD_LOG_ERROR, |
| "Syntax error on line %d of remote.cache.", linenum); |
| } |
| else if (!strcasecmp(line, "DenyUser")) |
| { |
| if (value) |
| { |
| p->deny_users = 1; |
| cupsdAddString(&(p->users), value); |
| } |
| else |
| cupsdLogMessage(CUPSD_LOG_ERROR, |
| "Syntax error on line %d of remote.cache.", linenum); |
| } |
| else |
| { |
| /* |
| * Something else we don't understand... |
| */ |
| |
| cupsdLogMessage(CUPSD_LOG_ERROR, |
| "Unknown configuration directive %s on line %d of remote.cache.", |
| line, linenum); |
| } |
| } |
| |
| cupsFileClose(fp); |
| |
| /* |
| * Do auto-classing if needed... |
| */ |
| |
| process_implicit_classes(); |
| } |
| |
| |
| /* |
| * 'cupsdRegisterPrinter()' - Start sending broadcast information for a |
| * printer or update the broadcast contents. |
| */ |
| |
| void |
| cupsdRegisterPrinter(cupsd_printer_t *p)/* I - Printer */ |
| { |
| cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdRegisterPrinter(p=%p(%s))", p, |
| p->name); |
| |
| if (!Browsing || !BrowseLocalProtocols || |
| (p->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT | |
| CUPS_PRINTER_SCANNER))) |
| return; |
| |
| #ifdef HAVE_LIBSLP |
| /* if (BrowseLocalProtocols & BROWSE_SLP) |
| slpRegisterPrinter(p); */ |
| #endif /* HAVE_LIBSLP */ |
| |
| #ifdef HAVE_DNSSD |
| if ((BrowseLocalProtocols & BROWSE_DNSSD) && DNSSDRef) |
| dnssdRegisterPrinter(p); |
| #endif /* HAVE_DNSSD */ |
| } |
| |
| |
| /* |
| * 'cupsdRestartPolling()' - Restart polling servers as needed. |
| */ |
| |
| void |
| cupsdRestartPolling(void) |
| { |
| int i; /* Looping var */ |
| cupsd_dirsvc_poll_t *pollp; /* Current polling server */ |
| |
| |
| for (i = 0, pollp = Polled; i < NumPolled; i ++, pollp ++) |
| if (pollp->pid) |
| kill(pollp->pid, SIGHUP); |
| } |
| |
| |
| /* |
| * 'cupsdSaveRemoteCache()' - Save the remote printer cache. |
| */ |
| |
| void |
| cupsdSaveRemoteCache(void) |
| { |
| int i; /* Looping var */ |
| cups_file_t *fp; /* remote.cache file */ |
| char filename[1024], /* remote.cache filename */ |
| temp[1024], /* Temporary string */ |
| value[2048], /* Value string */ |
| *name; /* Current user name */ |
| 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(filename, sizeof(filename), "%s/remote.cache", CacheDir); |
| |
| if ((fp = cupsdCreateConfFile(filename, ConfigFilePerm)) == NULL) |
| return; |
| |
| cupsdLogMessage(CUPSD_LOG_DEBUG, "Saving remote.cache..."); |
| |
| /* |
| * Write a small header to the file... |
| */ |
| |
| 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); |
| |
| /* |
| * Write each local printer known to the system... |
| */ |
| |
| for (printer = (cupsd_printer_t *)cupsArrayFirst(Printers); |
| printer; |
| printer = (cupsd_printer_t *)cupsArrayNext(Printers)) |
| { |
| /* |
| * Skip local destinations... |
| */ |
| |
| if (!(printer->type & CUPS_PRINTER_DISCOVERED)) |
| continue; |
| |
| /* |
| * Write printers as needed... |
| */ |
| |
| if (printer == DefaultPrinter) |
| cupsFilePuts(fp, "<Default"); |
| else |
| cupsFilePutChar(fp, '<'); |
| |
| if (printer->type & CUPS_PRINTER_CLASS) |
| cupsFilePrintf(fp, "Class %s>\n", printer->name); |
| else |
| cupsFilePrintf(fp, "Printer %s>\n", printer->name); |
| |
| cupsFilePrintf(fp, "BrowseTime %d\n", (int)printer->browse_expire); |
| |
| cupsFilePrintf(fp, "UUID %s\n", printer->uuid); |
| |
| if (printer->info) |
| cupsFilePutConf(fp, "Info", printer->info); |
| |
| if (printer->location) |
| cupsFilePutConf(fp, "Location", printer->location); |
| |
| if (printer->make_model) |
| cupsFilePutConf(fp, "MakeModel", printer->make_model); |
| |
| cupsFilePutConf(fp, "DeviceURI", printer->device_uri); |
| |
| if (printer->state == IPP_PRINTER_STOPPED) |
| cupsFilePuts(fp, "State Stopped\n"); |
| else |
| cupsFilePuts(fp, "State Idle\n"); |
| |
| for (i = 0; i < printer->num_reasons; i ++) |
| cupsFilePutConf(fp, "Reason", printer->reasons[i]); |
| |
| cupsFilePrintf(fp, "Type %d\n", printer->type); |
| |
| if (printer->accepting) |
| cupsFilePuts(fp, "Accepting Yes\n"); |
| else |
| cupsFilePuts(fp, "Accepting No\n"); |
| |
| snprintf(value, sizeof(value), "%s %s", printer->job_sheets[0], |
| printer->job_sheets[1]); |
| cupsFilePutConf(fp, "JobSheets", value); |
| |
| for (name = (char *)cupsArrayFirst(printer->users); |
| name; |
| name = (char *)cupsArrayNext(printer->users)) |
| cupsFilePutConf(fp, printer->deny_users ? "DenyUser" : "AllowUser", name); |
| |
| for (i = printer->num_options, option = printer->options; |
| i > 0; |
| i --, option ++) |
| { |
| snprintf(value, sizeof(value), "%s %s", option->name, option->value); |
| cupsFilePutConf(fp, "Option", value); |
| } |
| |
| if (printer->type & CUPS_PRINTER_CLASS) |
| cupsFilePuts(fp, "</Class>\n"); |
| else |
| cupsFilePuts(fp, "</Printer>\n"); |
| } |
| |
| cupsdCloseCreatedConfFile(fp, filename); |
| } |
| |
| |
| /* |
| * 'cupsdSendBrowseList()' - Send new browsing information as necessary. |
| */ |
| |
| 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 (!Browsing || !Printers) |
| return; |
| |
| /* |
| * Compute the update and timeout times... |
| */ |
| |
| to = time(NULL); |
| ut = to - BrowseInterval; |
| |
| /* |
| * Figure out how many printers need an update... |
| */ |
| |
| if (BrowseInterval > 0 && BrowseLocalProtocols) |
| { |
| 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; |
| |
| 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 | |
| CUPS_PRINTER_SCANNER)) && |
| p->shared && p->browse_time < ut) |
| count ++; |
| |
| /* |
| * Loop through all of the printers and send local updates as needed... |
| */ |
| |
| if (BrowseNext) |
| p = (cupsd_printer_t *)cupsArrayFind(Printers, BrowseNext); |
| else |
| p = (cupsd_printer_t *)cupsArrayFirst(Printers); |
| |
| for (; |
| count > 0; |
| p = (cupsd_printer_t *)cupsArrayNext(Printers)) |
| { |
| /* |
| * Check for wraparound... |
| */ |
| |
| if (!p) |
| p = (cupsd_printer_t *)cupsArrayFirst(Printers); |
| |
| if (!p) |
| break; |
| else if ((p->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT | |
| CUPS_PRINTER_SCANNER)) || |
| !p->shared) |
| continue; |
| else if (p->browse_time < ut) |
| { |
| /* |
| * Need to send an update... |
| */ |
| |
| count --; |
| |
| p->browse_time = time(NULL); |
| |
| if ((BrowseLocalProtocols & BROWSE_CUPS) && BrowseSocket >= 0) |
| send_cups_browse(p); |
| |
| #ifdef HAVE_LIBSLP |
| if (BrowseLocalProtocols & BROWSE_SLP) |
| send_slp_browse(p); |
| #endif /* HAVE_LIBSLP */ |
| |
| #ifdef HAVE_LDAP |
| if (BrowseLocalProtocols & BROWSE_LDAP) |
| send_ldap_browse(p); |
| #endif /* HAVE_LDAP */ |
| } |
| } |
| |
| /* |
| * Save where we left off so that all printers get updated... |
| */ |
| |
| BrowseNext = p; |
| } |
| |
| /* |
| * Loop through all of the printers and timeout old printers as needed... |
| */ |
| |
| for (p = (cupsd_printer_t *)cupsArrayFirst(Printers); |
| p; |
| p = (cupsd_printer_t *)cupsArrayNext(Printers)) |
| { |
| /* |
| * If this is a remote queue, see if it needs to be timed out... |
| */ |
| |
| if ((p->type & CUPS_PRINTER_DISCOVERED) && |
| !(p->type & CUPS_PRINTER_IMPLICIT) && |
| 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); |
| |
| cupsdLogMessage(CUPSD_LOG_DEBUG, |
| "Remote destination \"%s\" has timed out; " |
| "deleting it...", |
| p->name); |
| |
| cupsArraySave(Printers); |
| cupsdDeletePrinter(p, 1); |
| cupsArrayRestore(Printers); |
| cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP | CUPSD_DIRTY_REMOTE); |
| } |
| } |
| } |
| |
| |
| #ifdef HAVE_LDAP_REBIND_PROC |
| # if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) |
| /* |
| * 'ldap_rebind_proc()' - Callback function for LDAP rebind |
| */ |
| |
| static int /* O - Result code */ |
| ldap_rebind_proc( |
| LDAP *RebindLDAPHandle, /* I - LDAP handle */ |
| LDAP_CONST char *refsp, /* I - ??? */ |
| ber_tag_t request, /* I - ??? */ |
| ber_int_t msgid, /* I - ??? */ |
| void *params) /* I - ??? */ |
| { |
| int rc; /* Result code */ |
| # if LDAP_API_VERSION > 3000 |
| struct berval bval; /* Bind value */ |
| # endif /* LDAP_API_VERSION > 3000 */ |
| |
| |
| (void)request; |
| (void)msgid; |
| (void)params; |
| |
| /* |
| * Bind to new LDAP server... |
| */ |
| |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, "ldap_rebind_proc: Rebind to %s", refsp); |
| |
| # if LDAP_API_VERSION > 3000 |
| bval.bv_val = BrowseLDAPPassword; |
| bval.bv_len = (BrowseLDAPPassword == NULL) ? 0 : strlen(BrowseLDAPPassword); |
| |
| rc = ldap_sasl_bind_s(RebindLDAPHandle, BrowseLDAPBindDN, LDAP_SASL_SIMPLE, |
| &bval, NULL, NULL, NULL); |
| # else |
| rc = ldap_bind_s(RebindLDAPHandle, BrowseLDAPBindDN, BrowseLDAPPassword, |
| LDAP_AUTH_SIMPLE); |
| # endif /* LDAP_API_VERSION > 3000 */ |
| |
| return (rc); |
| } |
| |
| |
| # else /* defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) */ |
| /* |
| * 'ldap_rebind_proc()' - Callback function for LDAP rebind |
| */ |
| |
| static int /* O - Result code */ |
| ldap_rebind_proc( |
| LDAP *RebindLDAPHandle, /* I - LDAP handle */ |
| char **dnp, /* I - ??? */ |
| char **passwdp, /* I - ??? */ |
| int *authmethodp, /* I - ??? */ |
| int freeit, /* I - ??? */ |
| void *arg) /* I - ??? */ |
| { |
| switch (freeit) |
| { |
| case 1: |
| /* |
| * Free current values... |
| */ |
| |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, "ldap_rebind_proc: Free values..."); |
| |
| if (dnp && *dnp) |
| free(*dnp); |
| |
| if (passwdp && *passwdp) |
| free(*passwdp); |
| break; |
| |
| case 0: |
| /* |
| * Return credentials for LDAP referal... |
| */ |
| |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, |
| "ldap_rebind_proc: Return necessary values..."); |
| |
| *dnp = strdup(BrowseLDAPBindDN); |
| *passwdp = strdup(BrowseLDAPPassword); |
| *authmethodp = LDAP_AUTH_SIMPLE; |
| break; |
| |
| default: |
| /* |
| * Should never happen... |
| */ |
| |
| cupsdLogMessage(CUPSD_LOG_ERROR, |
| "LDAP rebind has been called with wrong freeit value!"); |
| break; |
| } |
| |
| return (LDAP_SUCCESS); |
| } |
| # endif /* defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) */ |
| #endif /* HAVE_LDAP_REBIND_PROC */ |
| |
| |
| #ifdef HAVE_LDAP |
| /* |
| * 'ldap_connect()' - Start new LDAP connection |
| */ |
| |
| static LDAP * /* O - LDAP handle */ |
| ldap_connect(void) |
| { |
| int rc; /* LDAP API status */ |
| int version = 3; /* LDAP version */ |
| struct berval bv = {0, ""}; /* SASL bind value */ |
| LDAP *TempBrowseLDAPHandle=NULL; |
| /* Temporary LDAP Handle */ |
| # if defined(HAVE_LDAP_SSL) && defined (HAVE_MOZILLA_LDAP) |
| int ldap_ssl = 0; /* LDAP SSL indicator */ |
| int ssl_err = 0; /* LDAP SSL error value */ |
| # endif /* defined(HAVE_LDAP_SSL) && defined (HAVE_MOZILLA_LDAP) */ |
| |
| |
| # ifdef HAVE_OPENLDAP |
| # ifdef HAVE_LDAP_SSL |
| /* |
| * Set the certificate file to use for encrypted LDAP sessions... |
| */ |
| |
| if (BrowseLDAPCACertFile) |
| { |
| cupsdLogMessage(CUPSD_LOG_DEBUG, |
| "ldap_connect: Setting CA certificate file \"%s\"", |
| BrowseLDAPCACertFile); |
| |
| if ((rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE, |
| (void *)BrowseLDAPCACertFile)) != LDAP_SUCCESS) |
| cupsdLogMessage(CUPSD_LOG_ERROR, |
| "Unable to set CA certificate file for LDAP " |
| "connections: %d - %s", rc, ldap_err2string(rc)); |
| } |
| # endif /* HAVE_LDAP_SSL */ |
| |
| /* |
| * Initialize OPENLDAP connection... |
| * LDAP stuff currently only supports ldapi EXTERNAL SASL binds... |
| */ |
| |
| if (!BrowseLDAPServer || !strcasecmp(BrowseLDAPServer, "localhost")) |
| rc = ldap_initialize(&TempBrowseLDAPHandle, "ldapi:///"); |
| else |
| rc = ldap_initialize(&TempBrowseLDAPHandle, BrowseLDAPServer); |
| |
| # else /* HAVE_OPENLDAP */ |
| |
| int ldap_port = 0; /* LDAP port */ |
| char ldap_protocol[11], /* LDAP protocol */ |
| ldap_host[255]; /* LDAP host */ |
| |
| /* |
| * Split LDAP URI into its components... |
| */ |
| |
| if (!BrowseLDAPServer) |
| { |
| cupsdLogMessage(CUPSD_LOG_ERROR, "BrowseLDAPServer not configured!"); |
| cupsdLogMessage(CUPSD_LOG_ERROR, "Disabling LDAP browsing!"); |
| BrowseLocalProtocols &= ~BROWSE_LDAP; |
| BrowseRemoteProtocols &= ~BROWSE_LDAP; |
| return (NULL); |
| } |
| |
| sscanf(BrowseLDAPServer, "%10[^:]://%254[^:/]:%d", ldap_protocol, ldap_host, |
| &ldap_port); |
| |
| if (!strcmp(ldap_protocol, "ldap")) |
| ldap_ssl = 0; |
| else if (!strcmp(ldap_protocol, "ldaps")) |
| ldap_ssl = 1; |
| else |
| { |
| cupsdLogMessage(CUPSD_LOG_ERROR, "Unrecognized LDAP protocol (%s)!", |
| ldap_protocol); |
| cupsdLogMessage(CUPSD_LOG_ERROR, "Disabling LDAP browsing!"); |
| BrowseLocalProtocols &= ~BROWSE_LDAP; |
| BrowseRemoteProtocols &= ~BROWSE_LDAP; |
| return (NULL); |
| } |
| |
| if (ldap_port == 0) |
| { |
| if (ldap_ssl) |
| ldap_port = LDAPS_PORT; |
| else |
| ldap_port = LDAP_PORT; |
| } |
| |
| cupsdLogMessage(CUPSD_LOG_DEBUG, "ldap_connect: PROT:%s HOST:%s PORT:%d", |
| ldap_protocol, ldap_host, ldap_port); |
| |
| /* |
| * Initialize LDAP connection... |
| */ |
| |
| if (!ldap_ssl) |
| { |
| if ((TempBrowseLDAPHandle = ldap_init(ldap_host, ldap_port)) == NULL) |
| rc = LDAP_OPERATIONS_ERROR; |
| else |
| rc = LDAP_SUCCESS; |
| |
| # ifdef HAVE_LDAP_SSL |
| } |
| else |
| { |
| /* |
| * Initialize SSL LDAP connection... |
| */ |
| |
| if (BrowseLDAPCACertFile) |
| { |
| rc = ldapssl_client_init(BrowseLDAPCACertFile, (void *)NULL); |
| if (rc != LDAP_SUCCESS) |
| { |
| cupsdLogMessage(CUPSD_LOG_ERROR, |
| "Failed to initialize LDAP SSL client!"); |
| rc = LDAP_OPERATIONS_ERROR; |
| } |
| else |
| { |
| if ((TempBrowseLDAPHandle = ldapssl_init(ldap_host, ldap_port, |
| 1)) == NULL) |
| rc = LDAP_OPERATIONS_ERROR; |
| else |
| rc = LDAP_SUCCESS; |
| } |
| } |
| else |
| { |
| cupsdLogMessage(CUPSD_LOG_ERROR, |
| "LDAP SSL certificate file/database not configured!"); |
| rc = LDAP_OPERATIONS_ERROR; |
| } |
| |
| # else /* HAVE_LDAP_SSL */ |
| |
| /* |
| * Return error, because client libraries doesn't support SSL |
| */ |
| |
| cupsdLogMessage(CUPSD_LOG_ERROR, |
| "LDAP client libraries do not support SSL"); |
| rc = LDAP_OPERATIONS_ERROR; |
| |
| # endif /* HAVE_LDAP_SSL */ |
| } |
| # endif /* HAVE_OPENLDAP */ |
| |
| /* |
| * Check return code from LDAP initialize... |
| */ |
| |
| if (rc != LDAP_SUCCESS) |
| { |
| cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to initialize LDAP!"); |
| |
| if (rc == LDAP_SERVER_DOWN || rc == LDAP_CONNECT_ERROR) |
| cupsdLogMessage(CUPSD_LOG_ERROR, "Temporarily disabling LDAP browsing..."); |
| else |
| { |
| cupsdLogMessage(CUPSD_LOG_ERROR, "Disabling LDAP browsing!"); |
| |
| BrowseLocalProtocols &= ~BROWSE_LDAP; |
| BrowseRemoteProtocols &= ~BROWSE_LDAP; |
| } |
| |
| ldap_disconnect(TempBrowseLDAPHandle); |
| |
| return (NULL); |
| } |
| |
| /* |
| * Upgrade LDAP version... |
| */ |
| |
| if (ldap_set_option(TempBrowseLDAPHandle, LDAP_OPT_PROTOCOL_VERSION, |
| (const void *)&version) != LDAP_SUCCESS) |
| { |
| cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to set LDAP protocol version %d!", |
| version); |
| cupsdLogMessage(CUPSD_LOG_ERROR, "Disabling LDAP browsing!"); |
| |
| BrowseLocalProtocols &= ~BROWSE_LDAP; |
| BrowseRemoteProtocols &= ~BROWSE_LDAP; |
| ldap_disconnect(TempBrowseLDAPHandle); |
| |
| return (NULL); |
| } |
| |
| /* |
| * Register LDAP rebind procedure... |
| */ |
| |
| # ifdef HAVE_LDAP_REBIND_PROC |
| # if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) |
| |
| rc = ldap_set_rebind_proc(TempBrowseLDAPHandle, &ldap_rebind_proc, |
| (void *)NULL); |
| if (rc != LDAP_SUCCESS) |
| cupsdLogMessage(CUPSD_LOG_ERROR, |
| "Setting LDAP rebind function failed with status %d: %s", |
| rc, ldap_err2string(rc)); |
| |
| # else |
| |
| ldap_set_rebind_proc(TempBrowseLDAPHandle, &ldap_rebind_proc, (void *)NULL); |
| |
| # endif /* defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) */ |
| # endif /* HAVE_LDAP_REBIND_PROC */ |
| |
| /* |
| * Start LDAP bind... |
| */ |
| |
| # if LDAP_API_VERSION > 3000 |
| struct berval bval; |
| bval.bv_val = BrowseLDAPPassword; |
| bval.bv_len = (BrowseLDAPPassword == NULL) ? 0 : strlen(BrowseLDAPPassword); |
| |
| if (!BrowseLDAPServer || !strcasecmp(BrowseLDAPServer, "localhost")) |
| rc = ldap_sasl_bind_s(TempBrowseLDAPHandle, NULL, "EXTERNAL", &bv, NULL, |
| NULL, NULL); |
| else |
| rc = ldap_sasl_bind_s(TempBrowseLDAPHandle, BrowseLDAPBindDN, LDAP_SASL_SIMPLE, &bval, NULL, NULL, NULL); |
| |
| # else |
| rc = ldap_bind_s(TempBrowseLDAPHandle, BrowseLDAPBindDN, |
| BrowseLDAPPassword, LDAP_AUTH_SIMPLE); |
| # endif /* LDAP_API_VERSION > 3000 */ |
| |
| if (rc != LDAP_SUCCESS) |
| { |
| cupsdLogMessage(CUPSD_LOG_ERROR, "LDAP bind failed with error %d: %s", |
| rc, ldap_err2string(rc)); |
| |
| # if defined(HAVE_LDAP_SSL) && defined (HAVE_MOZILLA_LDAP) |
| if (ldap_ssl && (rc == LDAP_SERVER_DOWN || rc == LDAP_CONNECT_ERROR)) |
| { |
| ssl_err = PORT_GetError(); |
| if (ssl_err != 0) |
| cupsdLogMessage(CUPSD_LOG_ERROR, "LDAP SSL error %d: %s", ssl_err, |
| ldapssl_err2string(ssl_err)); |
| } |
| # endif /* defined(HAVE_LDAP_SSL) && defined (HAVE_MOZILLA_LDAP) */ |
| |
| ldap_disconnect(TempBrowseLDAPHandle); |
| |
| return (NULL); |
| } |
| |
| cupsdLogMessage(CUPSD_LOG_INFO, "LDAP connection established"); |
| |
| return (TempBrowseLDAPHandle); |
| } |
| |
| |
| /* |
| * 'ldap_reconnect()' - Reconnect to LDAP Server |
| */ |
| |
| static LDAP * /* O - New LDAP handle */ |
| ldap_reconnect(void) |
| { |
| LDAP *TempBrowseLDAPHandle = NULL; /* Temp Handle to LDAP server */ |
| |
| |
| /* |
| * Get a new LDAP Handle and replace the global Handle |
| * if the new connection was successful. |
| */ |
| |
| cupsdLogMessage(CUPSD_LOG_INFO, "Try LDAP reconnect..."); |
| |
| TempBrowseLDAPHandle = ldap_connect(); |
| |
| if (TempBrowseLDAPHandle != NULL) |
| { |
| if (BrowseLDAPHandle != NULL) |
| ldap_disconnect(BrowseLDAPHandle); |
| |
| BrowseLDAPHandle = TempBrowseLDAPHandle; |
| } |
| |
| return (BrowseLDAPHandle); |
| } |
| |
| |
| /* |
| * 'ldap_disconnect()' - Disconnect from LDAP Server |
| */ |
| |
| static void |
| ldap_disconnect(LDAP *ld) /* I - LDAP handle */ |
| { |
| int rc; /* Return code */ |
| |
| |
| /* |
| * Close LDAP handle... |
| */ |
| |
| # if defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 |
| rc = ldap_unbind_ext_s(ld, NULL, NULL); |
| # else |
| rc = ldap_unbind_s(ld); |
| # endif /* defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 */ |
| |
| if (rc != LDAP_SUCCESS) |
| cupsdLogMessage(CUPSD_LOG_ERROR, |
| "Unbind from LDAP server failed with status %d: %s", |
| rc, ldap_err2string(rc)); |
| } |
| #endif /* HAVE_LDAP */ |
| |
| |
| /* |
| * 'cupsdStartBrowsing()' - Start sending and receiving broadcast information. |
| */ |
| |
| void |
| cupsdStartBrowsing(void) |
| { |
| int val; /* Socket option value */ |
| struct sockaddr_in addr; /* Broadcast address */ |
| cupsd_printer_t *p; /* Current printer */ |
| |
| |
| BrowseNext = NULL; |
| |
| if (!Browsing || !(BrowseLocalProtocols | BrowseRemoteProtocols)) |
| return; |
| |
| if ((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_CUPS) |
| { |
| if (BrowseSocket < 0) |
| { |
| /* |
| * Create the broadcast socket... |
| */ |
| |
| if ((BrowseSocket = socket(AF_INET, SOCK_DGRAM, 0)) < 0) |
| { |
| cupsdLogMessage(CUPSD_LOG_ERROR, |
| "Unable to create broadcast socket - %s.", |
| strerror(errno)); |
| BrowseLocalProtocols &= ~BROWSE_CUPS; |
| BrowseRemoteProtocols &= ~BROWSE_CUPS; |
| |
| if (FatalErrors & CUPSD_FATAL_BROWSE) |
| cupsdEndProcess(getpid(), 0); |
| } |
| } |
| |
| if (BrowseSocket >= 0) |
| { |
| /* |
| * Bind the socket to browse port... |
| */ |
| |
| memset(&addr, 0, sizeof(addr)); |
| addr.sin_addr.s_addr = htonl(INADDR_ANY); |
| addr.sin_family = AF_INET; |
| addr.sin_port = htons(BrowsePort); |
| |
| if (bind(BrowseSocket, (struct sockaddr *)&addr, sizeof(addr))) |
| { |
| cupsdLogMessage(CUPSD_LOG_ERROR, |
| "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; |
| |
| if (FatalErrors & CUPSD_FATAL_BROWSE) |
| cupsdEndProcess(getpid(), 0); |
| } |
| } |
| |
| if (BrowseSocket >= 0) |
| { |
| /* |
| * Set the "broadcast" flag... |
| */ |
| |
| val = 1; |
| if (setsockopt(BrowseSocket, SOL_SOCKET, SO_BROADCAST, &val, sizeof(val))) |
| { |
| cupsdLogMessage(CUPSD_LOG_ERROR, "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; |
| |
| if (FatalErrors & CUPSD_FATAL_BROWSE) |
| cupsdEndProcess(getpid(), 0); |
| } |
| } |
| |
| if (BrowseSocket >= 0) |
| { |
| /* |
| * Close the socket on exec... |
| */ |
| |
| fcntl(BrowseSocket, F_SETFD, fcntl(BrowseSocket, F_GETFD) | FD_CLOEXEC); |
| |
| /* |
| * Finally, add the socket to the input selection set as needed... |
| */ |
| |
| if (BrowseRemoteProtocols & BROWSE_CUPS) |
| { |
| /* |
| * We only listen if we want remote printers... |
| */ |
| |
| cupsdAddSelect(BrowseSocket, (cupsd_selfunc_t)update_cups_browse, |
| NULL, NULL); |
| } |
| } |
| } |
| else |
| BrowseSocket = -1; |
| |
| #ifdef HAVE_DNSSD |
| if ((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_DNSSD) |
| { |
| DNSServiceErrorType error; /* Error from service creation */ |
| cupsd_listener_t *lis; /* Current listening socket */ |
| |
| |
| /* |
| * First create a "master" connection for all registrations... |
| */ |
| |
| if ((error = DNSServiceCreateConnection(&DNSSDRef)) |
| != kDNSServiceErr_NoError) |
| { |
| cupsdLogMessage(CUPSD_LOG_ERROR, |
| "Unable to create master DNS-SD reference: %d", error); |
| |
| if (FatalErrors & CUPSD_FATAL_BROWSE) |
| cupsdEndProcess(getpid(), 0); |
| } |
| else |
| { |
| /* |
| * Add the master connection to the select list... |
| */ |
| |
| int fd = DNSServiceRefSockFD(DNSSDRef); |
| |
| fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); |
| |
| cupsdAddSelect(fd, (cupsd_selfunc_t)dnssdUpdate, NULL, NULL); |
| |
| /* |
| * Then 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... |
| */ |
| |
| DNSSDPort = 0; |
| |
| for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners); |
| lis; |
| lis = (cupsd_listener_t *)cupsArrayNext(Listeners)) |
| { |
| if (httpAddrLocalhost(&(lis->address))) |
| continue; |
| |
| DNSSDPort = _httpAddrPort(&(lis->address)); |
| break; |
| } |
| |
| /* |
| * Create an array to track the printers we share... |
| */ |
| |
| if (BrowseRemoteProtocols & BROWSE_DNSSD) |
| DNSSDPrinters = cupsArrayNew((cups_array_func_t)dnssdComparePrinters, |
| NULL); |
| |
| /* |
| * Set the computer name and register the web interface... |
| */ |
| |
| cupsdUpdateDNSSDName(); |
| } |
| } |
| #endif /* HAVE_DNSSD */ |
| |
| #ifdef HAVE_LIBSLP |
| if ((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_SLP) |
| { |
| /* |
| * Open SLP handle... |
| */ |
| |
| 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; |
| BrowseSLPHandle = NULL; |
| |
| if (FatalErrors & CUPSD_FATAL_BROWSE) |
| cupsdEndProcess(getpid(), 0); |
| } |
| |
| BrowseSLPRefresh = 0; |
| } |
| else |
| BrowseSLPHandle = NULL; |
| #endif /* HAVE_LIBSLP */ |
| |
| #ifdef HAVE_LDAP |
| 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; |
| |
| if (FatalErrors & CUPSD_FATAL_BROWSE) |
| cupsdEndProcess(getpid(), 0); |
| } |
| else |
| { |
| /* |
| * Open LDAP handle... |
| */ |
| |
| if ((BrowseLDAPHandle = ldap_connect()) == NULL && |
| (FatalErrors & CUPSD_FATAL_BROWSE)) |
| cupsdEndProcess(getpid(), 0); |
| } |
| |
| BrowseLDAPRefresh = 0; |
| } |
| #endif /* HAVE_LDAP */ |
| |
| /* |
| * Enable LPD and SMB printer sharing as needed through external programs... |
| */ |
| |
| if (BrowseLocalProtocols & BROWSE_LPD) |
| update_lpd(1); |
| |
| if (BrowseLocalProtocols & BROWSE_SMB) |
| update_smb(1); |
| |
| /* |
| * Register the individual printers |
| */ |
| |
| for (p = (cupsd_printer_t *)cupsArrayFirst(Printers); |
| p; |
| p = (cupsd_printer_t *)cupsArrayNext(Printers)) |
| if (!(p->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT | |
| CUPS_PRINTER_SCANNER))) |
| cupsdRegisterPrinter(p); |
| } |
| |
| |
| /* |
| * 'cupsdStartPolling()' - Start polling servers as needed. |
| */ |
| |
| void |
| cupsdStartPolling(void) |
| { |
| int i; /* Looping var */ |
| cupsd_dirsvc_poll_t *pollp; /* Current polling server */ |
| char polld[1024]; /* Poll daemon path */ |
| char sport[255]; /* Server port */ |
| char bport[255]; /* Browser port */ |
| char interval[255]; /* Poll interval */ |
| int statusfds[2]; /* Status pipe */ |
| char *argv[6]; /* Arguments */ |
| char *envp[100]; /* Environment */ |
| |
| |
| /* |
| * Don't do anything if we aren't polling... |
| */ |
| |
| if (NumPolled == 0 || BrowseSocket < 0) |
| { |
| PollPipe = -1; |
| PollStatusBuffer = NULL; |
| return; |
| } |
| |
| /* |
| * Setup string arguments for polld, port and interval options. |
| */ |
| |
| snprintf(polld, sizeof(polld), "%s/daemon/cups-polld", ServerBin); |
| |
| sprintf(bport, "%d", BrowsePort); |
| |
| 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; |
| |
| cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0]))); |
| |
| /* |
| * Create a pipe that receives the status messages from each |
| * polling daemon... |
| */ |
| |
| if (cupsdOpenPipe(statusfds)) |
| { |
| cupsdLogMessage(CUPSD_LOG_ERROR, |
| "Unable to create polling status pipes - %s.", |
| strerror(errno)); |
| PollPipe = -1; |
| PollStatusBuffer = NULL; |
| return; |
| } |
| |
| PollPipe = statusfds[0]; |
| PollStatusBuffer = cupsdStatBufNew(PollPipe, "[Poll]"); |
| |
| /* |
| * Run each polling daemon, redirecting stderr to the polling pipe... |
| */ |
| |
| 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, -1, |
| 0, DefaultProfile, NULL, &(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]); |
| |
| /* |
| * Finally, add the pipe to the input selection set... |
| */ |
| |
| cupsdAddSelect(PollPipe, (cupsd_selfunc_t)update_polling, NULL, NULL); |
| } |
| |
| |
| /* |
| * 'cupsdStopBrowsing()' - Stop sending and receiving broadcast information. |
| */ |
| |
| void |
| cupsdStopBrowsing(void) |
| { |
| cupsd_printer_t *p; /* Current printer */ |
| |
| |
| if (!Browsing || !(BrowseLocalProtocols | BrowseRemoteProtocols)) |
| return; |
| |
| /* |
| * De-register the individual printers |
| */ |
| |
| for (p = (cupsd_printer_t *)cupsArrayFirst(Printers); |
| p; |
| p = (cupsd_printer_t *)cupsArrayNext(Printers)) |
| if (!(p->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT | |
| CUPS_PRINTER_SCANNER))) |
| cupsdDeregisterPrinter(p, 1); |
| |
| /* |
| * Shut down browsing sockets... |
| */ |
| |
| if (((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_CUPS) && |
| BrowseSocket >= 0) |
| { |
| /* |
| * Close the socket and remove it from the input selection set. |
| */ |
| |
| #ifdef WIN32 |
| closesocket(BrowseSocket); |
| #else |
| close(BrowseSocket); |
| #endif /* WIN32 */ |
| |
| cupsdRemoveSelect(BrowseSocket); |
| BrowseSocket = -1; |
| } |
| |
| #ifdef HAVE_DNSSD |
| if ((BrowseLocalProtocols & BROWSE_DNSSD) && DNSSDRef) |
| dnssdStop(); |
| #endif /* HAVE_DNSSD */ |
| |
| #ifdef HAVE_LIBSLP |
| if (((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_SLP) && |
| BrowseSLPHandle) |
| { |
| /* |
| * Close SLP handle... |
| */ |
| |
| SLPClose(BrowseSLPHandle); |
| BrowseSLPHandle = NULL; |
| } |
| #endif /* HAVE_LIBSLP */ |
| |
| #ifdef HAVE_LDAP |
| if (((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_LDAP) && |
| BrowseLDAPHandle) |
| { |
| ldap_dereg_ou(ServerName, BrowseLDAPDN); |
| ldap_disconnect(BrowseLDAPHandle); |
| BrowseLDAPHandle = NULL; |
| } |
| #endif /* HAVE_OPENLDAP */ |
| |
| /* |
| * Disable LPD and SMB printer sharing as needed through external programs... |
| */ |
| |
| if (BrowseLocalProtocols & BROWSE_LPD) |
| update_lpd(0); |
| |
| if (BrowseLocalProtocols & BROWSE_SMB) |
| update_smb(0); |
| } |
| |
| |
| /* |
| * 'cupsdStopPolling()' - Stop polling servers as needed. |
| */ |
| |
| void |
| cupsdStopPolling(void) |
| { |
| int i; /* Looping var */ |
| cupsd_dirsvc_poll_t *pollp; /* Current polling server */ |
| |
| |
| if (PollPipe >= 0) |
| { |
| cupsdStatBufDelete(PollStatusBuffer); |
| close(PollPipe); |
| |
| cupsdRemoveSelect(PollPipe); |
| |
| PollPipe = -1; |
| PollStatusBuffer = NULL; |
| } |
| |
| for (i = 0, pollp = Polled; i < NumPolled; i ++, pollp ++) |
| if (pollp->pid) |
| cupsdEndProcess(pollp->pid, 0); |
| } |
| |
| |
| #ifdef HAVE_DNSSD |
| /* |
| * 'cupsdUpdateDNSSDName()' - Update the computer name we use for browsing... |
| */ |
| |
| void |
| cupsdUpdateDNSSDName(void) |
| { |
| DNSServiceErrorType error; /* Error from service creation */ |
| char webif[1024]; /* Web interface share name */ |
| # ifdef HAVE_SYSTEMCONFIGURATION |
| 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 /* HAVE_SYSTEMCONFIGURATION */ |
| |
| |
| /* |
| * Only share the web interface and printers when non-local listening is |
| * enabled... |
| */ |
| |
| |
| if (!DNSSDPort) |
| return; |
| |
| /* |
| * Get the computer name as a c-string... |
| */ |
| |
| # ifdef HAVE_SYSTEMCONFIGURATION |
| sc = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("cupsd"), NULL, NULL); |
| |
| if (sc) |
| { |
| /* |
| * Get the computer name from the dynamic store... |
| */ |
| |
| cupsdClearString(&DNSSDComputerName); |
| |
| if ((nameRef = SCDynamicStoreCopyComputerName(sc, &nameEncoding)) != NULL) |
| { |
| if (CFStringGetCString(nameRef, nameBuffer, sizeof(nameBuffer), |
| kCFStringEncodingUTF8)) |
| { |
| cupsdLogMessage(CUPSD_LOG_DEBUG, |
| "Dynamic store computer name is \"%s\".", nameBuffer); |
| cupsdSetString(&DNSSDComputerName, nameBuffer); |
| } |
| |
| CFRelease(nameRef); |
| } |
| |
| if (!DNSSDComputerName) |
| { |
| /* |
| * Use the ServerName instead... |
| */ |
| |
| cupsdLogMessage(CUPSD_LOG_DEBUG, |
| "Using ServerName \"%s\" as computer name.", ServerName); |
| cupsdSetString(&DNSSDComputerName, ServerName); |
| } |
| |
| /* |
| * Get the local hostname from the dynamic store... |
| */ |
| |
| cupsdClearString(&DNSSDHostName); |
| |
| if ((nameRef = SCDynamicStoreCopyLocalHostName(sc)) != NULL) |
| { |
| if (CFStringGetCString(nameRef, nameBuffer, sizeof(nameBuffer), |
| kCFStringEncodingUTF8)) |
| { |
| cupsdLogMessage(CUPSD_LOG_DEBUG, |
| "Dynamic store host name is \"%s\".", nameBuffer); |
| cupsdSetString(&DNSSDHostName, nameBuffer); |
| } |
| |
| CFRelease(nameRef); |
| } |
| |
| if (!DNSSDHostName) |
| { |
| /* |
| * Use the ServerName instead... |
| */ |
| |
| cupsdLogMessage(CUPSD_LOG_DEBUG, |
| "Using ServerName \"%s\" as host name.", ServerName); |
| cupsdSetString(&DNSSDHostName, ServerName); |
| } |
| |
| /* |
| * Get any Back-to-My-Mac domains and add them as aliases... |
| */ |
| |
| cupsdFreeAliases(DNSSDAlias); |
| DNSSDAlias = NULL; |
| |
| btmm = SCDynamicStoreCopyValue(sc, CFSTR("Setup:/Network/BackToMyMac")); |
| if (btmm && CFGetTypeID(btmm) == CFDictionaryGetTypeID()) |
| { |
| cupsdLogMessage(CUPSD_LOG_DEBUG, "%d Back to My Mac aliases to add.", |
| (int)CFDictionaryGetCount(btmm)); |
| CFDictionaryApplyFunction(btmm, dnssdAddAlias, NULL); |
| } |
| else if (btmm) |
| cupsdLogMessage(CUPSD_LOG_ERROR, |
| "Bad Back to My Mac data in dynamic store!"); |
| else |
| cupsdLogMessage(CUPSD_LOG_DEBUG, "No Back to My Mac aliases to add."); |
| |
| if (btmm) |
| CFRelease(btmm); |
| |
| CFRelease(sc); |
| } |
| else |
| # endif /* HAVE_SYSTEMCONFIGURATION */ |
| { |
| cupsdSetString(&DNSSDComputerName, ServerName); |
| cupsdSetString(&DNSSDHostName, ServerName); |
| } |
| |
| /* |
| * Then (re)register the web interface if enabled... |
| */ |
| |
| if (BrowseWebIF) |
| { |
| if (DNSSDComputerName) |
| snprintf(webif, sizeof(webif), "CUPS @ %s", DNSSDComputerName); |
| else |
| strlcpy(webif, "CUPS Web Interface", sizeof(webif)); |
| |
| if (WebIFRef) |
| DNSServiceRefDeallocate(WebIFRef); |
| |
| WebIFRef = DNSSDRef; |
| if ((error = DNSServiceRegister(&WebIFRef, |
| kDNSServiceFlagsShareConnection, |
| 0, webif, "_http._tcp", NULL, |
| NULL, htons(DNSSDPort), 7, |
| "\006path=/", dnssdRegisterCallback, |
| NULL)) != kDNSServiceErr_NoError) |
| cupsdLogMessage(CUPSD_LOG_ERROR, |
| "DNS-SD web interface registration failed: %d", error); |
| } |
| } |
| #endif /* HAVE_DNSSD */ |
| |
| |
| #ifdef HAVE_LDAP |
| /* |
| * 'cupsdUpdateLDAPBrowse()' - Scan for new printers via LDAP... |
| */ |
| |
| void |
| cupsdUpdateLDAPBrowse(void) |
| { |
| 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 */ |
| type_num[30]; /* Printer type number */ |
| int type; /* Printer type */ |
| int rc; /* LDAP status */ |
| int limit; /* Size limit */ |
| LDAPMessage *res, /* LDAP search results */ |
| *e; /* Current entry from search */ |
| |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, "UpdateLDAPBrowse: %s", ServerName); |
| |
| BrowseLDAPRefresh = time(NULL) + BrowseInterval; |
| |
| /* |
| * Reconnect if LDAP Handle is invalid... |
| */ |
| |
| if (! BrowseLDAPHandle) |
| { |
| ldap_reconnect(); |
| return; |
| } |
| |
| /* |
| * Search for cups printers in LDAP directory... |
| */ |
| |
| rc = ldap_search_rec(BrowseLDAPHandle, BrowseLDAPDN, LDAP_SCOPE_SUBTREE, |
| "(objectclass=cupsPrinter)", (char **)ldap_attrs, 0, &res); |
| |
| /* |
| * If ldap search was successfull then exit function |
| * and temporary disable LDAP updates... |
| */ |
| |
| if (rc != LDAP_SUCCESS) |
| { |
| if (BrowseLDAPUpdate && ((rc == LDAP_SERVER_DOWN) || (rc == LDAP_CONNECT_ERROR))) |
| { |
| BrowseLDAPUpdate = FALSE; |
| cupsdLogMessage(CUPSD_LOG_INFO, |
| "LDAP update temporary disabled"); |
| } |
| return; |
| } |
| |
| /* |
| * If LDAP updates were disabled, we will reenable them... |
| */ |
| |
| if (! BrowseLDAPUpdate) |
| { |
| BrowseLDAPUpdate = TRUE; |
| cupsdLogMessage(CUPSD_LOG_INFO, |
| "LDAP update enabled"); |
| } |
| |
| /* |
| * Count LDAP entries and return if no entry exist... |
| */ |
| |
| limit = ldap_count_entries(BrowseLDAPHandle, res); |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, "LDAP search returned %d entries", limit); |
| if (limit < 1) |
| { |
| ldap_freeres(res); |
| return; |
| } |
| |
| /* |
| * Loop through the available printers... |
| */ |
| |
| for (e = ldap_first_entry(BrowseLDAPHandle, res); |
| e; |
| e = ldap_next_entry(BrowseLDAPHandle, e)) |
| { |
| /* |
| * Get the required values from this entry... |
| */ |
| |
| if (ldap_getval_firststring(BrowseLDAPHandle, e, |
| "printerDescription", info, sizeof(info)) == -1) |
| continue; |
| |
| if (ldap_getval_firststring(BrowseLDAPHandle, e, |
| "printerLocation", location, sizeof(location)) == -1) |
| continue; |
| |
| if (ldap_getval_firststring(BrowseLDAPHandle, e, |
| "printerMakeAndModel", make_model, sizeof(make_model)) == -1) |
| continue; |
| |
| if (ldap_getval_firststring(BrowseLDAPHandle, e, |
| "printerType", type_num, sizeof(type_num)) == -1) |
| continue; |
| |
| type = atoi(type_num); |
| |
| if (ldap_getval_firststring(BrowseLDAPHandle, e, |
| "printerURI", uri, sizeof(uri)) == -1) |
| continue; |
| |
| /* |
| * 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); |
| |
| } |
| |
| ldap_freeres(res); |
| } |
| #endif /* HAVE_LDAP */ |
| |
| |
| #ifdef HAVE_LIBSLP |
| /* |
| * 'cupsdUpdateSLPBrowse()' - Get browsing information via SLP. |
| */ |
| |
| void |
| cupsdUpdateSLPBrowse(void) |
| { |
| 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 */ |
| |
| |
| /* |
| * Reset the refresh time... |
| */ |
| |
| BrowseSLPRefresh = time(NULL) + BrowseInterval; |
| |
| /* |
| * Poll for remote printers using SLP... |
| */ |
| |
| s = NULL; |
| |
| SLPFindSrvs(BrowseSLPHandle, SLP_CUPS_SRVTYPE, "", "", |
| slp_url_callback, &s); |
| |
| /* |
| * Loop through the list of available printers... |
| */ |
| |
| for (; s; s = next) |
| { |
| /* |
| * Save the "next" pointer... |
| */ |
| |
| 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 --; |
| } |
| |
| *dptr = '\0'; |
| } |
| else |
| *d = '\0'; |
| |
| return (d); |
| } |
| |
| |
| #ifdef HAVE_DNSSD |
| # ifdef HAVE_COREFOUNDATION |
| /* |
| * 'dnssdAddAlias()' - Add a DNS-SD alias name. |
| */ |
| |
| static void |
| dnssdAddAlias(const void *key, /* I - Key */ |
| const void *value, /* I - Value (domain) */ |
| void *context) /* I - Unused */ |
| { |
| char valueStr[1024], /* Domain string */ |
| hostname[1024]; /* Complete hostname */ |
| |
| |
| (void)key; |
| (void)context; |
| |
| if (CFGetTypeID((CFStringRef)value) == CFStringGetTypeID() && |
| CFStringGetCString((CFStringRef)value, valueStr, sizeof(valueStr), |
| kCFStringEncodingUTF8)) |
| { |
| snprintf(hostname, sizeof(hostname), "%s.%s", DNSSDHostName, valueStr); |
| if (!DNSSDAlias) |
| DNSSDAlias = cupsArrayNew(NULL, NULL); |
| |
| cupsdAddAlias(DNSSDAlias, hostname); |
| cupsdLogMessage(CUPSD_LOG_DEBUG, "Added Back to My Mac ServerAlias %s", |
| hostname); |
| } |
| else |
| cupsdLogMessage(CUPSD_LOG_ERROR, |
| "Bad Back to My Mac domain in dynamic store!"); |
| } |
| # endif /* HAVE_COREFOUNDATION */ |
| |
| |
| /* |
| * 'dnssdBuildTxtRecord()' - Build a TXT record from printer info. |
| */ |
| |
| static char * /* O - TXT record */ |
| dnssdBuildTxtRecord( |
| int *txt_len, /* O - TXT record length */ |
| cupsd_printer_t *p, /* I - Printer information */ |
| int for_lpd) /* I - 1 = LPD, 0 = IPP */ |
| { |
| int i; /* Looping var */ |
| 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 */ |
| |
| |
| /* |
| * Load up the key value pairs... |
| */ |
| |
| i = 0; |
| |
| keyvalue[i ][0] = "txtvers"; |
| keyvalue[i++][1] = "1"; |
| |
| keyvalue[i ][0] = "qtotal"; |
| keyvalue[i++][1] = "1"; |
| |
| keyvalue[i ][0] = "rp"; |
| keyvalue[i++][1] = rp_str; |
| if (for_lpd) |
| strlcpy(rp_str, p->name, sizeof(rp_str)); |
| else |
| snprintf(rp_str, sizeof(rp_str), "%s/%s", |
| (p->type & CUPS_PRINTER_CLASS) ? "classes" : "printers", p->name); |
| |
| keyvalue[i ][0] = "ty"; |
| keyvalue[i++][1] = p->make_model ? p->make_model : "Unknown"; |
| |
| snprintf(admin_hostname, sizeof(admin_hostname), "%s.local.", DNSSDHostName); |
| httpAssembleURIf(HTTP_URI_CODING_ALL, adminurl_str, sizeof(adminurl_str), |
| "http", NULL, admin_hostname, DNSSDPort, "/%s/%s", |
| (p->type & CUPS_PRINTER_CLASS) ? "classes" : "printers", |
| p->name); |
| keyvalue[i ][0] = "adminurl"; |
| keyvalue[i++][1] = adminurl_str; |
| |
| keyvalue[i ][0] = "note"; |
| keyvalue[i++][1] = p->location ? p->location : ""; |
| |
| keyvalue[i ][0] = "priority"; |
| keyvalue[i++][1] = for_lpd ? "100" : "0"; |
| |
| keyvalue[i ][0] = "product"; |
| keyvalue[i++][1] = p->pc && p->pc->product ? p->pc->product : "Unknown"; |
| |
| keyvalue[i ][0] = "pdl"; |
| keyvalue[i++][1] = p->pdl ? p->pdl : "application/postscript"; |
| |
| if (get_auth_info_required(p, air_str, sizeof(air_str))) |
| { |
| keyvalue[i ][0] = "air"; |
| keyvalue[i++][1] = air_str; |
| } |
| |
| keyvalue[i ][0] = "UUID"; |
| keyvalue[i++][1] = p->uuid + 9; |
| |
| #ifdef HAVE_SSL |
| keyvalue[i ][0] = "TLS"; |
| keyvalue[i++][1] = "1.2"; |
| #endif /* HAVE_SSL */ |
| |
| keyvalue[i ][0] = "Transparent"; |
| keyvalue[i++][1] = "F"; |
| |
| keyvalue[i ][0] = "Binary"; |
| keyvalue[i++][1] = "F"; |
| |
| keyvalue[i ][0] = "Fax"; |
| keyvalue[i++][1] = (p->type & CUPS_PRINTER_FAX) ? "T" : "F"; |
| |
| keyvalue[i ][0] = "Color"; |
| keyvalue[i++][1] = (p->type & CUPS_PRINTER_COLOR) ? "T" : "F"; |
| |
| keyvalue[i ][0] = "Duplex"; |
| keyvalue[i++][1] = (p->type & CUPS_PRINTER_DUPLEX) ? "T" : "F"; |
| |
| keyvalue[i ][0] = "Staple"; |
| keyvalue[i++][1] = (p->type & CUPS_PRINTER_STAPLE) ? "T" : "F"; |
| |
| keyvalue[i ][0] = "Copies"; |
| keyvalue[i++][1] = (p->type & CUPS_PRINTER_COPIES) ? "T" : "F"; |
| |
| keyvalue[i ][0] = "Collate"; |
| keyvalue[i++][1] = (p->type & CUPS_PRINTER_COLLATE) ? "T" : "F"; |
| |
| keyvalue[i ][0] = "Punch"; |
| keyvalue[i++][1] = (p->type & CUPS_PRINTER_PUNCH) ? "T" : "F"; |
| |
| keyvalue[i ][0] = "Bind"; |
| keyvalue[i++][1] = (p->type & CUPS_PRINTER_BIND) ? "T" : "F"; |
| |
| keyvalue[i ][0] = "Sort"; |
| keyvalue[i++][1] = (p->type & CUPS_PRINTER_SORT) ? "T" : "F"; |
| |
| keyvalue[i ][0] = "Scan"; |
| keyvalue[i++][1] = (p->type & CUPS_PRINTER_MFP) ? "T" : "F"; |
| |
| snprintf(type_str, sizeof(type_str), "0x%X", p->type | CUPS_PRINTER_REMOTE); |
| snprintf(state_str, sizeof(state_str), "%d", p->state); |
| |
| keyvalue[i ][0] = "printer-state"; |
| keyvalue[i++][1] = state_str; |
| |
| keyvalue[i ][0] = "printer-type"; |
| keyvalue[i++][1] = type_str; |
| |
| /* |
| * Then pack them into a proper txt record... |
| */ |
| |
| return (dnssdPackTxtRecord(txt_len, keyvalue, i)); |
| } |
| |
| |
| /* |
| * 'dnssdComparePrinters()' - Compare the registered names of two printers. |
| */ |
| |
| static int /* O - Result of comparison */ |
| dnssdComparePrinters(cupsd_printer_t *a,/* I - First printer */ |
| cupsd_printer_t *b)/* I - Second printer */ |
| { |
| return (strcasecmp(a->reg_name, b->reg_name)); |
| } |
| |
| |
| /* |
| * 'dnssdDeregisterPrinter()' - Stop sending broadcast information for a |
| * printer. |
| */ |
| |
| static void |
| dnssdDeregisterPrinter( |
| cupsd_printer_t *p) /* I - Printer */ |
| { |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, "dnssdDeregisterPrinter(%s)", p->name); |
| |
| /* |
| * Closing the socket deregisters the service |
| */ |
| |
| if (p->ipp_ref) |
| { |
| DNSServiceRefDeallocate(p->ipp_ref); |
| p->ipp_ref = NULL; |
| } |
| |
| if (p->ipp_txt) |
| { |
| /* |
| * p->ipp_txt is malloc'd, not _cupsStrAlloc'd... |
| */ |
| |
| free(p->ipp_txt); |
| p->ipp_txt = NULL; |
| } |
| |
| if (p->printer_ref) |
| { |
| DNSServiceRefDeallocate(p->printer_ref); |
| p->printer_ref = NULL; |
| } |
| |
| if (p->printer_txt) |
| { |
| /* |
| * p->printer_txt is malloc'd, not _cupsStrAlloc'd... |
| */ |
| |
| free(p->printer_txt); |
| p->printer_txt = NULL; |
| } |
| |
| /* |
| * Remove the printer from the array of DNS-SD printers, then clear the |
| * registered name... |
| */ |
| |
| cupsArrayRemove(DNSSDPrinters, p); |
| cupsdClearString(&p->reg_name); |
| } |
| |
| |
| /* |
| * 'dnssdPackTxtRecord()' - Pack an array of key/value pairs into the |
| * TXT record format. |
| */ |
| |
| static char * /* O - TXT record */ |
| dnssdPackTxtRecord(int *txt_len, /* O - TXT record length */ |
| char *keyvalue[][2], /* I - Table of key value pairs */ |
| int count) /* I - Items in table */ |
| { |
| int i; /* Looping var */ |
| int length; /* Length of TXT record */ |
| int length2; /* Length of value */ |
| char *txtRecord; /* TXT record buffer */ |
| char *cursor; /* Looping pointer */ |
| |
| |
| /* |
| * Calculate the buffer size |
| */ |
| |
| if (count <= 0) |
| return (NULL); |
| |
| for (length = i = 0; i < count; i++) |
| length += 1 + strlen(keyvalue[i][0]) + |
| (keyvalue[i][1] ? 1 + strlen(keyvalue[i][1]) : 0); |
| |
| /* |
| * Allocate and fill it |
| */ |
| |
| txtRecord = malloc(length); |
| if (txtRecord) |
| { |
| *txt_len = length; |
| |
| for (cursor = txtRecord, i = 0; i < count; i++) |
| { |
| /* |
| * Drop in the p-string style length byte followed by the data |
| */ |
| |
| length = strlen(keyvalue[i][0]); |
| length2 = keyvalue[i][1] ? 1 + strlen(keyvalue[i][1]) : 0; |
| |
| *cursor++ = (unsigned char)(length + length2); |
| |
| memcpy(cursor, keyvalue[i][0], length); |
| cursor += length; |
| |
| if (length2) |
| { |
| length2 --; |
| *cursor++ = '='; |
| memcpy(cursor, keyvalue[i][1], length2); |
| cursor += length2; |
| } |
| } |
| } |
| |
| return (txtRecord); |
| } |
| |
| |
| /* |
| * 'dnssdRegisterCallback()' - DNSServiceRegister callback. |
| */ |
| |
| 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 - User-defined context */ |
| { |
| cupsd_printer_t *p = (cupsd_printer_t *)context; |
| /* Current printer */ |
| |
| |
| (void)sdRef; |
| (void)flags; |
| (void)domain; |
| |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, "dnssdRegisterCallback(%s, %s) for %s (%s)", |
| name, regtype, p ? p->name : "Web Interface", |
| p ? (p->reg_name ? p->reg_name : "(null)") : "NA"); |
| |
| if (errorCode) |
| { |
| cupsdLogMessage(CUPSD_LOG_ERROR, |
| "DNSServiceRegister failed with error %d", (int)errorCode); |
| return; |
| } |
| else if (p && (!p->reg_name || strcasecmp(name, p->reg_name))) |
| { |
| cupsdLogMessage(CUPSD_LOG_INFO, "Using service name \"%s\" for \"%s\"", |
| name, p->name); |
| |
| cupsArrayRemove(DNSSDPrinters, p); |
| cupsdSetString(&p->reg_name, name); |
| cupsArrayAdd(DNSSDPrinters, p); |
| |
| LastEvent |= CUPSD_EVENT_PRINTER_MODIFIED; |
| } |
| } |
| |
| |
| /* |
| * 'dnssdRegisterPrinter()' - Start sending broadcast information for a printer |
| * or update the broadcast contents. |
| */ |
| |
| static void |
| dnssdRegisterPrinter(cupsd_printer_t *p)/* I - Printer */ |
| { |
| DNSServiceErrorType se; /* dnssd errors */ |
| char *ipp_txt, /* IPP TXT record buffer */ |
| *printer_txt, /* LPD TXT record buffer */ |
| name[1024], /* Service name */ |
| *nameptr; /* Pointer into name */ |
| int ipp_len, /* IPP TXT record length */ |
| printer_len, /* LPD TXT record length */ |
| printer_port; /* LPD port number */ |
| const char *regtype; /* Registration type */ |
| |
| |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, "dnssdRegisterPrinter(%s) %s", p->name, |
| !p->ipp_ref ? "new" : "update"); |
| |
| /* |
| * If per-printer sharing was just disabled make sure we're not |
| * registered before returning. |
| */ |
| |
| if (!p->shared) |
| { |
| dnssdDeregisterPrinter(p); |
| return; |
| } |
| |
| /* |
| * The registered name takes the form of "<printer-info> @ <computer 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)); |
| |
| /* |
| * If an existing printer was renamed, unregister it and start over... |
| */ |
| |
| if (p->reg_name && strcmp(p->reg_name, name)) |
| dnssdDeregisterPrinter(p); |
| |
| if (!p->reg_name) |
| { |
| cupsdSetString(&p->reg_name, name); |
| cupsArrayAdd(DNSSDPrinters, p); |
| } |
| |
| /* |
| * Register IPP and (optionally) LPD... |
| */ |
| |
| ipp_len = 0; /* anti-compiler-warning-code */ |
| ipp_txt = dnssdBuildTxtRecord(&ipp_len, p, 0); |
| |
| if (p->ipp_ref && |
| (ipp_len != p->ipp_len || memcmp(ipp_txt, p->ipp_txt, ipp_len))) |
| { |
| /* |
| * Update the existing registration... |
| */ |
| |
| /* A TTL of 0 means use record's original value (Radar 3176248) */ |
| if ((se = DNSServiceUpdateRecord(p->ipp_ref, NULL, 0, ipp_len, ipp_txt, |
| 0)) == kDNSServiceErr_NoError) |
| { |
| if (p->ipp_txt) |
| free(p->ipp_txt); |
| |
| p->ipp_txt = ipp_txt; |
| p->ipp_len = ipp_len; |
| ipp_txt = NULL; |
| } |
| else |
| { |
| /* |
| * Failed to update record, lets close this reference and move on... |
| */ |
| |
| cupsdLogMessage(CUPSD_LOG_ERROR, |
| "Unable to update IPP DNS-SD record for %s - %d", p->name, |
| se); |
| |
| DNSServiceRefDeallocate(p->ipp_ref); |
| p->ipp_ref = NULL; |
| } |
| } |
| |
| if (!p->ipp_ref) |
| { |
| /* |
| * Initial registration. Use the _fax-ipp regtype for fax queues... |
| */ |
| |
| regtype = (p->type & CUPS_PRINTER_FAX) ? "_fax-ipp._tcp" : DNSSDRegType; |
| |
| cupsdLogMessage(CUPSD_LOG_DEBUG, |
| "Registering DNS-SD printer %s with name \"%s\" and " |
| "type \"%s\"", p->name, name, regtype); |
| |
| /* |
| * Register the queue, dropping characters as needed until we succeed... |
| */ |
| |
| nameptr = name + strlen(name); |
| |
| do |
| { |
| p->ipp_ref = DNSSDRef; |
| if ((se = DNSServiceRegister(&p->ipp_ref, kDNSServiceFlagsShareConnection, |
| 0, name, regtype, NULL, NULL, |
| htons(DNSSDPort), ipp_len, ipp_txt, |
| dnssdRegisterCallback, |
| p)) == kDNSServiceErr_BadParam) |
| { |
| /* |
| * Name is too long, drop trailing characters, taking into account |
| * UTF-8 encoding... |
| */ |
| |
| nameptr --; |
| |
| while (nameptr > name && (*nameptr & 0xc0) == 0x80) |
| nameptr --; |
| |
| if (nameptr > name) |
| *nameptr = '\0'; |
| } |
| } |
| while (se == kDNSServiceErr_BadParam && nameptr > name); |
| |
| if (se == kDNSServiceErr_NoError) |
| { |
| p->ipp_txt = ipp_txt; |
| p->ipp_len = ipp_len; |
| ipp_txt = NULL; |
| } |
| else |
| cupsdLogMessage(CUPSD_LOG_WARN, |
| "DNS-SD IPP registration of \"%s\" failed: %d", |
| p->name, se); |
| } |
| |
| if (ipp_txt) |
| free(ipp_txt); |
| |
| if (BrowseLocalProtocols & BROWSE_LPD) |
| { |
| printer_len = 0; /* anti-compiler-warning-code */ |
| printer_port = 515; |
| printer_txt = dnssdBuildTxtRecord(&printer_len, p, 1); |
| } |
| else |
| { |
| printer_len = 0; |
| printer_port = 0; |
| printer_txt = NULL; |
| } |
| |
| if (p->printer_ref && |
| (printer_len != p->printer_len || |
| memcmp(printer_txt, p->printer_txt, printer_len))) |
| { |
| /* |
| * Update the existing registration... |
| */ |
| |
| /* A TTL of 0 means use record's original value (Radar 3176248) */ |
| if ((se = DNSServiceUpdateRecord(p->printer_ref, NULL, 0, printer_len, |
| printer_txt, |
| 0)) == kDNSServiceErr_NoError) |
| { |
| if (p->printer_txt) |
| free(p->printer_txt); |
| |
| p->printer_txt = printer_txt; |
| p->printer_len = printer_len; |
| printer_txt = NULL; |
| } |
| else |
| { |
| /* |
| * Failed to update record, lets close this reference and move on... |
| */ |
| |
| cupsdLogMessage(CUPSD_LOG_ERROR, |
| "Unable to update LPD DNS-SD record for %s - %d", |
| p->name, se); |
| |
| DNSServiceRefDeallocate(p->printer_ref); |
| p->printer_ref = NULL; |
| } |
| } |
| |
| if (!p->printer_ref) |
| { |
| /* |
| * Initial registration... |
| */ |
| |
| cupsdLogMessage(CUPSD_LOG_DEBUG, |
| "Registering DNS-SD printer %s with name \"%s\" and " |
| "type \"_printer._tcp\"", p->name, name); |
| |
| p->printer_ref = DNSSDRef; |
| if ((se = DNSServiceRegister(&p->printer_ref, |
| kDNSServiceFlagsShareConnection, |
| 0, name, "_printer._tcp", NULL, NULL, |
| htons(printer_port), printer_len, printer_txt, |
| dnssdRegisterCallback, |
| p)) == kDNSServiceErr_NoError) |
| { |
| p->printer_txt = printer_txt; |
| p->printer_len = printer_len; |
| printer_txt = NULL; |
| } |
| else |
| cupsdLogMessage(CUPSD_LOG_WARN, |
| "DNS-SD LPD registration of \"%s\" failed: %d", |
| p->name, se); |
| } |
| |
| if (printer_txt) |
| free(printer_txt); |
| } |
| |
| |
| /* |
| * 'dnssdStop()' - Stop all DNS-SD registrations. |
| */ |
| |
| static void |
| dnssdStop(void) |
| { |
| cupsd_printer_t *p; /* Current printer */ |
| |
| |
| /* |
| * De-register the individual printers |
| */ |
| |
| for (p = (cupsd_printer_t *)cupsArrayFirst(Printers); |
| p; |
| p = (cupsd_printer_t *)cupsArrayNext(Printers)) |
| dnssdDeregisterPrinter(p); |
| |
| /* |
| * Shutdown the rest of the service refs... |
| */ |
| |
| if (WebIFRef) |
| { |
| DNSServiceRefDeallocate(WebIFRef); |
| WebIFRef = NULL; |
| } |
| |
| if (RemoteRef) |
| { |
| DNSServiceRefDeallocate(RemoteRef); |
| RemoteRef = NULL; |
| } |
| |
| cupsdRemoveSelect(DNSServiceRefSockFD(DNSSDRef)); |
| |
| DNSServiceRefDeallocate(DNSSDRef); |
| DNSSDRef = NULL; |
| |
| cupsArrayDelete(DNSSDPrinters); |
| DNSSDPrinters = NULL; |
| |
| DNSSDPort = 0; |
| } |
| |
| |
| /* |
| * 'dnssdUpdate()' - Handle DNS-SD queries. |
| */ |
| |
| static void |
| dnssdUpdate(void) |
| { |
| DNSServiceErrorType sdErr; /* Service discovery error */ |
| |
| |
| if ((sdErr = DNSServiceProcessResult(DNSSDRef)) != kDNSServiceErr_NoError) |
| { |
| cupsdLogMessage(CUPSD_LOG_ERROR, |
| "DNS Service Discovery registration error %d!", |
| sdErr); |
| dnssdStop(); |
| } |
| } |
| #endif /* HAVE_DNSSD */ |
| |
| |
| /* |
| * 'get_auth_info_required()' - Get the auth-info-required value to advertise. |
| */ |
| |
| static char * /* O - String or NULL if none */ |
| get_auth_info_required( |
| cupsd_printer_t *p, /* I - Printer */ |
| char *buffer, /* I - Value buffer */ |
| size_t bufsize) /* I - Size of value buffer */ |
| { |
| cupsd_location_t *auth; /* Pointer to authentication element */ |
| char resource[1024]; /* Printer/class resource path */ |
| |
| |
| /* |
| * If auth-info-required is set for this printer, return that... |
| */ |
| |
| if (p->num_auth_info_required > 0 && strcmp(p->auth_info_required[0], "none")) |
| { |
| int i; /* Looping var */ |
| char *bufptr; /* Pointer into buffer */ |
| |
| for (i = 0, bufptr = buffer; i < p->num_auth_info_required; i ++) |
| { |
| if (bufptr >= (buffer + bufsize - 2)) |
| break; |
| |
| if (i) |
| *bufptr++ = ','; |
| |
| strlcpy(bufptr, p->auth_info_required[i], bufsize - (bufptr - buffer)); |
| bufptr += strlen(bufptr); |
| } |
| |
| return (buffer); |
| } |
| |
| /* |
| * Figure out the authentication data requirements to advertise... |
| */ |
| |
| if (p->type & CUPS_PRINTER_CLASS) |
| snprintf(resource, sizeof(resource), "/classes/%s", p->name); |
| else |
| snprintf(resource, sizeof(resource), "/printers/%s", p->name); |
| |
| if ((auth = cupsdFindBest(resource, HTTP_POST)) == NULL || |
| auth->type == CUPSD_AUTH_NONE) |
| auth = cupsdFindPolicyOp(p->op_policy_ptr, IPP_PRINT_JOB); |
| |
| if (auth) |
| { |
| int auth_type; /* Authentication type */ |
| |
| if ((auth_type = auth->type) == CUPSD_AUTH_DEFAULT) |
| auth_type = DefaultAuthType; |
| |
| switch (auth_type) |
| { |
| case CUPSD_AUTH_NONE : |
| return (NULL); |
| |
| case CUPSD_AUTH_NEGOTIATE : |
| strlcpy(buffer, "negotiate", bufsize); |
| break; |
| |
| default : |
| strlcpy(buffer, "username,password", bufsize); |
| break; |
| } |
| |
| return (buffer); |
| } |
| |
| return ("none"); |
| } |
| |
| |
| #ifdef __APPLE__ |
| /* |
| * 'get_hostconfig()' - Get an /etc/hostconfig service setting. |
| */ |
| |
| static int /* O - 1 for YES or AUTOMATIC, 0 for NO */ |
| get_hostconfig(const char *name) /* I - Name of service */ |
| { |
| cups_file_t *fp; /* Hostconfig file */ |
| char line[1024], /* Line from file */ |
| *ptr; /* Pointer to value */ |
| int state = 1; /* State of service */ |
| |
| |
| /* |
| * Try opening the /etc/hostconfig file; if we can't open it, assume that |
| * the service is enabled/auto. |
| */ |
| |
| if ((fp = cupsFileOpen("/etc/hostconfig", "r")) != NULL) |
| { |
| /* |
| * Read lines from the file until we find the service... |
| */ |
| |
| while (cupsFileGets(fp, line, sizeof(line))) |
| { |
| if (line[0] == '#' || (ptr = strchr(line, '=')) == NULL) |
| continue; |
| |
| *ptr++ = '\0'; |
| |
| if (!strcasecmp(line, name)) |
| { |
| /* |
| * Found the service, see if it is set to "-NO-"... |
| */ |
| |
| if (!strncasecmp(ptr, "-NO-", 4)) |
| state = 0; |
| break; |
| } |
| } |
| |
| cupsFileClose(fp); |
| } |
| |
| return (state); |
| } |
| #endif /* __APPLE__ */ |
| |
| |
| /* |
| * '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 */ |
| const char *shortname; /* Short queue name (queue) */ |
| 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 */ |
| *uuid; /* uuid value */ |
| int is_class; /* Is this queue a class? */ |
| |
| |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, |
| "process_browse_data(uri=\"%s\", host=\"%s\", " |
| "resource=\"%s\", type=%x, state=%d, location=\"%s\", " |
| "info=\"%s\", make_model=\"%s\", num_attrs=%d, attrs=%p)", |
| uri, host, resource, type, state, |
| location ? location : "(nil)", info ? info : "(nil)", |
| make_model ? make_model : "(nil)", num_attrs, attrs); |
| |
| /* |
| * Determine if the URI contains any illegal characters in it... |
| */ |
| |
| if (strncmp(uri, "ipp://", 6) || !host[0] || |
| (strncmp(resource, "/printers/", 10) && |
| strncmp(resource, "/classes/", 9))) |
| { |
| cupsdLogMessage(CUPSD_LOG_ERROR, "Bad printer URI in browse data: %s", uri); |
| return; |
| } |
| |
| if (strchr(resource, '?') || |
| (!strncmp(resource, "/printers/", 10) && strchr(resource + 10, '/')) || |
| (!strncmp(resource, "/classes/", 9) && strchr(resource + 9, '/'))) |
| { |
| cupsdLogMessage(CUPSD_LOG_ERROR, "Bad resource in browse data: %s", |
| resource); |
| return; |
| } |
| |
| /* |
| * OK, this isn't a local printer; add any remote options... |
| */ |
| |
| ipp_options = cupsGetOption("ipp-options", num_attrs, attrs); |
| |
| if (BrowseRemoteOptions) |
| { |
| if (BrowseRemoteOptions[0] == '?') |
| { |
| /* |
| * Override server-supplied options... |
| */ |
| |
| snprintf(finaluri, sizeof(finaluri), "%s%s", uri, BrowseRemoteOptions); |
| } |
| else if (ipp_options) |
| { |
| /* |
| * Combine the server and local options... |
| */ |
| |
| 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; |
| } |
| |
| /* |
| * See if we already have it listed in the Printers list, and add it if not... |
| */ |
| |
| type |= CUPS_PRINTER_REMOTE | CUPS_PRINTER_DISCOVERED; |
| type &= ~CUPS_PRINTER_IMPLICIT; |
| update = 0; |
| hptr = strchr(host, '.'); |
| sptr = strchr(ServerName, '.'); |
| is_class = type & CUPS_PRINTER_CLASS; |
| uuid = cupsGetOption("uuid", num_attrs, attrs); |
| |
| if (!ServerNameIsIP && sptr != NULL && hptr != NULL) |
| { |
| /* |
| * Strip the common domain name components... |
| */ |
| |
| while (hptr != NULL) |
| { |
| if (!strcasecmp(hptr, sptr)) |
| { |
| *hptr = '\0'; |
| break; |
| } |
| else |
| hptr = strchr(hptr + 1, '.'); |
| } |
| } |
| |
| if (is_class) |
| { |
| /* |
| * Remote destination is a class... |
| */ |
| |
| if (!strncmp(resource, "/classes/", 9)) |
| snprintf(name, sizeof(name), "%s@%s", resource + 9, host); |
| else |
| return; |
| |
| shortname = resource + 9; |
| } |
| else |
| { |
| /* |
| * Remote destination is a printer... |
| */ |
| |
| if (!strncmp(resource, "/printers/", 10)) |
| snprintf(name, sizeof(name), "%s@%s", resource + 10, host); |
| else |
| return; |
| |
| shortname = resource + 10; |
| } |
| |
| if (hptr && !*hptr) |
| *hptr = '.'; /* Resource FQDN */ |
| |
| if ((p = cupsdFindDest(name)) == NULL && BrowseShortNames) |
| { |
| /* |
| * Long name doesn't exist, try short name... |
| */ |
| |
| cupsdLogMessage(CUPSD_LOG_DEBUG, "process_browse_data: %s not found...", |
| name); |
| |
| if ((p = cupsdFindDest(shortname)) == NULL) |
| { |
| /* |
| * Short name doesn't exist, use it for this shared queue. |
| */ |
| |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, "process_browse_data: %s not found...", |
| shortname); |
| strlcpy(name, shortname, sizeof(name)); |
| } |
| else |
| { |
| /* |
| * Short name exists... |
| */ |
| |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, |
| "process_browse_data: %s found, type=%x, hostname=%s...", |
| shortname, p->type, p->hostname ? p->hostname : "(nil)"); |
| |
| if (p->type & CUPS_PRINTER_IMPLICIT) |
| p = NULL; /* Don't replace implicit classes */ |
| else if (p->hostname && strcasecmp(p->hostname, host)) |
| { |
| /* |
| * Short name exists but is for a different host. If this is a remote |
| * queue, rename it and use the long name... |
| */ |
| |
| if (p->type & CUPS_PRINTER_REMOTE) |
| { |
| cupsdLogMessage(CUPSD_LOG_DEBUG, |
| "Renamed remote %s \"%s\" to \"%s@%s\"...", |
| is_class ? "class" : "printer", p->name, p->name, |
| p->hostname); |
| cupsdAddEvent(CUPSD_EVENT_PRINTER_DELETED, p, NULL, |
| "%s \'%s\' deleted by directory services.", |
| is_class ? "Class" : "Printer", p->name); |
| |
| snprintf(newname, sizeof(newname), "%s@%s", p->name, p->hostname); |
| cupsdRenamePrinter(p, newname); |
| |
| cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED, p, NULL, |
| "%s \'%s\' added by directory services.", |
| is_class ? "Class" : "Printer", p->name); |
| } |
| |
| /* |
| * Force creation with long name... |
| */ |
| |
| p = NULL; |
| } |
| } |
| } |
| else if (p) |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, |
| "process_browse_data: %s found, type=%x, hostname=%s...", |
| name, p->type, p->hostname ? p->hostname : "(nil)"); |
| |
| if (!p) |
| { |
| /* |
| * Queue doesn't exist; add it... |
| */ |
| |
| if (is_class) |
| p = cupsdAddClass(name); |
| else |
| p = cupsdAddPrinter(name); |
| |
| if (!p) |
| return; |
| |
| cupsdClearString(&(p->hostname)); |
| |
| cupsdLogMessage(CUPSD_LOG_DEBUG, "Added remote %s \"%s\"...", |
| is_class ? "class" : "printer", name); |
| |
| cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED, p, NULL, |
| "%s \'%s\' added by directory services.", |
| is_class ? "Class" : "Printer", name); |
| |
| /* |
| * Force the URI to point to the real server... |
| */ |
| |
| p->type = type & ~CUPS_PRINTER_REJECTING; |
| p->accepting = 1; |
| |
| cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP); |
| } |
| |
| 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; |
| |
| cupsdMarkDirty(CUPSD_DIRTY_REMOTE); |
| } |
| |
| /* |
| * Update the state... |
| */ |
| |
| p->state = state; |
| p->browse_time = time(NULL); |
| |
| if ((lease_duration = cupsGetOption("lease-duration", num_attrs, |
| attrs)) != NULL) |
| { |
| /* |
| * 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 (uuid && strcmp(p->uuid, uuid)) |
| { |
| cupsdSetString(&p->uuid, uuid); |
| update = 1; |
| } |
| |
| if (location && (!p->location || strcmp(p->location, location))) |
| { |
| cupsdSetString(&p->location, location); |
| update = 1; |
| } |
| |
| if (info && (!p->info || strcmp(p->info, info))) |
| { |
| cupsdSetString(&p->info, info); |
| update = 1; |
| |
| cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP | CUPSD_DIRTY_REMOTE); |
| } |
| |
| if (!make_model || !make_model[0]) |
| { |
| if (is_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); |
| } |
| 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; |
| } |
| |
| 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; |
| } |
| } |
| } |
| |
| /* |
| * Free the old options... |
| */ |
| |
| cupsFreeOptions(p->num_options, p->options); |
| } |
| |
| p->num_options = num_attrs; |
| p->options = attrs; |
| |
| if (type & CUPS_PRINTER_DELETE) |
| { |
| cupsdAddEvent(CUPSD_EVENT_PRINTER_DELETED, p, NULL, |
| "%s \'%s\' deleted by directory services.", |
| is_class ? "Class" : "Printer", p->name); |
| |
| cupsdExpireSubscriptions(p, NULL); |
| |
| cupsdDeletePrinter(p, 1); |
| cupsdUpdateImplicitClasses(); |
| cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP | CUPSD_DIRTY_REMOTE); |
| } |
| else if (update) |
| { |
| cupsdSetPrinterAttrs(p); |
| cupsdUpdateImplicitClasses(); |
| } |
| |
| /* |
| * See if we have a default printer... If not, make the first network |
| * default printer the default. |
| */ |
| |
| if (DefaultPrinter == NULL && Printers != NULL && UseNetworkDefault) |
| { |
| /* |
| * Find the first network default printer and use it... |
| */ |
| |
| for (p = (cupsd_printer_t *)cupsArrayFirst(Printers); |
| p; |
| p = (cupsd_printer_t *)cupsArrayNext(Printers)) |
| if (p->type & CUPS_PRINTER_DEFAULT) |
| { |
| DefaultPrinter = p; |
| cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP | CUPSD_DIRTY_REMOTE); |
| break; |
| } |
| } |
| |
| /* |
| * Do auto-classing if needed... |
| */ |
| |
| process_implicit_classes(); |
| } |
| |
| |
| /* |
| * 'process_implicit_classes()' - Create/update implicit classes as needed. |
| */ |
| |
| 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; |
| |
| /* |
| * Loop through all available printers and create classes as needed... |
| */ |
| |
| 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)) |
| { |
| /* |
| * Skip implicit classes... |
| */ |
| |
| 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); |
| |
| cupsdSetString(&pclass->job_sheets[0], p->job_sheets[0]); |
| cupsdSetString(&pclass->job_sheets[1], p->job_sheets[1]); |
| |
| update = 1; |
| |
| cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP | CUPSD_DIRTY_REMOTE); |
| |
| 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; |
| } |
| } |
| 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); |
| |
| if (len >= sizeof(name)) |
| { |
| /* |
| * If the printer name length somehow is greater than we normally allow, |
| * skip this printer... |
| */ |
| |
| len = 0; |
| cupsArrayRestore(Printers); |
| continue; |
| } |
| |
| 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"... |
| */ |
| |
| if (ImplicitAnyClasses && len < (sizeof(name) - 4)) |
| { |
| /* |
| * Add "Any" to the class name... |
| */ |
| |
| 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... |
| */ |
| |
| len = 0; |
| cupsArrayRestore(Printers); |
| continue; |
| } |
| } |
| |
| first = p; |
| } |
| |
| cupsArrayRestore(Printers); |
| } |
| |
| /* |
| * Update the last printer class as needed... |
| */ |
| |
| if (pclass && update) |
| cupsdSetPrinterAttrs(pclass); |
| } |
| |
| |
| /* |
| * 'send_cups_browse()' - Send new browsing information using the CUPS |
| * protocol. |
| */ |
| |
| static void |
| send_cups_browse(cupsd_printer_t *p) /* I - Printer to send */ |
| { |
| 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 */ |
| air[1024]; /* auth-info-required */ |
| 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; |
| |
| if (p == DefaultPrinter) |
| type |= CUPS_PRINTER_DEFAULT; |
| |
| /* |
| * Remove quotes from printer-info, printer-location, and |
| * printer-make-and-model attributes... |
| */ |
| |
| 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)); |
| |
| if (get_auth_info_required(p, packet, sizeof(packet))) |
| snprintf(air, sizeof(air), " auth-info-required=%s", packet); |
| else |
| air[0] = '\0'; |
| |
| /* |
| * Send a packet to each browse address... |
| */ |
| |
| 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; |
| |
| 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%s uuid=%s\n", |
| type, p->state, uri, location, info, make_model, |
| p->browse_attrs ? p->browse_attrs : "", air, p->uuid); |
| |
| 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 if ((iface = cupsdNetIFFind(b->iface)) != NULL) |
| { |
| /* |
| * Send to the named interface using the IPv4 address... |
| */ |
| |
| 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 (iface) |
| { |
| 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%s uuid=%s\n", |
| type, p->state, uri, location, info, make_model, |
| p->browse_attrs ? p->browse_attrs : "", air, p->uuid); |
| |
| 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... |
| */ |
| |
| snprintf(packet, sizeof(packet), |
| "%x %x %s \"%s\" \"%s\" \"%s\" %s%s uuid=%s\n", |
| type, p->state, p->uri, location, info, make_model, |
| p->browse_attrs ? p->browse_attrs : "", air, p->uuid); |
| |
| 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... |
| */ |
| |
| cupsdLogMessage(CUPSD_LOG_ERROR, |
| "cupsdSendBrowseList: sendto failed for browser " |
| "%d - %s.", |
| (int)(b - Browsers + 1), strerror(errno)); |
| |
| if (i > 1) |
| memmove(b, b + 1, (i - 1) * sizeof(cupsd_dirsvc_addr_t)); |
| |
| b --; |
| NumBrowsers --; |
| } |
| } |
| } |
| |
| |
| #ifdef HAVE_LDAP |
| /* |
| * 'ldap_search_rec()' - LDAP Search with reconnect |
| */ |
| |
| static int /* O - Return code */ |
| ldap_search_rec(LDAP *ld, /* I - LDAP handler */ |
| char *base, /* I - Base dn */ |
| int scope, /* I - LDAP search scope */ |
| char *filter, /* I - Filter string */ |
| char *attrs[], /* I - Requested attributes */ |
| int attrsonly, /* I - Return only attributes? */ |
| LDAPMessage **res) /* I - LDAP handler */ |
| { |
| int rc; /* Return code */ |
| LDAP *ldr; /* LDAP handler after reconnect */ |
| |
| |
| # if defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 |
| rc = ldap_search_ext_s(ld, base, scope, filter, attrs, attrsonly, NULL, NULL, |
| NULL, LDAP_NO_LIMIT, res); |
| # else |
| rc = ldap_search_s(ld, base, scope, filter, attrs, attrsonly, res); |
| # endif /* defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 */ |
| |
| /* |
| * If we have a connection problem try again... |
| */ |
| |
| if (rc == LDAP_SERVER_DOWN || rc == LDAP_CONNECT_ERROR) |
| { |
| cupsdLogMessage(CUPSD_LOG_ERROR, |
| "LDAP search failed with status %d: %s", |
| rc, ldap_err2string(rc)); |
| cupsdLogMessage(CUPSD_LOG_INFO, |
| "We try the LDAP search once again after reconnecting to " |
| "the server"); |
| ldap_freeres(*res); |
| ldr = ldap_reconnect(); |
| |
| # if defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 |
| rc = ldap_search_ext_s(ldr, base, scope, filter, attrs, attrsonly, NULL, |
| NULL, NULL, LDAP_NO_LIMIT, res); |
| # else |
| rc = ldap_search_s(ldr, base, scope, filter, attrs, attrsonly, res); |
| # endif /* defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 */ |
| } |
| |
| if (rc == LDAP_NO_SUCH_OBJECT) |
| cupsdLogMessage(CUPSD_LOG_DEBUG, |
| "ldap_search_rec: LDAP entry/object not found"); |
| else if (rc != LDAP_SUCCESS) |
| cupsdLogMessage(CUPSD_LOG_ERROR, |
| "ldap_search_rec: LDAP search failed with status %d: %s", |
| rc, ldap_err2string(rc)); |
| |
| if (rc != LDAP_SUCCESS) |
| ldap_freeres(*res); |
| |
| return (rc); |
| } |
| |
| |
| /* |
| * 'ldap_freeres()' - Free LDAPMessage |
| */ |
| |
| static void |
| ldap_freeres(LDAPMessage *entry) /* I - LDAP handler */ |
| { |
| int rc; /* Return value */ |
| |
| |
| rc = ldap_msgfree(entry); |
| if (rc == -1) |
| cupsdLogMessage(CUPSD_LOG_WARN, "Can't free LDAPMessage!"); |
| else if (rc == 0) |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, "Freeing LDAPMessage was unnecessary"); |
| } |
| |
| |
| /* |
| * 'ldap_getval_char()' - Get first LDAP value and convert to string |
| */ |
| |
| static int /* O - Return code */ |
| ldap_getval_firststring( |
| LDAP *ld, /* I - LDAP handler */ |
| LDAPMessage *entry, /* I - LDAP message or search result */ |
| char *attr, /* I - the wanted attribute */ |
| char *retval, /* O - String to return */ |
| unsigned long maxsize) /* I - Max string size */ |
| { |
| char *dn; /* LDAP DN */ |
| int rc = 0; /* Return code */ |
| # if defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 |
| struct berval **bval; /* LDAP value array */ |
| unsigned long size; /* String size */ |
| |
| |
| /* |
| * Get value from LDAPMessage... |
| */ |
| |
| if ((bval = ldap_get_values_len(ld, entry, attr)) == NULL) |
| { |
| rc = -1; |
| dn = ldap_get_dn(ld, entry); |
| cupsdLogMessage(CUPSD_LOG_WARN, |
| "Failed to get LDAP value %s for %s!", |
| attr, dn); |
| ldap_memfree(dn); |
| } |
| else |
| { |
| /* |
| * Check size and copy value into our string... |
| */ |
| |
| size = maxsize; |
| if (size < (bval[0]->bv_len + 1)) |
| { |
| rc = -1; |
| dn = ldap_get_dn(ld, entry); |
| cupsdLogMessage(CUPSD_LOG_WARN, |
| "Attribute %s is too big! (dn: %s)", |
| attr, dn); |
| ldap_memfree(dn); |
| } |
| else |
| size = bval[0]->bv_len + 1; |
| |
| strlcpy(retval, bval[0]->bv_val, size); |
| ldap_value_free_len(bval); |
| } |
| # else |
| char **value; /* LDAP value */ |
| |
| /* |
| * Get value from LDAPMessage... |
| */ |
| |
| if ((value = (char **)ldap_get_values(ld, entry, attr)) == NULL) |
| { |
| rc = -1; |
| dn = ldap_get_dn(ld, entry); |
| cupsdLogMessage(CUPSD_LOG_WARN, "Failed to get LDAP value %s for %s!", |
| attr, dn); |
| ldap_memfree(dn); |
| } |
| else |
| { |
| strlcpy(retval, *value, maxsize); |
| ldap_value_free(value); |
| } |
| # endif /* defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 */ |
| |
| return (rc); |
| } |
| |
| |
| /* |
| * 'send_ldap_ou()' - Send LDAP ou registrations. |
| */ |
| |
| static void |
| send_ldap_ou(char *ou, /* I - Servername/ou to register */ |
| char *basedn, /* I - Our base dn */ |
| char *descstring) /* I - Description for ou */ |
| { |
| int i; /* Looping var... */ |
| LDAPMod mods[3]; /* The 3 attributes we will be adding */ |
| LDAPMod *pmods[4]; /* Pointers to the 3 attributes + NULL */ |
| LDAPMessage *res, /* Search result token */ |
| *e; /* Current entry from search */ |
| int rc; /* LDAP status */ |
| int rcmod; /* LDAP status for modifications */ |
| char dn[1024], /* DN of the organizational unit we are adding */ |
| *desc[2], /* Change records */ |
| *ou_value[2]; |
| char old_desc[1024]; /* Old description */ |
| static const char * const objectClass_values[] = |
| { /* The 2 objectClass's we use in */ |
| "top", /* our LDAP entries */ |
| "organizationalUnit", |
| NULL |
| }; |
| static const char * const ou_attrs[] =/* CUPS LDAP attributes */ |
| { |
| "description", |
| NULL |
| }; |
| |
| |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, "send_ldap_ou: %s", ou); |
| |
| /* |
| * Reconnect if LDAP Handle is invalid... |
| */ |
| |
| if (!BrowseLDAPHandle) |
| { |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, |
| "send_ldap_ou: LDAP Handle is invalid. Try reconnecting..."); |
| ldap_reconnect(); |
| return; |
| } |
| |
| /* |
| * Prepare ldap search... |
| */ |
| |
| snprintf(dn, sizeof(dn), "ou=%s, %s", ou, basedn); |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, "send_ldap_ou: dn=\"%s\"", dn); |
| |
| ou_value[0] = ou; |
| ou_value[1] = NULL; |
| desc[0] = descstring; |
| desc[1] = NULL; |
| |
| mods[0].mod_type = "ou"; |
| mods[0].mod_values = ou_value; |
| mods[1].mod_type = "description"; |
| mods[1].mod_values = desc; |
| mods[2].mod_type = "objectClass"; |
| mods[2].mod_values = (char **)objectClass_values; |
| |
| rc = ldap_search_rec(BrowseLDAPHandle, dn, LDAP_SCOPE_BASE, NULL, |
| (char **)ou_attrs, 0, &res); |
| |
| /* |
| * If ldap search was not successfull then exit function... |
| */ |
| |
| if (rc != LDAP_SUCCESS && rc != LDAP_NO_SUCH_OBJECT) |
| return; |
| |
| /* |
| * Check if we need to insert or update the LDAP entry... |
| */ |
| |
| if (ldap_count_entries(BrowseLDAPHandle, res) > 0 && |
| rc != LDAP_NO_SUCH_OBJECT) |
| { |
| /* |
| * Printserver has already been registered, check if |
| * modification is required... |
| */ |
| |
| e = ldap_first_entry(BrowseLDAPHandle, res); |
| |
| /* |
| * Get the required values from this entry... |
| */ |
| |
| if (ldap_getval_firststring(BrowseLDAPHandle, e, "description", old_desc, |
| sizeof(old_desc)) == -1) |
| old_desc[0] = '\0'; |
| |
| /* |
| * Check if modification is required... |
| */ |
| |
| if ( strcmp(desc[0], old_desc) == 0 ) |
| { |
| /* |
| * LDAP entry for the printer exists. |
| * Printer has already been registered, |
| * no modifications required... |
| */ |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, |
| "send_ldap_ou: No updates required for %s", ou); |
| } |
| else |
| { |
| |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, |
| "send_ldap_ou: Replace entry for %s", ou); |
| |
| for (i = 0; i < 3; i ++) |
| { |
| pmods[i] = mods + i; |
| pmods[i]->mod_op = LDAP_MOD_REPLACE; |
| } |
| pmods[i] = NULL; |
| |
| # if defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 |
| if ((rcmod = ldap_modify_ext_s(BrowseLDAPHandle, dn, pmods, NULL, |
| NULL)) != LDAP_SUCCESS) |
| # else |
| if ((rcmod = ldap_modify_s(BrowseLDAPHandle, dn, pmods)) != LDAP_SUCCESS) |
| # endif /* defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 */ |
| { |
| cupsdLogMessage(CUPSD_LOG_ERROR, |
| "LDAP modify for %s failed with status %d: %s", |
| ou, rcmod, ldap_err2string(rcmod)); |
| if (rcmod == LDAP_SERVER_DOWN) |
| ldap_reconnect(); |
| } |
| } |
| } |
| else |
| { |
| /* |
| * Printserver has never been registered, |
| * add registration... |
| */ |
| |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, |
| "send_ldap_ou: Add entry for %s", ou); |
| |
| for (i = 0; i < 3; i ++) |
| { |
| pmods[i] = mods + i; |
| pmods[i]->mod_op = LDAP_MOD_ADD; |
| } |
| pmods[i] = NULL; |
| |
| # if defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 |
| if ((rcmod = ldap_add_ext_s(BrowseLDAPHandle, dn, pmods, NULL, |
| NULL)) != LDAP_SUCCESS) |
| # else |
| if ((rcmod = ldap_add_s(BrowseLDAPHandle, dn, pmods)) != LDAP_SUCCESS) |
| # endif /* defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 */ |
| { |
| cupsdLogMessage(CUPSD_LOG_ERROR, |
| "LDAP add for %s failed with status %d: %s", |
| ou, rcmod, ldap_err2string(rcmod)); |
| if (rcmod == LDAP_SERVER_DOWN) |
| ldap_reconnect(); |
| } |
| } |
| |
| if (rc == LDAP_SUCCESS) |
| ldap_freeres(res); |
| } |
| |
| |
| /* |
| * 'send_ldap_browse()' - Send LDAP printer registrations. |
| */ |
| |
| static void |
| send_ldap_browse(cupsd_printer_t *p) /* I - Printer to register */ |
| { |
| 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 */ |
| *e; /* Current entry from search */ |
| char *cn_value[2], /* Change records */ |
| *uri[2], |
| *info[2], |
| *location[2], |
| *make_model[2], |
| *type[2], |
| typestring[255], /* String to hold printer-type */ |
| dn[1024]; /* DN of the printer we are adding */ |
| int rc; /* LDAP status */ |
| int rcmod; /* LDAP status for modifications */ |
| char old_uri[HTTP_MAX_URI], /* Printer URI */ |
| old_location[1024], /* Printer location */ |
| old_info[1024], /* Printer information */ |
| old_make_model[1024], /* Printer make and model */ |
| old_type_string[30]; /* Temporary type number */ |
| int old_type; /* Printer type */ |
| 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", p->name); |
| |
| /* |
| * Exit function if LDAP updates has been disabled... |
| */ |
| |
| if (!BrowseLDAPUpdate) |
| { |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, |
| "send_ldap_browse: Updates temporary disabled; " |
| "skipping..."); |
| return; |
| } |
| |
| /* |
| * Reconnect if LDAP Handle is invalid... |
| */ |
| |
| if (!BrowseLDAPHandle) |
| { |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, |
| "send_ldap_browse: LDAP Handle is invalid. Try " |
| "reconnecting..."); |
| ldap_reconnect(); |
| return; |
| } |
| |
| /* |
| * Everything in ldap is ** so we fudge around it... |
| */ |
| |
| 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; |
| |
| /* |
| * Get ldap entry for printer ... |
| */ |
| |
| snprintf(dn, sizeof(dn), "cn=%s, ou=%s, %s", p->name, ServerName, |
| BrowseLDAPDN); |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, "send_ldap_browse: dn=\"%s\"", dn); |
| |
| rc = ldap_search_rec(BrowseLDAPHandle, dn, LDAP_SCOPE_BASE, NULL, |
| (char **)ldap_attrs, 0, &res); |
| |
| /* |
| * If ldap search was not successfull then exit function |
| * and temporary disable LDAP updates... |
| */ |
| |
| if (rc != LDAP_SUCCESS && rc != LDAP_NO_SUCH_OBJECT) |
| { |
| if (BrowseLDAPUpdate && |
| (rc == LDAP_SERVER_DOWN || rc == LDAP_CONNECT_ERROR)) |
| { |
| BrowseLDAPUpdate = FALSE; |
| cupsdLogMessage(CUPSD_LOG_INFO, |
| "LDAP update temporary disabled"); |
| } |
| |
| return; |
| } |
| |
| /* |
| * Fill modification array... |
| */ |
| |
| 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; |
| |
| /* |
| * Check if we need to insert or update the LDAP entry... |
| */ |
| |
| if (ldap_count_entries(BrowseLDAPHandle, res) > 0 && |
| rc != LDAP_NO_SUCH_OBJECT) |
| { |
| /* |
| * Printer has already been registered, check if |
| * modification is required... |
| */ |
| |
| e = ldap_first_entry(BrowseLDAPHandle, res); |
| |
| /* |
| * Get the required values from this entry... |
| */ |
| |
| if (ldap_getval_firststring(BrowseLDAPHandle, e, "printerDescription", |
| old_info, sizeof(old_info)) == -1) |
| old_info[0] = '\0'; |
| |
| if (ldap_getval_firststring(BrowseLDAPHandle, e, "printerLocation", |
| old_location, sizeof(old_location)) == -1) |
| old_info[0] = '\0'; |
| |
| if (ldap_getval_firststring(BrowseLDAPHandle, e, "printerMakeAndModel", |
| old_make_model, sizeof(old_make_model)) == -1) |
| old_info[0] = '\0'; |
| |
| if (ldap_getval_firststring(BrowseLDAPHandle, e, "printerType", |
| old_type_string, sizeof(old_type_string)) == -1) |
| old_info[0] = '\0'; |
| |
| old_type = atoi(old_type_string); |
| |
| if (ldap_getval_firststring(BrowseLDAPHandle, e, "printerURI", old_uri, |
| sizeof(old_uri)) == -1) |
| old_info[0] = '\0'; |
| |
| /* |
| * Check if modification is required... |
| */ |
| |
| if (!strcmp(info[0], old_info) && !strcmp(uri[0], old_uri) && |
| !strcmp(location[0], old_location) && |
| !strcmp(make_model[0], old_make_model) && p->type == old_type) |
| { |
| /* |
| * LDAP entry for the printer exists. Printer has already been registered, |
| * no modifications required... |
| */ |
| |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, |
| "send_ldap_browse: No updates required for %s", p->name); |
| } |
| else |
| { |
| /* |
| * LDAP entry for the printer exists. Printer has already been registered, |
| * modify the current registration... |
| */ |
| |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, |
| "send_ldap_browse: Replace entry for %s", p->name); |
| |
| for (i = 0; i < 7; i ++) |
| { |
| pmods[i] = mods + i; |
| pmods[i]->mod_op = LDAP_MOD_REPLACE; |
| } |
| pmods[i] = NULL; |
| |
| # if defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 |
| if ((rcmod = ldap_modify_ext_s(BrowseLDAPHandle, dn, pmods, NULL, |
| NULL)) != LDAP_SUCCESS) |
| # else |
| if ((rcmod = ldap_modify_s(BrowseLDAPHandle, dn, pmods)) != LDAP_SUCCESS) |
| # endif /* defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 */ |
| { |
| cupsdLogMessage(CUPSD_LOG_ERROR, |
| "LDAP modify for %s failed with status %d: %s", |
| p->name, rcmod, ldap_err2string(rcmod)); |
| if (rcmod == LDAP_SERVER_DOWN) |
| ldap_reconnect(); |
| } |
| } |
| } |
| else |
| { |
| /* |
| * No LDAP entry exists for the printer. Printer has never been registered, |
| * add the current registration... |
| */ |
| |
| send_ldap_ou(ServerName, BrowseLDAPDN, "CUPS Server"); |
| |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, |
| "send_ldap_browse: Add entry for %s", p->name); |
| |
| for (i = 0; i < 7; i ++) |
| { |
| pmods[i] = mods + i; |
| pmods[i]->mod_op = LDAP_MOD_ADD; |
| } |
| pmods[i] = NULL; |
| |
| # if defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 |
| if ((rcmod = ldap_add_ext_s(BrowseLDAPHandle, dn, pmods, NULL, |
| NULL)) != LDAP_SUCCESS) |
| # else |
| if ((rcmod = ldap_add_s(BrowseLDAPHandle, dn, pmods)) != LDAP_SUCCESS) |
| # endif /* defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 */ |
| { |
| cupsdLogMessage(CUPSD_LOG_ERROR, |
| "LDAP add for %s failed with status %d: %s", |
| p->name, rcmod, ldap_err2string(rcmod)); |
| if (rcmod == LDAP_SERVER_DOWN) |
| ldap_reconnect(); |
| } |
| } |
| |
| if (rc == LDAP_SUCCESS) |
| ldap_freeres(res); |
| } |
| |
| |
| /* |
| * 'ldap_dereg_printer()' - Delete printer from directory |
| */ |
| |
| static void |
| ldap_dereg_printer(cupsd_printer_t *p) /* I - Printer to deregister */ |
| { |
| char dn[1024]; /* DN of the printer */ |
| int rc; /* LDAP status */ |
| |
| |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, "ldap_dereg_printer: Remove entry for %s", |
| p->name); |
| |
| /* |
| * Reconnect if LDAP Handle is invalid... |
| */ |
| |
| if (!BrowseLDAPHandle) |
| { |
| ldap_reconnect(); |
| return; |
| } |
| |
| /* |
| * Get dn for printer and delete LDAP entry... |
| */ |
| |
| snprintf(dn, sizeof(dn), "cn=%s, ou=%s, %s", p->name, ServerName, |
| BrowseLDAPDN); |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, "ldap_dereg_printer: dn=\"%s\"", dn); |
| |
| # if defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 |
| if ((rc = ldap_delete_ext_s(BrowseLDAPHandle, dn, NULL, |
| NULL)) != LDAP_SUCCESS) |
| # else |
| if ((rc = ldap_delete_s(BrowseLDAPHandle, dn)) != LDAP_SUCCESS) |
| # endif /* defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 */ |
| { |
| cupsdLogMessage(CUPSD_LOG_WARN, |
| "LDAP delete for %s failed with status %d: %s", |
| p->name, rc, ldap_err2string(rc)); |
| |
| /* |
| * If we had a connection problem (connection timed out, etc.) |
| * we should reconnect and try again to delete the entry... |
| */ |
| |
| if (rc == LDAP_SERVER_DOWN || rc == LDAP_CONNECT_ERROR) |
| { |
| cupsdLogMessage(CUPSD_LOG_INFO, |
| "Retry deleting LDAP entry for %s after a reconnect...", p->name); |
| ldap_reconnect(); |
| |
| # if defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 |
| if ((rc = ldap_delete_ext_s(BrowseLDAPHandle, dn, NULL, |
| NULL)) != LDAP_SUCCESS) |
| # else |
| if ((rc = ldap_delete_s(BrowseLDAPHandle, dn)) != LDAP_SUCCESS) |
| # endif /* defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 */ |
| cupsdLogMessage(CUPSD_LOG_WARN, |
| "LDAP delete for %s failed with status %d: %s", |
| p->name, rc, ldap_err2string(rc)); |
| } |
| } |
| } |
| |
| |
| /* |
| * 'ldap_dereg_ou()' - Remove the organizational unit. |
| */ |
| |
| static void |
| ldap_dereg_ou(char *ou, /* I - Organizational unit (servername) */ |
| char *basedn) /* I - Dase dn */ |
| { |
| char dn[1024]; /* DN of the printer */ |
| int rc; /* LDAP status */ |
| |
| |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, "ldap_dereg_ou: Remove entry for %s", ou); |
| |
| /* |
| * Reconnect if LDAP Handle is invalid... |
| */ |
| |
| if (!BrowseLDAPHandle) |
| { |
| ldap_reconnect(); |
| return; |
| } |
| |
| /* |
| * Get dn for printer and delete LDAP entry... |
| */ |
| |
| snprintf(dn, sizeof(dn), "ou=%s, %s", ou, basedn); |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, "ldap_dereg_ou: dn=\"%s\"", dn); |
| |
| # if defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 |
| if ((rc = ldap_delete_ext_s(BrowseLDAPHandle, dn, NULL, |
| NULL)) != LDAP_SUCCESS) |
| # else |
| if ((rc = ldap_delete_s(BrowseLDAPHandle, dn)) != LDAP_SUCCESS) |
| # endif /* defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 */ |
| { |
| cupsdLogMessage(CUPSD_LOG_WARN, |
| "LDAP delete for %s failed with status %d: %s", |
| ou, rc, ldap_err2string(rc)); |
| |
| /* |
| * If we had a connection problem (connection timed out, etc.) |
| * we should reconnect and try again to delete the entry... |
| */ |
| |
| if (rc == LDAP_SERVER_DOWN || rc == LDAP_CONNECT_ERROR) |
| { |
| cupsdLogMessage(CUPSD_LOG_INFO, |
| "Retry deleting LDAP entry for %s after a reconnect...", ou); |
| ldap_reconnect(); |
| # if defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 |
| if ((rc = ldap_delete_ext_s(BrowseLDAPHandle, dn, NULL, |
| NULL)) != LDAP_SUCCESS) |
| # else |
| if ((rc = ldap_delete_s(BrowseLDAPHandle, dn)) != LDAP_SUCCESS) |
| # endif /* defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 */ |
| cupsdLogMessage(CUPSD_LOG_WARN, |
| "LDAP delete for %s failed with status %d: %s", |
| ou, rc, ldap_err2string(rc)); |
| } |
| } |
| } |
| #endif /* HAVE_LDAP */ |
| |
| |
| #ifdef HAVE_LIBSLP |
| /* |
| * 'send_slp_browse()' - Register the specified printer with SLP. |
| */ |
| |
| 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); |
| |
| /* |
| * Make the SLP service URL that conforms to the IANA |
| * 'printer:' template. |
| */ |
| |
| snprintf(srvurl, sizeof(srvurl), SLP_CUPS_SRVTYPE ":%s", p->uri); |
| |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, "Service URL = \"%s\"", srvurl); |
| |
| /* |
| * Figure out the finishings string... |
| */ |
| |
| if (p->type & CUPS_PRINTER_STAPLE) |
| strcpy(finishings, "staple"); |
| else |
| finishings[0] = '\0'; |
| |
| if (p->type & CUPS_PRINTER_BIND) |
| { |
| if (finishings[0]) |
| strlcat(finishings, ",bind", sizeof(finishings)); |
| else |
| strcpy(finishings, "bind"); |
| } |
| |
| if (p->type & CUPS_PRINTER_PUNCH) |
| { |
| if (finishings[0]) |
| strlcat(finishings, ",punch", sizeof(finishings)); |
| else |
| strcpy(finishings, "punch"); |
| } |
| |
| if (p->type & CUPS_PRINTER_COVER) |
| { |
| if (finishings[0]) |
| strlcat(finishings, ",cover", sizeof(finishings)); |
| else |
| strcpy(finishings, "cover"); |
| } |
| |
| if (p->type & CUPS_PRINTER_SORT) |
| { |
| if (finishings[0]) |
| strlcat(finishings, ",sort", sizeof(finishings)); |
| else |
| strcpy(finishings, "sort"); |
| } |
| |
| if (!finishings[0]) |
| strcpy(finishings, "none"); |
| |
| /* |
| * Quote any commas in the make and model, location, and info strings... |
| */ |
| |
| for (src = p->make_model, dst = make_model; |
| src && *src && dst < (make_model + sizeof(make_model) - 2);) |
| { |
| if (*src == ',' || *src == '\\' || *src == ')') |
| *dst++ = '\\'; |
| |
| *dst++ = *src++; |
| } |
| |
| *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++ = '\\'; |
| |
| *dst++ = *src++; |
| } |
| |
| *dst = '\0'; |
| |
| if (!location[0]) |
| strcpy(location, "Unknown"); |
| |
| for (src = p->info, dst = info; |
| src && *src && dst < (info + sizeof(info) - 2);) |
| { |
| if (*src == ',' || *src == '\\' || *src == ')') |
| *dst++ = '\\'; |
| |
| *dst++ = *src++; |
| } |
| |
| *dst = '\0'; |
| |
| if (!info[0]) |
| strcpy(info, "Unknown"); |
| |
| /* |
| * Get the authentication value... |
| */ |
| |
| authentication = ippFindAttribute(p->attrs, "uri-authentication-supported", |
| IPP_TAG_KEYWORD); |
| |
| /* |
| * Make the SLP attribute string list that conforms to |
| * the IANA 'printer:' template. |
| */ |
| |
| 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); |
| |
| /* |
| * Register the printer with the SLP server... |
| */ |
| |
| error = SLPReg(BrowseSLPHandle, srvurl, BrowseTimeout, |
| SLP_CUPS_SRVTYPE, attrs, SLP_TRUE, slp_reg_callback, 0); |
| |
| if (error != SLP_OK) |
| cupsdLogMessage(CUPSD_LOG_ERROR, "SLPReg of \"%s\" failed with status %d!", p->name, |
| error); |
| } |
| |
| |
| /* |
| * 'slp_attr_callback()' - SLP attribute callback |
| */ |
| |
| 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 */ |
| { |
| char *tmp = 0; /* Temporary string */ |
| cupsd_printer_t *p = (cupsd_printer_t*)cookie; |
| /* Current printer */ |
| |
| |
| (void)hslp; /* anti-compiler-warning-code */ |
| |
| /* |
| * Bail if there was an error |
| */ |
| |
| if (errcode != SLP_OK) |
| return (SLP_TRUE); |
| |
| /* |
| * Parse the attrlist to obtain things needed to build CUPS browse packet |
| */ |
| |
| memset(p, 0, sizeof(cupsd_printer_t)); |
| |
| 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; |
| |
| cupsdClearString(&tmp); |
| |
| return (SLP_TRUE); |
| } |
| |
| |
| /* |
| * 'slp_dereg_printer()' - SLPDereg() the specified printer |
| */ |
| |
| static void |
| slp_dereg_printer(cupsd_printer_t *p) /* I - Printer */ |
| { |
| char srvurl[HTTP_MAX_URI]; /* Printer service URI */ |
| |
| |
| cupsdLogMessage(CUPSD_LOG_DEBUG, "slp_dereg_printer: printer=\"%s\"", p->name); |
| |
| if (!(p->type & CUPS_PRINTER_REMOTE)) |
| { |
| /* |
| * Make the SLP service URL that conforms to the IANA |
| * 'printer:' template. |
| */ |
| |
| snprintf(srvurl, sizeof(srvurl), SLP_CUPS_SRVTYPE ":%s", p->uri); |
| |
| /* |
| * Deregister the printer... |
| */ |
| |
| SLPDereg(BrowseSLPHandle, srvurl, slp_reg_callback, 0); |
| } |
| } |
| |
| |
| /* |
| * 'slp_get_attr()' - Get an attribute from an SLP registration. |
| */ |
| |
| 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; /* ... */ |
| |
| |
| cupsdClearString(valbuf); |
| |
| if ((ptr1 = strstr(attrlist, tag)) != NULL) |
| { |
| ptr1 += strlen(tag); |
| |
| if ((ptr2 = strchr(ptr1,')')) != NULL) |
| { |
| /* |
| * Copy the value... |
| */ |
| |
| *valbuf = calloc(ptr2 - ptr1 + 1, 1); |
| strncpy(*valbuf, ptr1, ptr2 - ptr1); |
| |
| /* |
| * Dequote the value... |
| */ |
| |
| for (ptr1 = *valbuf; *ptr1; ptr1 ++) |
| if (*ptr1 == '\\' && ptr1[1]) |
| _cups_strcpy(ptr1, ptr1 + 1); |
| |
| return (0); |
| } |
| } |
| |
| return (-1); |
| } |
| |
| |
| /* |
| * 'slp_reg_callback()' - Empty SLPRegReport. |
| */ |
| |
| 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 */ |
| { |
| 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; |
| |
| /* |
| * Bail if there was an error |
| */ |
| |
| if (errcode != SLP_OK) |
| return (SLP_TRUE); |
| |
| /* |
| * Grab the head of the list... |
| */ |
| |
| head = (slpsrvurl_t**)cookie; |
| |
| /* |
| * Allocate a *temporary* slpsrvurl_t to hold this entry. |
| */ |
| |
| if ((s = (slpsrvurl_t *)calloc(1, sizeof(slpsrvurl_t))) == NULL) |
| return (SLP_FALSE); |
| |
| /* |
| * Copy the SLP service URL... |
| */ |
| |
| strlcpy(s->url, srvurl, sizeof(s->url)); |
| |
| /* |
| * Link the SLP service URL into the head of the list |
| */ |
| |
| if (*head) |
| s->next = *head; |
| |
| *head = s; |
| |
| return (SLP_TRUE); |
| } |
| #endif /* HAVE_LIBSLP */ |
| |
| |
| /* |
| * 'update_cups_browse()' - Update the browse lists using the CUPS protocol. |
| */ |
| |
| static void |
| update_cups_browse(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 */ |
| |
| |
| /* |
| * Read a packet from the browse socket... |
| */ |
| |
| 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... |
| */ |
| |
| if (errno != ECONNREFUSED && errno != EAGAIN) |
| { |
| cupsdLogMessage(CUPSD_LOG_ERROR, "Browse recv failed - %s.", |
| strerror(errno)); |
| cupsdLogMessage(CUPSD_LOG_ERROR, "CUPS browsing turned off."); |
| |
| #ifdef WIN32 |
| closesocket(BrowseSocket); |
| #else |
| close(BrowseSocket); |
| #endif /* WIN32 */ |
| |
| cupsdRemoveSelect(BrowseSocket); |
| BrowseSocket = -1; |
| |
| BrowseLocalProtocols &= ~BROWSE_CUPS; |
| BrowseRemoteProtocols &= ~BROWSE_CUPS; |
| } |
| |
| return; |
| } |
| |
| packet[bytes] = '\0'; |
| |
| /* |
| * If we're about to sleep, ignore incoming browse packets. |
| */ |
| |
| if (Sleeping) |
| return; |
| |
| /* |
| * Figure out where it came from... |
| */ |
| |
| #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); |
| } |
| |
| if (HostNameLookups) |
| httpAddrLookup(&srcaddr, srcname, sizeof(srcname)); |
| else |
| httpAddrString(&srcaddr, srcname, sizeof(srcname)); |
| |
| len = strlen(srcname); |
| |
| /* |
| * Do ACL stuff... |
| */ |
| |
| if (BrowseACL) |
| { |
| if (httpAddrLocalhost(&srcaddr) || !strcasecmp(srcname, "localhost")) |
| { |
| /* |
| * Access from localhost (127.0.0.1) is always allowed... |
| */ |
| |
| auth = CUPSD_AUTH_ALLOW; |
| } |
| else |
| { |
| /* |
| * Do authorization checks on the domain/address... |
| */ |
| |
| switch (BrowseACL->order_type) |
| { |
| default : |
| auth = CUPSD_AUTH_DENY; /* anti-compiler-warning-code */ |
| break; |
| |
| case CUPSD_AUTH_ALLOW : /* Order Deny,Allow */ |
| auth = CUPSD_AUTH_ALLOW; |
| |
| if (cupsdCheckAuth(address, srcname, len, BrowseACL->deny)) |
| auth = CUPSD_AUTH_DENY; |
| |
| if (cupsdCheckAuth(address, srcname, len, BrowseACL->allow)) |
| auth = CUPSD_AUTH_ALLOW; |
| break; |
| |
| case CUPSD_AUTH_DENY : /* Order Allow,Deny */ |
| auth = CUPSD_AUTH_DENY; |
| |
| if (cupsdCheckAuth(address, srcname, len, BrowseACL->allow)) |
| auth = CUPSD_AUTH_ALLOW; |
| |
| if (cupsdCheckAuth(address, srcname, len, BrowseACL->deny)) |
| auth = CUPSD_AUTH_DENY; |
| break; |
| } |
| } |
| } |
| else |
| auth = CUPSD_AUTH_ALLOW; |
| |
| if (auth == CUPSD_AUTH_DENY) |
| { |
| cupsdLogMessage(CUPSD_LOG_DEBUG, |
| "update_cups_browse: Refused %d bytes from %s", bytes, |
| srcname); |
| return; |
| } |
| |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, |
| "update_cups_browse: (%d bytes from %s) %s", bytes, |
| srcname, packet); |
| |
| /* |
| * Parse packet... |
| */ |
| |
| if (sscanf(packet, "%x%x%1023s", &type, &state, uri) < 3) |
| { |
| cupsdLogMessage(CUPSD_LOG_WARN, |
| "update_cups_browse: 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, Relays[i].from)) |
| if (sendto(BrowseSocket, packet, bytes, 0, |
| (struct sockaddr *)&(Relays[i].to), |
| httpAddrLength(&(Relays[i].to))) <= 0) |
| { |
| cupsdLogMessage(CUPSD_LOG_ERROR, |
| "update_cups_browse: 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); |
| } |
| |
| |
| /* |
| * 'update_lpd()' - Update the LPD configuration as needed. |
| */ |
| |
| static void |
| update_lpd(int onoff) /* - 1 = turn on, 0 = turn off */ |
| { |
| if (!LPDConfigFile) |
| return; |
| |
| #ifdef __APPLE__ |
| /* |
| * Allow /etc/hostconfig CUPS_LPD service setting to override cupsd.conf |
| * setting for backwards-compatibility. |
| */ |
| |
| if (onoff && !get_hostconfig("CUPS_LPD")) |
| onoff = 0; |
| #endif /* __APPLE__ */ |
| |
| if (!strncmp(LPDConfigFile, "xinetd:///", 10)) |
| { |
| /* |
| * Enable/disable LPD via the xinetd.d config file for cups-lpd... |
| */ |
| |
| char newfile[1024]; /* New cups-lpd.N file */ |
| cups_file_t *ofp, /* Original file pointer */ |
| *nfp; /* New file pointer */ |
| char line[1024]; /* Line from file */ |
| |
| |
| snprintf(newfile, sizeof(newfile), "%s.N", LPDConfigFile + 9); |
| |
| if ((ofp = cupsFileOpen(LPDConfigFile + 9, "r")) == NULL) |
| { |
| cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open \"%s\" - %s", |
| LPDConfigFile + 9, strerror(errno)); |
| return; |
| } |
| |
| if ((nfp = cupsFileOpen(newfile, "w")) == NULL) |
| { |
| cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create \"%s\" - %s", |
| newfile, strerror(errno)); |
| cupsFileClose(ofp); |
| return; |
| } |
| |
| /* |
| * Copy all of the lines from the cups-lpd file... |
| */ |
| |
| while (cupsFileGets(ofp, line, sizeof(line))) |
| { |
| if (line[0] == '{') |
| { |
| cupsFilePrintf(nfp, "%s\n", line); |
| snprintf(line, sizeof(line), "\tdisable = %s", |
| onoff ? "no" : "yes"); |
| } |
| else if (!strstr(line, "disable =")) |
| cupsFilePrintf(nfp, "%s\n", line); |
| } |
| |
| cupsFileClose(nfp); |
| cupsFileClose(ofp); |
| rename(newfile, LPDConfigFile + 9); |
| } |
| #ifdef __APPLE__ |
| else if (!strncmp(LPDConfigFile, "launchd:///", 11)) |
| { |
| /* |
| * Enable/disable LPD via the launchctl command... |
| */ |
| |
| char *argv[5], /* Arguments for command */ |
| *envp[MAX_ENV]; /* Environment for command */ |
| int pid; /* Process ID */ |
| |
| |
| cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0]))); |
| argv[0] = (char *)"launchctl"; |
| argv[1] = (char *)(onoff ? "load" : "unload"); |
| argv[2] = (char *)"-w"; |
| argv[3] = LPDConfigFile + 10; |
| argv[4] = NULL; |
| |
| cupsdStartProcess("/bin/launchctl", argv, envp, -1, -1, -1, -1, -1, 1, |
| NULL, NULL, &pid); |
| } |
| #endif /* __APPLE__ */ |
| else |
| cupsdLogMessage(CUPSD_LOG_INFO, "Unknown LPDConfigFile scheme!"); |
| } |
| |
| |
| /* |
| * 'update_polling()' - Read status messages from the poll daemons. |
| */ |
| |
| static void |
| update_polling(void) |
| { |
| 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 (loglevel == CUPSD_LOG_INFO) |
| cupsdLogMessage(CUPSD_LOG_INFO, "%s", message); |
| |
| if (!strchr(PollStatusBuffer->buffer, '\n')) |
| break; |
| } |
| |
| if (ptr == NULL && !PollStatusBuffer->bufused) |
| { |
| /* |
| * All polling processes have died; stop polling... |
| */ |
| |
| cupsdLogMessage(CUPSD_LOG_ERROR, |
| "update_polling: all polling processes have exited!"); |
| cupsdStopPolling(); |
| } |
| } |
| |
| |
| /* |
| * 'update_smb()' - Update the SMB configuration as needed. |
| */ |
| |
| static void |
| update_smb(int onoff) /* I - 1 = turn on, 0 = turn off */ |
| { |
| if (!SMBConfigFile) |
| return; |
| |
| if (!strncmp(SMBConfigFile, "samba:///", 9)) |
| { |
| /* |
| * Enable/disable SMB via the specified smb.conf config file... |
| */ |
| |
| char newfile[1024]; /* New smb.conf.N file */ |
| cups_file_t *ofp, /* Original file pointer */ |
| *nfp; /* New file pointer */ |
| char line[1024]; /* Line from file */ |
| int in_printers; /* In [printers] section? */ |
| |
| |
| snprintf(newfile, sizeof(newfile), "%s.N", SMBConfigFile + 8); |
| |
| if ((ofp = cupsFileOpen(SMBConfigFile + 8, "r")) == NULL) |
| { |
| cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open \"%s\" - %s", |
| SMBConfigFile + 8, strerror(errno)); |
| return; |
| } |
| |
| if ((nfp = cupsFileOpen(newfile, "w")) == NULL) |
| { |
| cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create \"%s\" - %s", |
| newfile, strerror(errno)); |
| cupsFileClose(ofp); |
| return; |
| } |
| |
| /* |
| * Copy all of the lines from the smb.conf file... |
| */ |
| |
| in_printers = 0; |
| |
| while (cupsFileGets(ofp, line, sizeof(line))) |
| { |
| if (in_printers && strstr(line, "printable =")) |
| snprintf(line, sizeof(line), " printable = %s", |
| onoff ? "yes" : "no"); |
| |
| cupsFilePrintf(nfp, "%s\n", line); |
| |
| if (line[0] == '[') |
| in_printers = !strcmp(line, "[printers]"); |
| } |
| |
| cupsFileClose(nfp); |
| cupsFileClose(ofp); |
| rename(newfile, SMBConfigFile + 8); |
| } |
| else |
| cupsdLogMessage(CUPSD_LOG_INFO, "Unknown SMBConfigFile scheme!"); |
| } |
| |
| |
| /* |
| * End of "$Id: dirsvc.c 7933 2008-09-11 00:44:58Z mike $". |
| */ |