Revert manifest to default one
[external/cups.git] / scheduler / dirsvc.c
1 /*
2  * "$Id: dirsvc.c 10243 2012-02-11 02:05:21Z mike $"
3  *
4  *   Directory services routines for the CUPS scheduler.
5  *
6  *   Copyright 2007-2011 by Apple Inc.
7  *   Copyright 1997-2007 by Easy Software Products, all rights reserved.
8  *
9  *   These coded instructions, statements, and computer programs are the
10  *   property of Apple Inc. and are protected by Federal copyright
11  *   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
12  *   which should have been included with this file.  If this file is
13  *   file is missing or damaged, see the license at "http://www.cups.org/".
14  *
15  * Contents:
16  *
17  *   cupsdDeregisterPrinter()   - Stop sending broadcast information for a local
18  *                                printer and remove any pending references to
19  *                                remote printers.
20  *   cupsdLoadRemoteCache()     - Load the remote printer cache.
21  *   cupsdRegisterPrinter()     - Start sending broadcast information for a
22  *                                printer or update the broadcast contents.
23  *   cupsdRestartPolling()      - Restart polling servers as needed.
24  *   cupsdSaveRemoteCache()     - Save the remote printer cache.
25  *   cupsdSendBrowseList()      - Send new browsing information as necessary.
26  *   ldap_rebind_proc()         - Callback function for LDAP rebind
27  *   ldap_connect()             - Start new LDAP connection
28  *   ldap_reconnect()           - Reconnect to LDAP Server
29  *   ldap_disconnect()          - Disconnect from LDAP Server
30  *   cupsdStartAvahiClient()    - Start an Avahi client if needed
31  *   cupsdStartBrowsing()       - Start sending and receiving broadcast
32  *                                information.
33  *   cupsdStartPolling()        - Start polling servers as needed.
34  *   cupsdStopBrowsing()        - Stop sending and receiving broadcast
35  *                                information.
36  *   cupsdStopPolling()         - Stop polling servers as needed.
37  *   cupsdUpdateDNSSDName()     - Update the computer name we use for
38  *                                browsing...
39  *   cupsdUpdateLDAPBrowse()    - Scan for new printers via LDAP...
40  *   cupsdUpdateSLPBrowse()     - Get browsing information via SLP.
41  *   dequote()                  - Remote quotes from a string.
42  *   dnssdAddAlias()            - Add a DNS-SD alias name.
43  *   dnssdBuildTxtRecord()      - Build a TXT record from printer info.
44  *   dnssdComparePrinters()     - Compare the registered names of two printers.
45  *   dnssdDeregisterPrinter()   - Stop sending broadcast information for a
46  *                                printer.
47  *   dnssdPackTxtRecord()       - Pack an array of key/value pairs into the TXT
48  *                                record format.
49  *   avahiPackTxtRecord()       - Pack an array of key/value pairs into an
50  *                                AvahiStringList.
51  *   dnssdRegisterCallback()    - DNSServiceRegister callback.
52  *   dnssdRegisterPrinter()     - Start sending broadcast information for a
53  *                                printer or update the broadcast contents.
54  *   dnssdStop()                - Stop all DNS-SD registrations.
55  *   dnssdUpdate()              - Handle DNS-SD queries.
56  *   get_auth_info_required()   - Get the auth-info-required value to advertise.
57  *   get_hostconfig()           - Get an /etc/hostconfig service setting.
58  *   is_local_queue()           - Determine whether the URI points at a local
59  *                                queue.
60  *   process_browse_data()      - Process new browse data.
61  *   process_implicit_classes() - Create/update implicit classes as needed.
62  *   send_cups_browse()         - Send new browsing information using the CUPS
63  *                                protocol.
64  *   ldap_search_rec()          - LDAP Search with reconnect
65  *   ldap_freeres()             - Free LDAPMessage
66  *   ldap_getval_char()         - Get first LDAP value and convert to string
67  *   send_ldap_ou()             - Send LDAP ou registrations.
68  *   send_ldap_browse()         - Send LDAP printer registrations.
69  *   ldap_dereg_printer()       - Delete printer from directory
70  *   ldap_dereg_ou()            - Remove the organizational unit.
71  *   send_slp_browse()          - Register the specified printer with SLP.
72  *   slp_attr_callback()        - SLP attribute callback
73  *   slp_dereg_printer()        - SLPDereg() the specified printer
74  *   slp_get_attr()             - Get an attribute from an SLP registration.
75  *   slp_reg_callback()         - Empty SLPRegReport.
76  *   slp_url_callback()         - SLP service url callback
77  *   update_cups_browse()       - Update the browse lists using the CUPS
78  *                                protocol.
79  *   update_lpd()               - Update the LPD configuration as needed.
80  *   update_polling()           - Read status messages from the poll daemons.
81  *   update_smb()               - Update the SMB configuration as needed.
82  */
83
84 /*
85  * Include necessary headers...
86  */
87
88 #include "cupsd.h"
89 #include <assert.h>
90 #include <grp.h>
91
92 #ifdef HAVE_DNSSD
93 #  include <dns_sd.h>
94 #  ifdef __APPLE__
95 #    include <nameser.h>
96 #    ifdef HAVE_COREFOUNDATION
97 #      include <CoreFoundation/CoreFoundation.h>
98 #    endif /* HAVE_COREFOUNDATION */
99 #    ifdef HAVE_SYSTEMCONFIGURATION
100 #      include <SystemConfiguration/SystemConfiguration.h>
101 #    endif /* HAVE_SYSTEMCONFIGURATION */
102 #  endif /* __APPLE__ */
103 #endif /* HAVE_DNSSD */
104 #ifdef HAVE_AVAHI
105 #  include <avahi-common/domain.h>
106 #endif /* HAVE_AVAHI */
107
108
109 #ifdef HAVE_DNSSD
110 typedef char *cupsd_txt_record_t;
111 #endif /* HAVE_DNSSD */
112 #ifdef HAVE_AVAHI
113 typedef AvahiStringList *cupsd_txt_record_t;
114 #endif /* HAVE_AVAHI */
115
116
117 /*
118  * Local functions...
119  */
120
121 static char     *dequote(char *d, const char *s, int dlen);
122 static char     *get_auth_info_required(cupsd_printer_t *p, char *buffer,
123                                         size_t bufsize);
124 #ifdef __APPLE__
125 static int      get_hostconfig(const char *name);
126 #endif /* __APPLE__ */
127 static int      is_local_queue(const char *uri, char *host, int hostlen,
128                                char *resource, int resourcelen);
129 static void     process_browse_data(const char *uri, const char *host,
130                                     const char *resource, cups_ptype_t type,
131                                     ipp_pstate_t state, const char *location,
132                                     const char *info, const char *make_model,
133                                     int num_attrs, cups_option_t *attrs);
134 static void     process_implicit_classes(void);
135 static void     send_cups_browse(cupsd_printer_t *p);
136 #ifdef HAVE_LDAP
137 static LDAP     *ldap_connect(void);
138 static LDAP     *ldap_reconnect(void);
139 static void     ldap_disconnect(LDAP *ld);
140 static int      ldap_search_rec(LDAP *ld, char *base, int scope,
141                                 char *filter, char *attrs[],
142                                 int attrsonly, LDAPMessage **res);
143 static int      ldap_getval_firststring(LDAP *ld, LDAPMessage *entry,
144                                         char *attr, char *retval,
145                                         unsigned long maxsize);
146 static void     ldap_freeres(LDAPMessage *entry);
147 static void     send_ldap_ou(char *ou, char *basedn, char *descstring);
148 static void     send_ldap_browse(cupsd_printer_t *p);
149 static void     ldap_dereg_printer(cupsd_printer_t *p);
150 static void     ldap_dereg_ou(char *ou, char *basedn);
151 #  ifdef HAVE_LDAP_REBIND_PROC
152 #    if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
153 static int      ldap_rebind_proc(LDAP *RebindLDAPHandle,
154                                  LDAP_CONST char *refsp,
155                                  ber_tag_t request,
156                                  ber_int_t msgid,
157                                  void *params);
158 #    else
159 static int      ldap_rebind_proc(LDAP *RebindLDAPHandle,
160                                  char **dnp,
161                                  char **passwdp,
162                                  int *authmethodp,
163                                  int freeit,
164                                  void *arg);
165 #    endif /* defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) */
166 #  endif /* HAVE_LDAP_REBIND_PROC */
167 #endif /* HAVE_LDAP */
168 #ifdef HAVE_LIBSLP
169 static void     send_slp_browse(cupsd_printer_t *p);
170 #endif /* HAVE_LIBSLP */
171 static void     update_cups_browse(void);
172 static void     update_lpd(int onoff);
173 static void     update_polling(void);
174 static void     update_smb(int onoff);
175
176
177 #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
178 static cupsd_txt_record_t dnssdBuildTxtRecord(int *txt_len, cupsd_printer_t *p,
179                                               int for_lpd);
180 static int      dnssdComparePrinters(cupsd_printer_t *a, cupsd_printer_t *b);
181 static void     dnssdDeregisterPrinter(cupsd_printer_t *p);
182 static void     dnssdRegisterPrinter(cupsd_printer_t *p);
183 static void     dnssdStop(void);
184 #endif /* defined(HAVE_DNSSD) || defined(HAVE_AVAHI) */
185
186 #ifdef HAVE_DNSSD
187 #  ifdef HAVE_COREFOUNDATION
188 static void     dnssdAddAlias(const void *key, const void *value,
189                               void *context);
190 #  endif /* HAVE_COREFOUNDATION */
191 static void     dnssdRegisterCallback(DNSServiceRef sdRef,
192                                       DNSServiceFlags flags,
193                                       DNSServiceErrorType errorCode,
194                                       const char *name, const char *regtype,
195                                       const char *domain, void *context);
196 static void     dnssdUpdate(void);
197 #endif /* HAVE_DNSSD */
198
199 #ifdef HAVE_AVAHI
200 static AvahiStringList *avahiPackTxtRecord(char *keyvalue[][2],
201                                            int count);
202 static void     avahi_entry_group_cb (AvahiEntryGroup *group,
203                                       AvahiEntryGroupState state,
204                                       void *userdata);
205 static void     avahi_client_cb (AvahiClient *client,
206                                  AvahiClientState state,
207                                  void *userdata);
208 #endif /* HAVE_AVAHI */
209
210 #ifdef HAVE_LDAP
211 static const char * const ldap_attrs[] =/* CUPS LDAP attributes */
212                 {
213                   "printerDescription",
214                   "printerLocation",
215                   "printerMakeAndModel",
216                   "printerType",
217                   "printerURI",
218                   NULL
219                 };
220 #endif /* HAVE_LDAP */
221
222 #ifdef HAVE_LIBSLP
223 /*
224  * SLP definitions...
225  */
226
227 /*
228  * SLP service name for CUPS...
229  */
230
231 #  define SLP_CUPS_SRVTYPE      "service:printer"
232 #  define SLP_CUPS_SRVLEN       15
233
234
235 /*
236  * Printer service URL structure
237  */
238
239 typedef struct _slpsrvurl_s             /**** SLP URL list ****/
240 {
241   struct _slpsrvurl_s   *next;          /* Next URL in list */
242   char                  url[HTTP_MAX_URI];
243                                         /* URL */
244 } slpsrvurl_t;
245
246
247 /*
248  * Local functions...
249  */
250
251 static SLPBoolean       slp_attr_callback(SLPHandle hslp, const char *attrlist,
252                                           SLPError errcode, void *cookie);
253 static void             slp_dereg_printer(cupsd_printer_t *p);
254 static int              slp_get_attr(const char *attrlist, const char *tag,
255                                      char **valbuf);
256 static void             slp_reg_callback(SLPHandle hslp, SLPError errcode,
257                                          void *cookie);
258 static SLPBoolean       slp_url_callback(SLPHandle hslp, const char *srvurl,
259                                          unsigned short lifetime,
260                                          SLPError errcode, void *cookie);
261 #endif /* HAVE_LIBSLP */
262
263
264 /*
265  * 'cupsdDeregisterPrinter()' - Stop sending broadcast information for a
266  *                              local printer and remove any pending
267  *                              references to remote printers.
268  */
269
270 void
271 cupsdDeregisterPrinter(
272     cupsd_printer_t *p,                 /* I - Printer to register */
273     int             removeit)           /* I - Printer being permanently removed */
274 {
275  /*
276   * Only deregister if browsing is enabled and it's a local printer...
277   */
278
279   cupsdLogMessage(CUPSD_LOG_DEBUG,
280                   "cupsdDeregisterPrinter(p=%p(%s), removeit=%d)", p, p->name,
281                   removeit);
282
283   if (!Browsing || !p->shared ||
284       (p->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT |
285                   CUPS_PRINTER_SCANNER)))
286     return;
287
288  /*
289   * Announce the deletion...
290   */
291
292   if ((BrowseLocalProtocols & BROWSE_CUPS) && BrowseSocket >= 0)
293   {
294     cups_ptype_t savedtype = p->type;   /* Saved printer type */
295
296     p->type |= CUPS_PRINTER_DELETE;
297
298     send_cups_browse(p);
299
300     p->type = savedtype;
301   }
302
303 #ifdef HAVE_LIBSLP
304   if (BrowseLocalProtocols & BROWSE_SLP)
305     slp_dereg_printer(p);
306 #endif /* HAVE_LIBSLP */
307
308 #ifdef HAVE_LDAP
309   if (BrowseLocalProtocols & BROWSE_LDAP)
310     ldap_dereg_printer(p);
311 #endif /* HAVE_LDAP */
312
313 #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
314   if (removeit && (BrowseLocalProtocols & BROWSE_DNSSD))
315     dnssdDeregisterPrinter(p);
316 #endif /* defined(HAVE_DNSSD) || defined(HAVE_AVAHI) */
317 }
318
319
320 /*
321  * 'cupsdLoadRemoteCache()' - Load the remote printer cache.
322  */
323
324 void
325 cupsdLoadRemoteCache(void)
326 {
327   int                   i;              /* Looping var */
328   cups_file_t           *fp;            /* remote.cache file */
329   int                   linenum;        /* Current line number */
330   char                  line[4096],     /* Line from file */
331                         *value,         /* Pointer to value */
332                         *valueptr,      /* Pointer into value */
333                         scheme[32],     /* Scheme portion of URI */
334                         username[64],   /* Username portion of URI */
335                         host[HTTP_MAX_HOST],
336                                         /* Hostname portion of URI */
337                         resource[HTTP_MAX_URI];
338                                         /* Resource portion of URI */
339   int                   port;           /* Port number */
340   cupsd_printer_t       *p;             /* Current printer */
341   time_t                now;            /* Current time */
342
343
344  /*
345   * Don't load the cache if the remote protocols are disabled...
346   */
347
348   if (!Browsing)
349   {
350     cupsdLogMessage(CUPSD_LOG_DEBUG,
351                     "cupsdLoadRemoteCache: Not loading remote cache.");
352     return;
353   }
354
355  /*
356   * Open the remote.cache file...
357   */
358
359   snprintf(line, sizeof(line), "%s/remote.cache", CacheDir);
360   if ((fp = cupsdOpenConfFile(line)) == NULL)
361     return;
362
363  /*
364   * Read printer configurations until we hit EOF...
365   */
366
367   linenum = 0;
368   p       = NULL;
369   now     = time(NULL);
370
371   while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
372   {
373    /*
374     * Decode the directive...
375     */
376
377     if (!_cups_strcasecmp(line, "<Printer") ||
378         !_cups_strcasecmp(line, "<DefaultPrinter"))
379     {
380      /*
381       * <Printer name> or <DefaultPrinter name>
382       */
383
384       if (p == NULL && value)
385       {
386        /*
387         * Add the printer and a base file type...
388         */
389
390         cupsdLogMessage(CUPSD_LOG_DEBUG,
391                         "cupsdLoadRemoteCache: Loading printer %s...", value);
392
393         if ((p = cupsdFindDest(value)) != NULL)
394         {
395           if (p->type & CUPS_PRINTER_CLASS)
396           {
397             cupsdLogMessage(CUPSD_LOG_WARN,
398                             "Cached remote printer \"%s\" conflicts with "
399                             "existing class!",
400                             value);
401             p = NULL;
402             continue;
403           }
404         }
405         else
406           p = cupsdAddPrinter(value);
407
408         p->accepting     = 1;
409         p->state         = IPP_PRINTER_IDLE;
410         p->type          |= CUPS_PRINTER_REMOTE | CUPS_PRINTER_DISCOVERED;
411         p->browse_time   = now;
412         p->browse_expire = now + BrowseTimeout;
413
414        /*
415         * Set the default printer as needed...
416         */
417
418         if (!_cups_strcasecmp(line, "<DefaultPrinter"))
419           DefaultPrinter = p;
420       }
421       else
422       {
423         cupsdLogMessage(CUPSD_LOG_ERROR,
424                         "Syntax error on line %d of remote.cache.", linenum);
425         break;
426       }
427     }
428     else if (!_cups_strcasecmp(line, "<Class") ||
429              !_cups_strcasecmp(line, "<DefaultClass"))
430     {
431      /*
432       * <Class name> or <DefaultClass name>
433       */
434
435       if (p == NULL && value)
436       {
437        /*
438         * Add the printer and a base file type...
439         */
440
441         cupsdLogMessage(CUPSD_LOG_DEBUG,
442                         "cupsdLoadRemoteCache: Loading class %s...", value);
443
444         if ((p = cupsdFindDest(value)) != NULL)
445           p->type = CUPS_PRINTER_CLASS;
446         else
447           p = cupsdAddClass(value);
448
449         p->accepting     = 1;
450         p->state         = IPP_PRINTER_IDLE;
451         p->type          |= CUPS_PRINTER_REMOTE | CUPS_PRINTER_DISCOVERED;
452         p->browse_time   = now;
453         p->browse_expire = now + BrowseTimeout;
454
455        /*
456         * Set the default printer as needed...
457         */
458
459         if (!_cups_strcasecmp(line, "<DefaultClass"))
460           DefaultPrinter = p;
461       }
462       else
463       {
464         cupsdLogMessage(CUPSD_LOG_ERROR,
465                         "Syntax error on line %d of remote.cache.", linenum);
466         break;
467       }
468     }
469     else if (!_cups_strcasecmp(line, "</Printer>") ||
470              !_cups_strcasecmp(line, "</Class>"))
471     {
472       if (p != NULL)
473       {
474        /*
475         * Close out the current printer...
476         */
477
478         cupsdSetPrinterAttrs(p);
479
480         p = NULL;
481       }
482       else
483         cupsdLogMessage(CUPSD_LOG_ERROR,
484                         "Syntax error on line %d of remote.cache.", linenum);
485     }
486     else if (!p)
487     {
488       cupsdLogMessage(CUPSD_LOG_ERROR,
489                       "Syntax error on line %d of remote.cache.", linenum);
490     }
491     else if (!_cups_strcasecmp(line, "UUID"))
492     {
493       if (value && !strncmp(value, "urn:uuid:", 9))
494         cupsdSetString(&(p->uuid), value);
495       else
496         cupsdLogMessage(CUPSD_LOG_ERROR,
497                         "Bad UUID on line %d of remote.cache.", linenum);
498     }
499     else if (!_cups_strcasecmp(line, "Info"))
500     {
501       if (value)
502         cupsdSetString(&p->info, value);
503     }
504     else if (!_cups_strcasecmp(line, "MakeModel"))
505     {
506       if (value)
507         cupsdSetString(&p->make_model, value);
508     }
509     else if (!_cups_strcasecmp(line, "Location"))
510     {
511       if (value)
512         cupsdSetString(&p->location, value);
513     }
514     else if (!_cups_strcasecmp(line, "DeviceURI"))
515     {
516       if (value)
517       {
518         httpSeparateURI(HTTP_URI_CODING_ALL, value, scheme, sizeof(scheme),
519                         username, sizeof(username), host, sizeof(host), &port,
520                         resource, sizeof(resource));
521
522         cupsdSetString(&p->hostname, host);
523         cupsdSetString(&p->uri, value);
524         cupsdSetDeviceURI(p, value);
525       }
526       else
527         cupsdLogMessage(CUPSD_LOG_ERROR,
528                         "Syntax error on line %d of remote.cache.", linenum);
529     }
530     else if (!_cups_strcasecmp(line, "Option") && value)
531     {
532      /*
533       * Option name value
534       */
535
536       for (valueptr = value; *valueptr && !isspace(*valueptr & 255); valueptr ++);
537
538       if (!*valueptr)
539         cupsdLogMessage(CUPSD_LOG_ERROR,
540                         "Syntax error on line %d of remote.cache.", linenum);
541       else
542       {
543         for (; *valueptr && isspace(*valueptr & 255); *valueptr++ = '\0');
544
545         p->num_options = cupsAddOption(value, valueptr, p->num_options,
546                                        &(p->options));
547       }
548     }
549     else if (!_cups_strcasecmp(line, "Reason"))
550     {
551       if (value)
552       {
553         for (i = 0 ; i < p->num_reasons; i ++)
554           if (!strcmp(value, p->reasons[i]))
555             break;
556
557         if (i >= p->num_reasons &&
558             p->num_reasons < (int)(sizeof(p->reasons) / sizeof(p->reasons[0])))
559         {
560           p->reasons[p->num_reasons] = _cupsStrAlloc(value);
561           p->num_reasons ++;
562         }
563       }
564       else
565         cupsdLogMessage(CUPSD_LOG_ERROR,
566                         "Syntax error on line %d of remote.cache.", linenum);
567     }
568     else if (!_cups_strcasecmp(line, "State"))
569     {
570      /*
571       * Set the initial queue state...
572       */
573
574       if (value && !_cups_strcasecmp(value, "idle"))
575         p->state = IPP_PRINTER_IDLE;
576       else if (value && !_cups_strcasecmp(value, "stopped"))
577       {
578         p->state = IPP_PRINTER_STOPPED;
579         cupsdSetPrinterReasons(p, "+paused");
580       }
581       else
582         cupsdLogMessage(CUPSD_LOG_ERROR,
583                         "Syntax error on line %d of remote.cache.", linenum);
584     }
585     else if (!_cups_strcasecmp(line, "StateMessage"))
586     {
587      /*
588       * Set the initial queue state message...
589       */
590
591       if (value)
592         strlcpy(p->state_message, value, sizeof(p->state_message));
593     }
594     else if (!_cups_strcasecmp(line, "Accepting"))
595     {
596      /*
597       * Set the initial accepting state...
598       */
599
600       if (value &&
601           (!_cups_strcasecmp(value, "yes") ||
602            !_cups_strcasecmp(value, "on") ||
603            !_cups_strcasecmp(value, "true")))
604         p->accepting = 1;
605       else if (value &&
606                (!_cups_strcasecmp(value, "no") ||
607                 !_cups_strcasecmp(value, "off") ||
608                 !_cups_strcasecmp(value, "false")))
609         p->accepting = 0;
610       else
611         cupsdLogMessage(CUPSD_LOG_ERROR,
612                         "Syntax error on line %d of remote.cache.", linenum);
613     }
614     else if (!_cups_strcasecmp(line, "Type"))
615     {
616       if (value)
617         p->type = atoi(value);
618       else
619         cupsdLogMessage(CUPSD_LOG_ERROR,
620                         "Syntax error on line %d of remote.cache.", linenum);
621     }
622     else if (!_cups_strcasecmp(line, "BrowseTime"))
623     {
624       if (value)
625       {
626         time_t t = atoi(value);
627
628         if (t > p->browse_expire)
629           p->browse_expire = t;
630       }
631       else
632         cupsdLogMessage(CUPSD_LOG_ERROR,
633                         "Syntax error on line %d of remote.cache.", linenum);
634     }
635     else if (!_cups_strcasecmp(line, "JobSheets"))
636     {
637      /*
638       * Set the initial job sheets...
639       */
640
641       if (value)
642       {
643         for (valueptr = value; *valueptr && !isspace(*valueptr & 255); valueptr ++);
644
645         if (*valueptr)
646           *valueptr++ = '\0';
647
648         cupsdSetString(&p->job_sheets[0], value);
649
650         while (isspace(*valueptr & 255))
651           valueptr ++;
652
653         if (*valueptr)
654         {
655           for (value = valueptr; *valueptr && !isspace(*valueptr & 255); valueptr ++);
656
657           if (*valueptr)
658             *valueptr = '\0';
659
660           cupsdSetString(&p->job_sheets[1], value);
661         }
662       }
663       else
664         cupsdLogMessage(CUPSD_LOG_ERROR,
665                         "Syntax error on line %d of remote.cache.", linenum);
666     }
667     else if (!_cups_strcasecmp(line, "AllowUser"))
668     {
669       if (value)
670       {
671         p->deny_users = 0;
672         cupsdAddString(&(p->users), value);
673       }
674       else
675         cupsdLogMessage(CUPSD_LOG_ERROR,
676                         "Syntax error on line %d of remote.cache.", linenum);
677     }
678     else if (!_cups_strcasecmp(line, "DenyUser"))
679     {
680       if (value)
681       {
682         p->deny_users = 1;
683         cupsdAddString(&(p->users), value);
684       }
685       else
686         cupsdLogMessage(CUPSD_LOG_ERROR,
687                         "Syntax error on line %d of remote.cache.", linenum);
688     }
689     else
690     {
691      /*
692       * Something else we don't understand...
693       */
694
695       cupsdLogMessage(CUPSD_LOG_ERROR,
696                       "Unknown configuration directive %s on line %d of remote.cache.",
697                       line, linenum);
698     }
699   }
700
701   cupsFileClose(fp);
702
703  /*
704   * Do auto-classing if needed...
705   */
706
707   process_implicit_classes();
708 }
709
710
711 /*
712  * 'cupsdRegisterPrinter()' - Start sending broadcast information for a
713  *                            printer or update the broadcast contents.
714  */
715
716 void
717 cupsdRegisterPrinter(cupsd_printer_t *p)/* I - Printer */
718 {
719   cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdRegisterPrinter(p=%p(%s))", p,
720                   p->name);
721
722   if (!Browsing || !BrowseLocalProtocols ||
723       (p->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT |
724                   CUPS_PRINTER_SCANNER)))
725     return;
726
727 #ifdef HAVE_LIBSLP
728 /*  if (BrowseLocalProtocols & BROWSE_SLP)
729     slpRegisterPrinter(p); */
730 #endif /* HAVE_LIBSLP */
731
732 #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
733   if ((BrowseLocalProtocols & BROWSE_DNSSD))
734     dnssdRegisterPrinter(p);
735 #endif /* defined(HAVE_DNSSD) || defined(HAVE_AVAHI) */
736 }
737
738
739 /*
740  * 'cupsdRestartPolling()' - Restart polling servers as needed.
741  */
742
743 void
744 cupsdRestartPolling(void)
745 {
746   int                   i;              /* Looping var */
747   cupsd_dirsvc_poll_t   *pollp;         /* Current polling server */
748
749
750   for (i = 0, pollp = Polled; i < NumPolled; i ++, pollp ++)
751     if (pollp->pid)
752       kill(pollp->pid, SIGHUP);
753 }
754
755
756 /*
757  * 'cupsdSaveRemoteCache()' - Save the remote printer cache.
758  */
759
760 void
761 cupsdSaveRemoteCache(void)
762 {
763   int                   i;              /* Looping var */
764   cups_file_t           *fp;            /* remote.cache file */
765   char                  filename[1024], /* remote.cache filename */
766                         temp[1024],     /* Temporary string */
767                         value[2048],    /* Value string */
768                         *name;          /* Current user name */
769   cupsd_printer_t       *printer;       /* Current printer class */
770   time_t                curtime;        /* Current time */
771   struct tm             *curdate;       /* Current date */
772   cups_option_t         *option;        /* Current option */
773
774
775  /*
776   * Create the remote.cache file...
777   */
778
779   snprintf(filename, sizeof(filename), "%s/remote.cache", CacheDir);
780
781   if ((fp = cupsdCreateConfFile(filename, ConfigFilePerm)) == NULL)
782     return;
783
784   cupsdLogMessage(CUPSD_LOG_DEBUG, "Saving remote.cache...");
785
786  /*
787   * Write a small header to the file...
788   */
789
790   curtime = time(NULL);
791   curdate = localtime(&curtime);
792   strftime(temp, sizeof(temp) - 1, "%Y-%m-%d %H:%M", curdate);
793
794   cupsFilePuts(fp, "# Remote cache file for " CUPS_SVERSION "\n");
795   cupsFilePrintf(fp, "# Written by cupsd\n");
796
797  /*
798   * Write each local printer known to the system...
799   */
800
801   for (printer = (cupsd_printer_t *)cupsArrayFirst(Printers);
802        printer;
803        printer = (cupsd_printer_t *)cupsArrayNext(Printers))
804   {
805    /*
806     * Skip local destinations...
807     */
808
809     if (!(printer->type & CUPS_PRINTER_DISCOVERED))
810       continue;
811
812    /*
813     * Write printers as needed...
814     */
815
816     if (printer == DefaultPrinter)
817       cupsFilePuts(fp, "<Default");
818     else
819       cupsFilePutChar(fp, '<');
820
821     if (printer->type & CUPS_PRINTER_CLASS)
822       cupsFilePrintf(fp, "Class %s>\n", printer->name);
823     else
824       cupsFilePrintf(fp, "Printer %s>\n", printer->name);
825
826     cupsFilePrintf(fp, "BrowseTime %d\n", (int)printer->browse_expire);
827
828     cupsFilePrintf(fp, "UUID %s\n", printer->uuid);
829
830     if (printer->info)
831       cupsFilePutConf(fp, "Info", printer->info);
832
833     if (printer->location)
834       cupsFilePutConf(fp, "Location", printer->location);
835
836     if (printer->make_model)
837       cupsFilePutConf(fp, "MakeModel", printer->make_model);
838
839     cupsFilePutConf(fp, "DeviceURI", printer->device_uri);
840
841     if (printer->state == IPP_PRINTER_STOPPED)
842       cupsFilePuts(fp, "State Stopped\n");
843     else
844       cupsFilePuts(fp, "State Idle\n");
845
846     for (i = 0; i < printer->num_reasons; i ++)
847       cupsFilePutConf(fp, "Reason", printer->reasons[i]);
848
849     cupsFilePrintf(fp, "Type %d\n", printer->type);
850
851     if (printer->accepting)
852       cupsFilePuts(fp, "Accepting Yes\n");
853     else
854       cupsFilePuts(fp, "Accepting No\n");
855
856     snprintf(value, sizeof(value), "%s %s", printer->job_sheets[0],
857              printer->job_sheets[1]);
858     cupsFilePutConf(fp, "JobSheets", value);
859
860     for (name = (char *)cupsArrayFirst(printer->users);
861          name;
862          name = (char *)cupsArrayNext(printer->users))
863       cupsFilePutConf(fp, printer->deny_users ? "DenyUser" : "AllowUser", name);
864
865     for (i = printer->num_options, option = printer->options;
866          i > 0;
867          i --, option ++)
868     {
869       snprintf(value, sizeof(value), "%s %s", option->name, option->value);
870       cupsFilePutConf(fp, "Option", value);
871     }
872
873     if (printer->type & CUPS_PRINTER_CLASS)
874       cupsFilePuts(fp, "</Class>\n");
875     else
876       cupsFilePuts(fp, "</Printer>\n");
877   }
878
879   cupsdCloseCreatedConfFile(fp, filename);
880 }
881
882
883 /*
884  * 'cupsdSendBrowseList()' - Send new browsing information as necessary.
885  */
886
887 void
888 cupsdSendBrowseList(void)
889 {
890   int                   count;          /* Number of dests to update */
891   cupsd_printer_t       *p;             /* Current printer */
892   time_t                ut,             /* Minimum update time */
893                         to;             /* Timeout time */
894
895
896   if (!Browsing || !Printers)
897     return;
898
899  /*
900   * Compute the update and timeout times...
901   */
902
903   to = time(NULL);
904   ut = to - BrowseInterval;
905
906  /*
907   * Figure out how many printers need an update...
908   */
909
910   if (BrowseInterval > 0 && BrowseLocalProtocols)
911   {
912     int max_count;                      /* Maximum number to update */
913
914
915    /*
916     * Throttle the number of printers we'll be updating this time
917     * around based on the number of queues that need updating and
918     * the maximum number of queues to update each second...
919     */
920
921     max_count = 2 * cupsArrayCount(Printers) / BrowseInterval + 1;
922
923     for (count = 0, p = (cupsd_printer_t *)cupsArrayFirst(Printers);
924          count < max_count && p != NULL;
925          p = (cupsd_printer_t *)cupsArrayNext(Printers))
926       if (!(p->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT |
927                        CUPS_PRINTER_SCANNER)) &&
928           p->shared && p->browse_time < ut)
929         count ++;
930
931    /*
932     * Loop through all of the printers and send local updates as needed...
933     */
934
935     if (BrowseNext)
936       p = (cupsd_printer_t *)cupsArrayFind(Printers, BrowseNext);
937     else
938       p = (cupsd_printer_t *)cupsArrayFirst(Printers);
939
940     for (;
941          count > 0;
942          p = (cupsd_printer_t *)cupsArrayNext(Printers))
943     {
944      /*
945       * Check for wraparound...
946       */
947
948       if (!p)
949         p = (cupsd_printer_t *)cupsArrayFirst(Printers);
950
951       if (!p)
952         break;
953       else if ((p->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT |
954                            CUPS_PRINTER_SCANNER)) ||
955                !p->shared)
956         continue;
957       else if (p->browse_time < ut)
958       {
959        /*
960         * Need to send an update...
961         */
962
963         count --;
964
965         p->browse_time = time(NULL);
966
967         if ((BrowseLocalProtocols & BROWSE_CUPS) && BrowseSocket >= 0)
968           send_cups_browse(p);
969
970 #ifdef HAVE_LIBSLP
971         if (BrowseLocalProtocols & BROWSE_SLP)
972           send_slp_browse(p);
973 #endif /* HAVE_LIBSLP */
974
975 #ifdef HAVE_LDAP
976         if (BrowseLocalProtocols & BROWSE_LDAP)
977           send_ldap_browse(p);
978 #endif /* HAVE_LDAP */
979       }
980     }
981
982    /*
983     * Save where we left off so that all printers get updated...
984     */
985
986     BrowseNext = p;
987   }
988
989  /*
990   * Loop through all of the printers and timeout old printers as needed...
991   */
992
993   for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
994        p;
995        p = (cupsd_printer_t *)cupsArrayNext(Printers))
996   {
997    /*
998     * If this is a remote queue, see if it needs to be timed out...
999     */
1000
1001     if ((p->type & CUPS_PRINTER_DISCOVERED) &&
1002         !(p->type & CUPS_PRINTER_IMPLICIT) &&
1003         p->browse_expire < to)
1004     {
1005       cupsdAddEvent(CUPSD_EVENT_PRINTER_DELETED, p, NULL,
1006                     "%s \'%s\' deleted by directory services (timeout).",
1007                     (p->type & CUPS_PRINTER_CLASS) ? "Class" : "Printer",
1008                     p->name);
1009
1010       cupsdLogMessage(CUPSD_LOG_DEBUG,
1011                       "Remote destination \"%s\" has timed out; "
1012                       "deleting it...",
1013                       p->name);
1014
1015       cupsArraySave(Printers);
1016       cupsdDeletePrinter(p, 1);
1017       cupsArrayRestore(Printers);
1018       cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP | CUPSD_DIRTY_REMOTE);
1019     }
1020   }
1021 }
1022
1023
1024 #ifdef HAVE_LDAP_REBIND_PROC
1025 #  if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
1026 /*
1027  * 'ldap_rebind_proc()' - Callback function for LDAP rebind
1028  */
1029
1030 static int                              /* O - Result code */
1031 ldap_rebind_proc(
1032     LDAP            *RebindLDAPHandle,  /* I - LDAP handle */
1033     LDAP_CONST char *refsp,             /* I - ??? */
1034     ber_tag_t       request,            /* I - ??? */
1035     ber_int_t       msgid,              /* I - ??? */
1036     void            *params)            /* I - ??? */
1037 {
1038   int           rc;                     /* Result code */
1039 #    if LDAP_API_VERSION > 3000
1040   struct berval bval;                   /* Bind value */
1041 #    endif /* LDAP_API_VERSION > 3000 */
1042
1043
1044   (void)request;
1045   (void)msgid;
1046   (void)params;
1047
1048  /*
1049   * Bind to new LDAP server...
1050   */
1051
1052   cupsdLogMessage(CUPSD_LOG_DEBUG2, "ldap_rebind_proc: Rebind to %s", refsp);
1053
1054 #    if LDAP_API_VERSION > 3000
1055   bval.bv_val = BrowseLDAPPassword;
1056   bval.bv_len = (BrowseLDAPPassword == NULL) ? 0 : strlen(BrowseLDAPPassword);
1057
1058   rc = ldap_sasl_bind_s(RebindLDAPHandle, BrowseLDAPBindDN, LDAP_SASL_SIMPLE,
1059                         &bval, NULL, NULL, NULL);
1060 #    else
1061   rc = ldap_bind_s(RebindLDAPHandle, BrowseLDAPBindDN, BrowseLDAPPassword,
1062                    LDAP_AUTH_SIMPLE);
1063 #    endif /* LDAP_API_VERSION > 3000 */
1064
1065   return (rc);
1066 }
1067
1068
1069 #  else /* defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) */
1070 /*
1071  * 'ldap_rebind_proc()' - Callback function for LDAP rebind
1072  */
1073
1074 static int                              /* O - Result code */
1075 ldap_rebind_proc(
1076     LDAP *RebindLDAPHandle,             /* I - LDAP handle */
1077     char **dnp,                         /* I - ??? */
1078     char **passwdp,                     /* I - ??? */
1079     int  *authmethodp,                  /* I - ??? */
1080     int  freeit,                        /* I - ??? */
1081     void *arg)                          /* I - ??? */
1082 {
1083   switch (freeit)
1084   {
1085     case 1:
1086        /*
1087         * Free current values...
1088         */
1089
1090         cupsdLogMessage(CUPSD_LOG_DEBUG2, "ldap_rebind_proc: Free values...");
1091
1092         if (dnp && *dnp)
1093           free(*dnp);
1094
1095         if (passwdp && *passwdp)
1096           free(*passwdp);
1097         break;
1098
1099     case 0:
1100        /*
1101         * Return credentials for LDAP referal...
1102         */
1103
1104         cupsdLogMessage(CUPSD_LOG_DEBUG2,
1105                         "ldap_rebind_proc: Return necessary values...");
1106
1107         *dnp         = strdup(BrowseLDAPBindDN);
1108         *passwdp     = strdup(BrowseLDAPPassword);
1109         *authmethodp = LDAP_AUTH_SIMPLE;
1110         break;
1111
1112     default:
1113        /*
1114         * Should never happen...
1115         */
1116
1117         cupsdLogMessage(CUPSD_LOG_ERROR,
1118                         "LDAP rebind has been called with wrong freeit value!");
1119         break;
1120   }
1121
1122   return (LDAP_SUCCESS);
1123 }
1124 #  endif /* defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) */
1125 #endif /* HAVE_LDAP_REBIND_PROC */
1126
1127
1128 #ifdef HAVE_LDAP
1129 /*
1130  * 'ldap_connect()' - Start new LDAP connection
1131  */
1132
1133 static LDAP *                           /* O - LDAP handle */
1134 ldap_connect(void)
1135 {
1136   int           rc;                     /* LDAP API status */
1137   int           version = 3;            /* LDAP version */
1138   struct berval bv = {0, ""};           /* SASL bind value */
1139   LDAP          *TempBrowseLDAPHandle=NULL;
1140                                         /* Temporary LDAP Handle */
1141 #  if defined(HAVE_LDAP_SSL) && defined (HAVE_MOZILLA_LDAP)
1142   int           ldap_ssl = 0;           /* LDAP SSL indicator */
1143   int           ssl_err = 0;            /* LDAP SSL error value */
1144 #  endif /* defined(HAVE_LDAP_SSL) && defined (HAVE_MOZILLA_LDAP) */
1145
1146
1147 #  ifdef HAVE_OPENLDAP
1148 #    ifdef HAVE_LDAP_SSL
1149  /*
1150   * Set the certificate file to use for encrypted LDAP sessions...
1151   */
1152
1153   if (BrowseLDAPCACertFile)
1154   {
1155     cupsdLogMessage(CUPSD_LOG_DEBUG,
1156                     "ldap_connect: Setting CA certificate file \"%s\"",
1157                     BrowseLDAPCACertFile);
1158
1159     if ((rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE,
1160                               (void *)BrowseLDAPCACertFile)) != LDAP_SUCCESS)
1161       cupsdLogMessage(CUPSD_LOG_ERROR,
1162                       "Unable to set CA certificate file for LDAP "
1163                       "connections: %d - %s", rc, ldap_err2string(rc));
1164   }
1165 #    endif /* HAVE_LDAP_SSL */
1166
1167  /*
1168   * Initialize OPENLDAP connection...
1169   * LDAP stuff currently only supports ldapi EXTERNAL SASL binds...
1170   */
1171
1172   if (!BrowseLDAPServer || !_cups_strcasecmp(BrowseLDAPServer, "localhost"))
1173     rc = ldap_initialize(&TempBrowseLDAPHandle, "ldapi:///");
1174   else
1175     rc = ldap_initialize(&TempBrowseLDAPHandle, BrowseLDAPServer);
1176
1177 #  else /* HAVE_OPENLDAP */
1178
1179   int           ldap_port = 0;                  /* LDAP port */
1180   char          ldap_protocol[11],              /* LDAP protocol */
1181                 ldap_host[255];                 /* LDAP host */
1182
1183  /*
1184   * Split LDAP URI into its components...
1185   */
1186
1187   if (!BrowseLDAPServer)
1188   {
1189     cupsdLogMessage(CUPSD_LOG_ERROR, "BrowseLDAPServer not configured!");
1190     cupsdLogMessage(CUPSD_LOG_ERROR, "Disabling LDAP browsing!");
1191     BrowseLocalProtocols  &= ~BROWSE_LDAP;
1192     BrowseRemoteProtocols &= ~BROWSE_LDAP;
1193     return (NULL);
1194   }
1195
1196   sscanf(BrowseLDAPServer, "%10[^:]://%254[^:/]:%d", ldap_protocol, ldap_host,
1197          &ldap_port);
1198
1199   if (!strcmp(ldap_protocol, "ldap"))
1200     ldap_ssl = 0;
1201   else if (!strcmp(ldap_protocol, "ldaps"))
1202     ldap_ssl = 1;
1203   else
1204   {
1205     cupsdLogMessage(CUPSD_LOG_ERROR, "Unrecognized LDAP protocol (%s)!",
1206                     ldap_protocol);
1207     cupsdLogMessage(CUPSD_LOG_ERROR, "Disabling LDAP browsing!");
1208     BrowseLocalProtocols &= ~BROWSE_LDAP;
1209     BrowseRemoteProtocols &= ~BROWSE_LDAP;
1210     return (NULL);
1211   }
1212
1213   if (ldap_port == 0)
1214   {
1215     if (ldap_ssl)
1216       ldap_port = LDAPS_PORT;
1217     else
1218       ldap_port = LDAP_PORT;
1219   }
1220
1221   cupsdLogMessage(CUPSD_LOG_DEBUG, "ldap_connect: PROT:%s HOST:%s PORT:%d",
1222                   ldap_protocol, ldap_host, ldap_port);
1223
1224  /*
1225   * Initialize LDAP connection...
1226   */
1227
1228   if (!ldap_ssl)
1229   {
1230     if ((TempBrowseLDAPHandle = ldap_init(ldap_host, ldap_port)) == NULL)
1231       rc = LDAP_OPERATIONS_ERROR;
1232     else
1233       rc = LDAP_SUCCESS;
1234
1235 #    ifdef HAVE_LDAP_SSL
1236   }
1237   else
1238   {
1239    /*
1240     * Initialize SSL LDAP connection...
1241     */
1242
1243     if (BrowseLDAPCACertFile)
1244     {
1245       rc = ldapssl_client_init(BrowseLDAPCACertFile, (void *)NULL);
1246       if (rc != LDAP_SUCCESS)
1247       {
1248         cupsdLogMessage(CUPSD_LOG_ERROR,
1249                         "Failed to initialize LDAP SSL client!");
1250         rc = LDAP_OPERATIONS_ERROR;
1251       }
1252       else
1253       {
1254         if ((TempBrowseLDAPHandle = ldapssl_init(ldap_host, ldap_port,
1255                                                  1)) == NULL)
1256           rc = LDAP_OPERATIONS_ERROR;
1257         else
1258           rc = LDAP_SUCCESS;
1259       }
1260     }
1261     else
1262     {
1263       cupsdLogMessage(CUPSD_LOG_ERROR,
1264                       "LDAP SSL certificate file/database not configured!");
1265       rc = LDAP_OPERATIONS_ERROR;
1266     }
1267
1268 #    else /* HAVE_LDAP_SSL */
1269
1270    /*
1271     * Return error, because client libraries doesn't support SSL
1272     */
1273
1274     cupsdLogMessage(CUPSD_LOG_ERROR,
1275                     "LDAP client libraries do not support SSL");
1276     rc = LDAP_OPERATIONS_ERROR;
1277
1278 #    endif /* HAVE_LDAP_SSL */
1279   }
1280 #  endif /* HAVE_OPENLDAP */
1281
1282  /*
1283   * Check return code from LDAP initialize...
1284   */
1285
1286   if (rc != LDAP_SUCCESS)
1287   {
1288     cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to initialize LDAP!");
1289
1290     if (rc == LDAP_SERVER_DOWN || rc == LDAP_CONNECT_ERROR)
1291       cupsdLogMessage(CUPSD_LOG_ERROR, "Temporarily disabling LDAP browsing...");
1292     else
1293     {
1294       cupsdLogMessage(CUPSD_LOG_ERROR, "Disabling LDAP browsing!");
1295
1296       BrowseLocalProtocols  &= ~BROWSE_LDAP;
1297       BrowseRemoteProtocols &= ~BROWSE_LDAP;
1298     }
1299
1300     ldap_disconnect(TempBrowseLDAPHandle);
1301
1302     return (NULL);
1303   }
1304
1305  /*
1306   * Upgrade LDAP version...
1307   */
1308
1309   if (ldap_set_option(TempBrowseLDAPHandle, LDAP_OPT_PROTOCOL_VERSION,
1310                            (const void *)&version) != LDAP_SUCCESS)
1311   {
1312     cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to set LDAP protocol version %d!",
1313                    version);
1314     cupsdLogMessage(CUPSD_LOG_ERROR, "Disabling LDAP browsing!");
1315
1316     BrowseLocalProtocols  &= ~BROWSE_LDAP;
1317     BrowseRemoteProtocols &= ~BROWSE_LDAP;
1318     ldap_disconnect(TempBrowseLDAPHandle);
1319
1320     return (NULL);
1321   }
1322
1323  /*
1324   * Register LDAP rebind procedure...
1325   */
1326
1327 #  ifdef HAVE_LDAP_REBIND_PROC
1328 #    if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
1329
1330   rc = ldap_set_rebind_proc(TempBrowseLDAPHandle, &ldap_rebind_proc,
1331                             (void *)NULL);
1332   if (rc != LDAP_SUCCESS)
1333     cupsdLogMessage(CUPSD_LOG_ERROR,
1334                     "Setting LDAP rebind function failed with status %d: %s",
1335                     rc, ldap_err2string(rc));
1336
1337 #    else
1338
1339   ldap_set_rebind_proc(TempBrowseLDAPHandle, &ldap_rebind_proc, (void *)NULL);
1340
1341 #    endif /* defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) */
1342 #  endif /* HAVE_LDAP_REBIND_PROC */
1343
1344  /*
1345   * Start LDAP bind...
1346   */
1347
1348 #  if LDAP_API_VERSION > 3000
1349   struct berval bval;
1350   bval.bv_val = BrowseLDAPPassword;
1351   bval.bv_len = (BrowseLDAPPassword == NULL) ? 0 : strlen(BrowseLDAPPassword);
1352
1353   if (!BrowseLDAPServer || !_cups_strcasecmp(BrowseLDAPServer, "localhost"))
1354     rc = ldap_sasl_bind_s(TempBrowseLDAPHandle, NULL, "EXTERNAL", &bv, NULL,
1355                           NULL, NULL);
1356   else
1357     rc = ldap_sasl_bind_s(TempBrowseLDAPHandle, BrowseLDAPBindDN, LDAP_SASL_SIMPLE, &bval, NULL, NULL, NULL);
1358
1359 #  else
1360     rc = ldap_bind_s(TempBrowseLDAPHandle, BrowseLDAPBindDN,
1361                      BrowseLDAPPassword, LDAP_AUTH_SIMPLE);
1362 #  endif /* LDAP_API_VERSION > 3000 */
1363
1364   if (rc != LDAP_SUCCESS)
1365   {
1366     cupsdLogMessage(CUPSD_LOG_ERROR, "LDAP bind failed with error %d: %s",
1367                     rc, ldap_err2string(rc));
1368
1369 #  if defined(HAVE_LDAP_SSL) && defined (HAVE_MOZILLA_LDAP)
1370     if (ldap_ssl && (rc == LDAP_SERVER_DOWN || rc == LDAP_CONNECT_ERROR))
1371     {
1372       ssl_err = PORT_GetError();
1373       if (ssl_err != 0)
1374         cupsdLogMessage(CUPSD_LOG_ERROR, "LDAP SSL error %d: %s", ssl_err,
1375                         ldapssl_err2string(ssl_err));
1376     }
1377 #  endif /* defined(HAVE_LDAP_SSL) && defined (HAVE_MOZILLA_LDAP) */
1378
1379     ldap_disconnect(TempBrowseLDAPHandle);
1380
1381     return (NULL);
1382   }
1383
1384   cupsdLogMessage(CUPSD_LOG_INFO, "LDAP connection established");
1385
1386   return (TempBrowseLDAPHandle);
1387 }
1388
1389
1390 /*
1391  * 'ldap_reconnect()' - Reconnect to LDAP Server
1392  */
1393
1394 static LDAP *                           /* O - New LDAP handle */
1395 ldap_reconnect(void)
1396 {
1397   LDAP  *TempBrowseLDAPHandle = NULL;   /* Temp Handle to LDAP server */
1398
1399
1400  /*
1401   * Get a new LDAP Handle and replace the global Handle
1402   * if the new connection was successful.
1403   */
1404
1405   cupsdLogMessage(CUPSD_LOG_INFO, "Try LDAP reconnect...");
1406
1407   TempBrowseLDAPHandle = ldap_connect();
1408
1409   if (TempBrowseLDAPHandle != NULL)
1410   {
1411     if (BrowseLDAPHandle != NULL)
1412       ldap_disconnect(BrowseLDAPHandle);
1413
1414     BrowseLDAPHandle = TempBrowseLDAPHandle;
1415   }
1416
1417   return (BrowseLDAPHandle);
1418 }
1419
1420
1421 /*
1422  * 'ldap_disconnect()' - Disconnect from LDAP Server
1423  */
1424
1425 static void
1426 ldap_disconnect(LDAP *ld)               /* I - LDAP handle */
1427 {
1428   int   rc;                             /* Return code */
1429
1430
1431  /*
1432   * Close LDAP handle...
1433   */
1434
1435 #  if defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000
1436   rc = ldap_unbind_ext_s(ld, NULL, NULL);
1437 #  else
1438   rc = ldap_unbind_s(ld);
1439 #  endif /* defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 */
1440
1441   if (rc != LDAP_SUCCESS)
1442     cupsdLogMessage(CUPSD_LOG_ERROR,
1443                     "Unbind from LDAP server failed with status %d: %s",
1444                     rc, ldap_err2string(rc));
1445 }
1446 #endif /* HAVE_LDAP */
1447
1448
1449 #ifdef HAVE_AVAHI
1450 /*
1451  * 'cupsdStartAvahiClient()' - Start an Avahi client if needed
1452  */
1453
1454 void
1455 cupsdStartAvahiClient(void)
1456 {
1457   int error = 0;
1458
1459   if (!AvahiCupsClient && !AvahiCupsClientConnecting)
1460   {
1461     if (!AvahiCupsPollHandle)
1462       AvahiCupsPollHandle = avahi_cups_poll_new ();
1463
1464     if (AvahiCupsPollHandle)
1465     {
1466       if (avahi_client_new (avahi_cups_poll_get (AvahiCupsPollHandle),
1467                             AVAHI_CLIENT_NO_FAIL,
1468                             avahi_client_cb, NULL,
1469                             &error) != NULL)
1470         AvahiCupsClientConnecting = 1;
1471       else
1472         cupsdLogMessage (CUPSD_LOG_WARN, "Avahi client failed: %d", error);
1473     }
1474   }
1475 }
1476 #endif /* HAVE_AVAHI */
1477
1478
1479 /*
1480  * 'cupsdStartBrowsing()' - Start sending and receiving broadcast information.
1481  */
1482
1483 void
1484 cupsdStartBrowsing(void)
1485 {
1486   int                   val;            /* Socket option value */
1487   struct sockaddr_in    addr;           /* Broadcast address */
1488   cupsd_printer_t       *p;             /* Current printer */
1489
1490
1491   BrowseNext = NULL;
1492
1493   if (!Browsing || !(BrowseLocalProtocols | BrowseRemoteProtocols))
1494     return;
1495
1496   if ((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_CUPS)
1497   {
1498     if (BrowseSocket < 0)
1499     {
1500      /*
1501       * Create the broadcast socket...
1502       */
1503
1504       if ((BrowseSocket = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
1505       {
1506         cupsdLogMessage(CUPSD_LOG_ERROR,
1507                         "Unable to create broadcast socket - %s.",
1508                         strerror(errno));
1509         BrowseLocalProtocols &= ~BROWSE_CUPS;
1510         BrowseRemoteProtocols &= ~BROWSE_CUPS;
1511
1512         if (FatalErrors & CUPSD_FATAL_BROWSE)
1513           cupsdEndProcess(getpid(), 0);
1514       }
1515     }
1516
1517     if (BrowseSocket >= 0)
1518     {
1519      /*
1520       * Bind the socket to browse port...
1521       */
1522
1523       memset(&addr, 0, sizeof(addr));
1524       addr.sin_addr.s_addr = htonl(INADDR_ANY);
1525       addr.sin_family      = AF_INET;
1526       addr.sin_port        = htons(BrowsePort);
1527
1528       if (bind(BrowseSocket, (struct sockaddr *)&addr, sizeof(addr)))
1529       {
1530         cupsdLogMessage(CUPSD_LOG_ERROR,
1531                         "Unable to bind broadcast socket - %s.",
1532                         strerror(errno));
1533
1534 #ifdef WIN32
1535         closesocket(BrowseSocket);
1536 #else
1537         close(BrowseSocket);
1538 #endif /* WIN32 */
1539
1540         BrowseSocket = -1;
1541         BrowseLocalProtocols &= ~BROWSE_CUPS;
1542         BrowseRemoteProtocols &= ~BROWSE_CUPS;
1543
1544         if (FatalErrors & CUPSD_FATAL_BROWSE)
1545           cupsdEndProcess(getpid(), 0);
1546       }
1547     }
1548
1549     if (BrowseSocket >= 0)
1550     {
1551      /*
1552       * Set the "broadcast" flag...
1553       */
1554
1555       val = 1;
1556       if (setsockopt(BrowseSocket, SOL_SOCKET, SO_BROADCAST, &val, sizeof(val)))
1557       {
1558         cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to set broadcast mode - %s.",
1559                         strerror(errno));
1560
1561 #ifdef WIN32
1562         closesocket(BrowseSocket);
1563 #else
1564         close(BrowseSocket);
1565 #endif /* WIN32 */
1566
1567         BrowseSocket = -1;
1568         BrowseLocalProtocols &= ~BROWSE_CUPS;
1569         BrowseRemoteProtocols &= ~BROWSE_CUPS;
1570
1571         if (FatalErrors & CUPSD_FATAL_BROWSE)
1572           cupsdEndProcess(getpid(), 0);
1573       }
1574     }
1575
1576     if (BrowseSocket >= 0)
1577     {
1578      /*
1579       * Close the socket on exec...
1580       */
1581
1582       fcntl(BrowseSocket, F_SETFD, fcntl(BrowseSocket, F_GETFD) | FD_CLOEXEC);
1583
1584      /*
1585       * Finally, add the socket to the input selection set as needed...
1586       */
1587
1588       if (BrowseRemoteProtocols & BROWSE_CUPS)
1589       {
1590        /*
1591         * We only listen if we want remote printers...
1592         */
1593
1594         cupsdAddSelect(BrowseSocket, (cupsd_selfunc_t)update_cups_browse,
1595                        NULL, NULL);
1596       }
1597     }
1598   }
1599   else
1600     BrowseSocket = -1;
1601
1602 #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
1603   if ((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_DNSSD)
1604   {
1605 #ifdef HAVE_DNSSD
1606     DNSServiceErrorType error;          /* Error from service creation */
1607 #endif /* HAVE_DNSSD */
1608     cupsd_listener_t    *lis;           /* Current listening socket */
1609
1610
1611 #ifdef HAVE_DNSSD
1612    /*
1613     * First create a "master" connection for all registrations...
1614     */
1615
1616     if ((error = DNSServiceCreateConnection(&DNSSDRef))
1617             != kDNSServiceErr_NoError)
1618     {
1619       cupsdLogMessage(CUPSD_LOG_ERROR,
1620                       "Unable to create master DNS-SD reference: %d", error);
1621
1622       if (FatalErrors & CUPSD_FATAL_BROWSE)
1623         cupsdEndProcess(getpid(), 0);
1624     }
1625     else
1626     {
1627      /*
1628       * Add the master connection to the select list...
1629       */
1630
1631       int fd = DNSServiceRefSockFD(DNSSDRef);
1632
1633       fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
1634
1635       cupsdAddSelect(fd, (cupsd_selfunc_t)dnssdUpdate, NULL, NULL);
1636 #endif /* HAVE_DNSSD */
1637
1638      /*
1639       * Then get the port we use for registrations.  If we are not listening
1640       * on any non-local ports, there is no sense sharing local printers via
1641       * Bonjour...
1642       */
1643
1644       DNSSDPort = 0;
1645
1646       for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners);
1647            lis;
1648            lis = (cupsd_listener_t *)cupsArrayNext(Listeners))
1649       {
1650         if (httpAddrLocalhost(&(lis->address)))
1651           continue;
1652
1653         DNSSDPort = _httpAddrPort(&(lis->address));
1654         break;
1655       }
1656
1657      /*
1658       * Create an array to track the printers we share...
1659       */
1660
1661       if (BrowseRemoteProtocols & BROWSE_DNSSD)
1662         DNSSDPrinters = cupsArrayNew(NULL, NULL);
1663
1664      /*
1665       * Set the computer name and register the web interface...
1666       */
1667
1668       cupsdUpdateDNSSDName();
1669
1670 #ifdef HAVE_AVAHI
1671       cupsdStartAvahiClient ();
1672 #endif /* HAVE_AVAHI */
1673
1674 #ifdef HAVE_DNSSD
1675     }
1676 #endif /* HAVE_DNSSD */
1677   }
1678 #endif /* defined(HAVE_DNSSD) || defined(HAVE_AVAHI) */
1679
1680 #ifdef HAVE_LIBSLP
1681   if ((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_SLP)
1682   {
1683    /*
1684     * Open SLP handle...
1685     */
1686
1687     if (SLPOpen("en", SLP_FALSE, &BrowseSLPHandle) != SLP_OK)
1688     {
1689       cupsdLogMessage(CUPSD_LOG_ERROR,
1690                       "Unable to open an SLP handle; disabling SLP browsing!");
1691       BrowseLocalProtocols &= ~BROWSE_SLP;
1692       BrowseRemoteProtocols &= ~BROWSE_SLP;
1693       BrowseSLPHandle = NULL;
1694
1695       if (FatalErrors & CUPSD_FATAL_BROWSE)
1696         cupsdEndProcess(getpid(), 0);
1697     }
1698
1699     BrowseSLPRefresh = 0;
1700   }
1701   else
1702     BrowseSLPHandle = NULL;
1703 #endif /* HAVE_LIBSLP */
1704
1705 #ifdef HAVE_LDAP
1706   if ((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_LDAP)
1707   {
1708     if (!BrowseLDAPDN)
1709     {
1710       cupsdLogMessage(CUPSD_LOG_ERROR,
1711                       "Need to set BrowseLDAPDN to use LDAP browsing!");
1712       BrowseLocalProtocols &= ~BROWSE_LDAP;
1713       BrowseRemoteProtocols &= ~BROWSE_LDAP;
1714
1715       if (FatalErrors & CUPSD_FATAL_BROWSE)
1716         cupsdEndProcess(getpid(), 0);
1717     }
1718     else
1719     {
1720      /*
1721       * Open LDAP handle...
1722       */
1723
1724       if ((BrowseLDAPHandle = ldap_connect()) == NULL &&
1725           (FatalErrors & CUPSD_FATAL_BROWSE))
1726         cupsdEndProcess(getpid(), 0);
1727     }
1728
1729     BrowseLDAPRefresh = 0;
1730   }
1731 #endif /* HAVE_LDAP */
1732
1733  /*
1734   * Enable LPD and SMB printer sharing as needed through external programs...
1735   */
1736
1737   if (BrowseLocalProtocols & BROWSE_LPD)
1738     update_lpd(1);
1739
1740   if (BrowseLocalProtocols & BROWSE_SMB)
1741     update_smb(1);
1742
1743  /*
1744   * Register the individual printers
1745   */
1746
1747   for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
1748        p;
1749        p = (cupsd_printer_t *)cupsArrayNext(Printers))
1750     if (!(p->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT |
1751                      CUPS_PRINTER_SCANNER)))
1752       cupsdRegisterPrinter(p);
1753 }
1754
1755
1756 /*
1757  * 'cupsdStartPolling()' - Start polling servers as needed.
1758  */
1759
1760 void
1761 cupsdStartPolling(void)
1762 {
1763   int                   i;              /* Looping var */
1764   cupsd_dirsvc_poll_t   *pollp;         /* Current polling server */
1765   char                  polld[1024];    /* Poll daemon path */
1766   char                  sport[255];     /* Server port */
1767   char                  bport[255];     /* Browser port */
1768   char                  interval[255];  /* Poll interval */
1769   int                   statusfds[2];   /* Status pipe */
1770   char                  *argv[6];       /* Arguments */
1771   char                  *envp[100];     /* Environment */
1772
1773
1774  /*
1775   * Don't do anything if we aren't polling...
1776   */
1777
1778   if (NumPolled == 0 || BrowseSocket < 0)
1779   {
1780     PollPipe         = -1;
1781     PollStatusBuffer = NULL;
1782     return;
1783   }
1784
1785  /*
1786   * Setup string arguments for polld, port and interval options.
1787   */
1788
1789   snprintf(polld, sizeof(polld), "%s/daemon/cups-polld", ServerBin);
1790
1791   sprintf(bport, "%d", BrowsePort);
1792
1793   if (BrowseInterval)
1794     sprintf(interval, "%d", BrowseInterval);
1795   else
1796     strcpy(interval, "30");
1797
1798   argv[0] = "cups-polld";
1799   argv[2] = sport;
1800   argv[3] = interval;
1801   argv[4] = bport;
1802   argv[5] = NULL;
1803
1804   cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
1805
1806  /*
1807   * Create a pipe that receives the status messages from each
1808   * polling daemon...
1809   */
1810
1811   if (cupsdOpenPipe(statusfds))
1812   {
1813     cupsdLogMessage(CUPSD_LOG_ERROR,
1814                     "Unable to create polling status pipes - %s.",
1815                     strerror(errno));
1816     PollPipe         = -1;
1817     PollStatusBuffer = NULL;
1818     return;
1819   }
1820
1821   PollPipe         = statusfds[0];
1822   PollStatusBuffer = cupsdStatBufNew(PollPipe, "[Poll]");
1823
1824  /*
1825   * Run each polling daemon, redirecting stderr to the polling pipe...
1826   */
1827
1828   for (i = 0, pollp = Polled; i < NumPolled; i ++, pollp ++)
1829   {
1830     sprintf(sport, "%d", pollp->port);
1831
1832     argv[1] = pollp->hostname;
1833
1834     if (cupsdStartProcess(polld, argv, envp, -1, -1, statusfds[1], -1, -1,
1835                           0, DefaultProfile, NULL, &(pollp->pid)) < 0)
1836     {
1837       cupsdLogMessage(CUPSD_LOG_ERROR,
1838                       "cupsdStartPolling: Unable to fork polling daemon - %s",
1839                       strerror(errno));
1840       pollp->pid = 0;
1841       break;
1842     }
1843     else
1844       cupsdLogMessage(CUPSD_LOG_DEBUG,
1845                       "cupsdStartPolling: Started polling daemon for %s:%d, pid = %d",
1846                       pollp->hostname, pollp->port, pollp->pid);
1847   }
1848
1849   close(statusfds[1]);
1850
1851  /*
1852   * Finally, add the pipe to the input selection set...
1853   */
1854
1855   cupsdAddSelect(PollPipe, (cupsd_selfunc_t)update_polling, NULL, NULL);
1856 }
1857
1858
1859 /*
1860  * 'cupsdStopBrowsing()' - Stop sending and receiving broadcast information.
1861  */
1862
1863 void
1864 cupsdStopBrowsing(void)
1865 {
1866   cupsd_printer_t       *p;             /* Current printer */
1867
1868
1869   if (!Browsing || !(BrowseLocalProtocols | BrowseRemoteProtocols))
1870     return;
1871
1872  /*
1873   * De-register the individual printers
1874   */
1875
1876   for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
1877        p;
1878        p = (cupsd_printer_t *)cupsArrayNext(Printers))
1879     if (!(p->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT |
1880                      CUPS_PRINTER_SCANNER)))
1881       cupsdDeregisterPrinter(p, 1);
1882
1883  /*
1884   * Shut down browsing sockets...
1885   */
1886
1887   if (((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_CUPS) &&
1888       BrowseSocket >= 0)
1889   {
1890    /*
1891     * Close the socket and remove it from the input selection set.
1892     */
1893
1894 #ifdef WIN32
1895     closesocket(BrowseSocket);
1896 #else
1897     close(BrowseSocket);
1898 #endif /* WIN32 */
1899
1900     cupsdRemoveSelect(BrowseSocket);
1901     BrowseSocket = -1;
1902   }
1903
1904 #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
1905   if ((BrowseLocalProtocols & BROWSE_DNSSD))
1906     dnssdStop();
1907 #endif /* defined(HAVE_DNSSD) || defined(HAVE_AVAHI) */
1908
1909 #ifdef HAVE_LIBSLP
1910   if (((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_SLP) &&
1911       BrowseSLPHandle)
1912   {
1913    /*
1914     * Close SLP handle...
1915     */
1916
1917     SLPClose(BrowseSLPHandle);
1918     BrowseSLPHandle = NULL;
1919   }
1920 #endif /* HAVE_LIBSLP */
1921
1922 #ifdef HAVE_LDAP
1923   if (((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_LDAP) &&
1924       BrowseLDAPHandle)
1925   {
1926     ldap_dereg_ou(ServerName, BrowseLDAPDN);
1927     ldap_disconnect(BrowseLDAPHandle);
1928     BrowseLDAPHandle = NULL;
1929   }
1930 #endif /* HAVE_OPENLDAP */
1931
1932  /*
1933   * Disable LPD and SMB printer sharing as needed through external programs...
1934   */
1935
1936   if (BrowseLocalProtocols & BROWSE_LPD)
1937     update_lpd(0);
1938
1939   if (BrowseLocalProtocols & BROWSE_SMB)
1940     update_smb(0);
1941 }
1942
1943
1944 /*
1945  * 'cupsdStopPolling()' - Stop polling servers as needed.
1946  */
1947
1948 void
1949 cupsdStopPolling(void)
1950 {
1951   int                   i;              /* Looping var */
1952   cupsd_dirsvc_poll_t   *pollp;         /* Current polling server */
1953
1954
1955   if (PollPipe >= 0)
1956   {
1957     cupsdStatBufDelete(PollStatusBuffer);
1958     close(PollPipe);
1959
1960     cupsdRemoveSelect(PollPipe);
1961
1962     PollPipe         = -1;
1963     PollStatusBuffer = NULL;
1964   }
1965
1966   for (i = 0, pollp = Polled; i < NumPolled; i ++, pollp ++)
1967     if (pollp->pid)
1968       cupsdEndProcess(pollp->pid, 0);
1969 }
1970
1971
1972 #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
1973 /*
1974  * 'cupsdUpdateDNSSDName()' - Update the computer name we use for browsing...
1975  */
1976
1977 void
1978 cupsdUpdateDNSSDName(void)
1979 {
1980 #ifdef HAVE_DNSSD
1981   DNSServiceErrorType error;            /* Error from service creation */
1982   char          webif[1024];            /* Web interface share name */
1983 #endif /* HAVE_DNSSD */
1984 #ifdef HAVE_AVAHI
1985   int           ret;                    /* Error from service creation */
1986   char          webif[AVAHI_LABEL_MAX]; /* Web interface share name */
1987 #endif /* HAVE_AVAHI */
1988 #  ifdef HAVE_SYSTEMCONFIGURATION
1989   SCDynamicStoreRef sc;                 /* Context for dynamic store */
1990   CFDictionaryRef btmm;                 /* Back-to-My-Mac domains */
1991   CFStringEncoding nameEncoding;        /* Encoding of computer name */
1992   CFStringRef   nameRef;                /* Host name CFString */
1993   char          nameBuffer[1024];       /* C-string buffer */
1994 #  endif /* HAVE_SYSTEMCONFIGURATION */
1995
1996
1997  /*
1998   * Only share the web interface and printers when non-local listening is
1999   * enabled...
2000   */
2001
2002
2003   if (!DNSSDPort)
2004     return;
2005
2006  /*
2007   * Get the computer name as a c-string...
2008   */
2009
2010 #  ifdef HAVE_SYSTEMCONFIGURATION
2011   sc = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("cupsd"), NULL, NULL);
2012
2013   if (sc)
2014   {
2015    /*
2016     * Get the computer name from the dynamic store...
2017     */
2018
2019     cupsdClearString(&DNSSDComputerName);
2020
2021     if ((nameRef = SCDynamicStoreCopyComputerName(sc, &nameEncoding)) != NULL)
2022     {
2023       if (CFStringGetCString(nameRef, nameBuffer, sizeof(nameBuffer),
2024                              kCFStringEncodingUTF8))
2025       {
2026         cupsdLogMessage(CUPSD_LOG_DEBUG,
2027                         "Dynamic store computer name is \"%s\".", nameBuffer);
2028         cupsdSetString(&DNSSDComputerName, nameBuffer);
2029       }
2030
2031       CFRelease(nameRef);
2032     }
2033
2034     if (!DNSSDComputerName)
2035     {
2036      /*
2037       * Use the ServerName instead...
2038       */
2039
2040       cupsdLogMessage(CUPSD_LOG_DEBUG,
2041                       "Using ServerName \"%s\" as computer name.", ServerName);
2042       cupsdSetString(&DNSSDComputerName, ServerName);
2043     }
2044
2045    /*
2046     * Get the local hostname from the dynamic store...
2047     */
2048
2049     cupsdClearString(&DNSSDHostName);
2050
2051     if ((nameRef = SCDynamicStoreCopyLocalHostName(sc)) != NULL)
2052     {
2053       if (CFStringGetCString(nameRef, nameBuffer, sizeof(nameBuffer),
2054                              kCFStringEncodingUTF8))
2055       {
2056         cupsdLogMessage(CUPSD_LOG_DEBUG,
2057                         "Dynamic store host name is \"%s\".", nameBuffer);
2058         cupsdSetString(&DNSSDHostName, nameBuffer);
2059       }
2060
2061       CFRelease(nameRef);
2062     }
2063
2064     if (!DNSSDHostName)
2065     {
2066      /*
2067       * Use the ServerName instead...
2068       */
2069
2070       cupsdLogMessage(CUPSD_LOG_DEBUG,
2071                       "Using ServerName \"%s\" as host name.", ServerName);
2072       cupsdSetString(&DNSSDHostName, ServerName);
2073     }
2074
2075    /*
2076     * Get any Back-to-My-Mac domains and add them as aliases...
2077     */
2078
2079     cupsdFreeAliases(DNSSDAlias);
2080     DNSSDAlias = NULL;
2081
2082     btmm = SCDynamicStoreCopyValue(sc, CFSTR("Setup:/Network/BackToMyMac"));
2083     if (btmm && CFGetTypeID(btmm) == CFDictionaryGetTypeID())
2084     {
2085       cupsdLogMessage(CUPSD_LOG_DEBUG, "%d Back to My Mac aliases to add.",
2086                       (int)CFDictionaryGetCount(btmm));
2087       CFDictionaryApplyFunction(btmm, dnssdAddAlias, NULL);
2088     }
2089     else if (btmm)
2090       cupsdLogMessage(CUPSD_LOG_ERROR,
2091                       "Bad Back to My Mac data in dynamic store!");
2092     else
2093       cupsdLogMessage(CUPSD_LOG_DEBUG, "No Back to My Mac aliases to add.");
2094
2095     if (btmm)
2096       CFRelease(btmm);
2097
2098     CFRelease(sc);
2099   }
2100   else
2101 #  endif /* HAVE_SYSTEMCONFIGURATION */
2102   {
2103     cupsdSetString(&DNSSDComputerName, ServerName);
2104     cupsdSetString(&DNSSDHostName, ServerName);
2105   }
2106
2107  /*
2108   * Then (re)register the web interface if enabled...
2109   */
2110
2111   if (BrowseWebIF)
2112   {
2113     if (DNSSDComputerName)
2114       snprintf(webif, sizeof(webif), "CUPS @ %s", DNSSDComputerName);
2115     else
2116       strlcpy(webif, "CUPS Web Interface", sizeof(webif));
2117
2118 #ifdef HAVE_DNSSD
2119     if (WebIFRef)
2120       DNSServiceRefDeallocate(WebIFRef);
2121
2122     WebIFRef = DNSSDRef;
2123     if ((error = DNSServiceRegister(&WebIFRef,
2124                                     kDNSServiceFlagsShareConnection,
2125                                     0, webif, "_http._tcp", NULL,
2126                                     NULL, htons(DNSSDPort), 7,
2127                                     "\006path=/", dnssdRegisterCallback,
2128                                     NULL)) != kDNSServiceErr_NoError)
2129       cupsdLogMessage(CUPSD_LOG_ERROR,
2130                       "DNS-SD web interface registration failed: %d", error);
2131 #endif /* HAVE_DNSSD */
2132
2133 #ifdef HAVE_AVAHI
2134     if (!AvahiCupsClient)
2135      /*
2136       * Client not yet running.
2137       */
2138       return;
2139
2140     if (AvahiWebIFGroup)
2141       avahi_entry_group_reset (AvahiWebIFGroup);
2142     else
2143       AvahiWebIFGroup = avahi_entry_group_new (AvahiCupsClient,
2144                                                avahi_entry_group_cb,
2145                                                NULL);
2146
2147     if (AvahiWebIFGroup)
2148     {
2149       ret = avahi_entry_group_add_service (AvahiWebIFGroup,
2150                                            AVAHI_IF_UNSPEC,
2151                                            AVAHI_PROTO_UNSPEC,
2152                                            0, /* flags */
2153                                            webif, /* name */
2154                                            "_http._tcp", /* type */
2155                                            NULL, /* domain */
2156                                            NULL, /* host */
2157                                            DNSSDPort, /* port */
2158                                            "path=/", NULL);
2159       if (ret == 0)
2160         ret = avahi_entry_group_commit (AvahiWebIFGroup);
2161
2162       if (ret != 0)
2163         cupsdLogMessage (CUPSD_LOG_ERROR,
2164                          "Avahi web interface registration failed: %d", ret);
2165     }
2166 #endif /* HAVE_AVAHI */
2167   }
2168 }
2169 #endif /* defined(HAVE_DNSSD) || defined(HAVE_AVAHI) */
2170
2171
2172 #ifdef HAVE_LDAP
2173 /*
2174  * 'cupsdUpdateLDAPBrowse()' - Scan for new printers via LDAP...
2175  */
2176
2177 void
2178 cupsdUpdateLDAPBrowse(void)
2179 {
2180   char          uri[HTTP_MAX_URI],      /* Printer URI */
2181                 host[HTTP_MAX_URI],     /* Hostname */
2182                 resource[HTTP_MAX_URI], /* Resource path */
2183                 location[1024],         /* Printer location */
2184                 info[1024],             /* Printer information */
2185                 make_model[1024],       /* Printer make and model */
2186                 type_num[30];           /* Printer type number */
2187   int           type;                   /* Printer type */
2188   int           rc;                     /* LDAP status */
2189   int           limit;                  /* Size limit */
2190   LDAPMessage   *res,                   /* LDAP search results */
2191                   *e;                   /* Current entry from search */
2192
2193   cupsdLogMessage(CUPSD_LOG_DEBUG2, "UpdateLDAPBrowse: %s", ServerName);
2194
2195   BrowseLDAPRefresh = time(NULL) + BrowseInterval;
2196
2197  /*
2198   * Reconnect if LDAP Handle is invalid...
2199   */
2200
2201   if (! BrowseLDAPHandle)
2202   {
2203     ldap_reconnect();
2204     return;
2205   }
2206
2207  /*
2208   * Search for cups printers in LDAP directory...
2209   */
2210
2211   rc = ldap_search_rec(BrowseLDAPHandle, BrowseLDAPDN, LDAP_SCOPE_SUBTREE,
2212                        "(objectclass=cupsPrinter)", (char **)ldap_attrs, 0, &res);
2213
2214  /*
2215   * If ldap search was successfull then exit function
2216   * and temporary disable LDAP updates...
2217   */
2218
2219   if (rc != LDAP_SUCCESS)
2220   {
2221     if (BrowseLDAPUpdate && ((rc == LDAP_SERVER_DOWN) || (rc == LDAP_CONNECT_ERROR)))
2222     {
2223       BrowseLDAPUpdate = FALSE;
2224       cupsdLogMessage(CUPSD_LOG_INFO,
2225                       "LDAP update temporary disabled");
2226     }
2227     return;
2228   }
2229
2230  /*
2231   * If LDAP updates were disabled, we will reenable them...
2232   */
2233
2234   if (! BrowseLDAPUpdate)
2235   {
2236     BrowseLDAPUpdate = TRUE;
2237     cupsdLogMessage(CUPSD_LOG_INFO,
2238                     "LDAP update enabled");
2239   }
2240
2241  /*
2242   * Count LDAP entries and return if no entry exist...
2243   */
2244
2245   limit = ldap_count_entries(BrowseLDAPHandle, res);
2246   cupsdLogMessage(CUPSD_LOG_DEBUG2, "LDAP search returned %d entries", limit);
2247   if (limit < 1)
2248   {
2249     ldap_freeres(res);
2250     return;
2251   }
2252
2253  /*
2254   * Loop through the available printers...
2255   */
2256
2257   for (e = ldap_first_entry(BrowseLDAPHandle, res);
2258        e;
2259        e = ldap_next_entry(BrowseLDAPHandle, e))
2260   {
2261    /*
2262     * Get the required values from this entry...
2263     */
2264
2265     if (ldap_getval_firststring(BrowseLDAPHandle, e,
2266                                 "printerDescription", info, sizeof(info)) == -1)
2267       continue;
2268
2269     if (ldap_getval_firststring(BrowseLDAPHandle, e,
2270                                 "printerLocation", location, sizeof(location)) == -1)
2271       continue;
2272
2273     if (ldap_getval_firststring(BrowseLDAPHandle, e,
2274                                 "printerMakeAndModel", make_model, sizeof(make_model)) == -1)
2275       continue;
2276
2277     if (ldap_getval_firststring(BrowseLDAPHandle, e,
2278                                 "printerType", type_num, sizeof(type_num)) == -1)
2279       continue;
2280
2281     type = atoi(type_num);
2282
2283     if (ldap_getval_firststring(BrowseLDAPHandle, e,
2284                                 "printerURI", uri, sizeof(uri)) == -1)
2285       continue;
2286
2287    /*
2288     * Process the entry as browse data...
2289     */
2290
2291     if (!is_local_queue(uri, host, sizeof(host), resource, sizeof(resource)))
2292       process_browse_data(uri, host, resource, type, IPP_PRINTER_IDLE,
2293                           location, info, make_model, 0, NULL);
2294
2295   }
2296
2297   ldap_freeres(res);
2298 }
2299 #endif /* HAVE_LDAP */
2300
2301
2302 #ifdef HAVE_LIBSLP
2303 /*
2304  * 'cupsdUpdateSLPBrowse()' - Get browsing information via SLP.
2305  */
2306
2307 void
2308 cupsdUpdateSLPBrowse(void)
2309 {
2310   slpsrvurl_t   *s,                     /* Temporary list of service URLs */
2311                 *next;                  /* Next service in list */
2312   cupsd_printer_t p;                    /* Printer information */
2313   const char    *uri;                   /* Pointer to printer URI */
2314   char          host[HTTP_MAX_URI],     /* Host portion of URI */
2315                 resource[HTTP_MAX_URI]; /* Resource portion of URI */
2316
2317
2318  /*
2319   * Reset the refresh time...
2320   */
2321
2322   BrowseSLPRefresh = time(NULL) + BrowseInterval;
2323
2324  /*
2325   * Poll for remote printers using SLP...
2326   */
2327
2328   s = NULL;
2329
2330   SLPFindSrvs(BrowseSLPHandle, SLP_CUPS_SRVTYPE, "", "",
2331               slp_url_callback, &s);
2332
2333  /*
2334   * Loop through the list of available printers...
2335   */
2336
2337   for (; s; s = next)
2338   {
2339    /*
2340     * Save the "next" pointer...
2341     */
2342
2343     next = s->next;
2344
2345    /*
2346     * Load a cupsd_printer_t structure with the SLP service attributes...
2347     */
2348
2349     SLPFindAttrs(BrowseSLPHandle, s->url, "", "", slp_attr_callback, &p);
2350
2351    /*
2352     * Process this printer entry...
2353     */
2354
2355     uri = s->url + SLP_CUPS_SRVLEN + 1;
2356
2357     if (!strncmp(uri, "http://", 7) || !strncmp(uri, "ipp://", 6))
2358     {
2359      /*
2360       * Pull the URI apart to see if this is a local or remote printer...
2361       */
2362
2363       if (!is_local_queue(uri, host, sizeof(host), resource, sizeof(resource)))
2364         process_browse_data(uri, host, resource, p.type, IPP_PRINTER_IDLE,
2365                             p.location,  p.info, p.make_model, 0, NULL);
2366     }
2367
2368    /*
2369     * Free this listing...
2370     */
2371
2372     cupsdClearString(&p.info);
2373     cupsdClearString(&p.location);
2374     cupsdClearString(&p.make_model);
2375
2376     free(s);
2377   }
2378 }
2379 #endif /* HAVE_LIBSLP */
2380
2381
2382 /*
2383  * 'dequote()' - Remote quotes from a string.
2384  */
2385
2386 static char *                           /* O - Dequoted string */
2387 dequote(char       *d,                  /* I - Destination string */
2388         const char *s,                  /* I - Source string */
2389         int        dlen)                /* I - Destination length */
2390 {
2391   char  *dptr;                          /* Pointer into destination */
2392
2393
2394   if (s)
2395   {
2396     for (dptr = d, dlen --; *s && dlen > 0; s ++)
2397       if (*s != '\"')
2398       {
2399         *dptr++ = *s;
2400         dlen --;
2401       }
2402
2403     *dptr = '\0';
2404   }
2405   else
2406     *d = '\0';
2407
2408   return (d);
2409 }
2410
2411
2412 #ifdef HAVE_DNSSD
2413 #  ifdef HAVE_COREFOUNDATION
2414 /*
2415  * 'dnssdAddAlias()' - Add a DNS-SD alias name.
2416  */
2417
2418 static void
2419 dnssdAddAlias(const void *key,          /* I - Key */
2420               const void *value,        /* I - Value (domain) */
2421               void       *context)      /* I - Unused */
2422 {
2423   char  valueStr[1024],                 /* Domain string */
2424         hostname[1024],                 /* Complete hostname */
2425         *hostptr;                       /* Pointer into hostname */
2426
2427
2428   (void)key;
2429   (void)context;
2430
2431   if (CFGetTypeID((CFStringRef)value) == CFStringGetTypeID() &&
2432       CFStringGetCString((CFStringRef)value, valueStr, sizeof(valueStr),
2433                          kCFStringEncodingUTF8))
2434   {
2435     snprintf(hostname, sizeof(hostname), "%s.%s", DNSSDHostName, valueStr);
2436     hostptr = hostname + strlen(hostname) - 1;
2437     if (*hostptr == '.')
2438       *hostptr = '\0';                  /* Strip trailing dot */
2439
2440     if (!DNSSDAlias)
2441       DNSSDAlias = cupsArrayNew(NULL, NULL);
2442
2443     cupsdAddAlias(DNSSDAlias, hostname);
2444     cupsdLogMessage(CUPSD_LOG_DEBUG, "Added Back to My Mac ServerAlias %s",
2445                     hostname);
2446   }
2447   else
2448     cupsdLogMessage(CUPSD_LOG_ERROR,
2449                     "Bad Back to My Mac domain in dynamic store!");
2450 }
2451 #  endif /* HAVE_COREFOUNDATION */
2452 #endif /* HAVE_DNSSD */
2453
2454
2455 #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
2456 /*
2457  * 'dnssdBuildTxtRecord()' - Build a TXT record from printer info.
2458  */
2459
2460 static cupsd_txt_record_t               /* O - TXT record */
2461 dnssdBuildTxtRecord(
2462     int             *txt_len,           /* O - TXT record length */
2463     cupsd_printer_t *p,                 /* I - Printer information */
2464     int             for_lpd)            /* I - 1 = LPD, 0 = IPP */
2465 {
2466   int           i;                      /* Looping var */
2467   char          admin_hostname[256],    /* .local hostname for admin page */
2468                 adminurl_str[256],      /* URL for the admin page */
2469                 type_str[32],           /* Type to string buffer */
2470                 state_str[32],          /* State to string buffer */
2471                 rp_str[1024],           /* Queue name string buffer */
2472                 air_str[1024],          /* auth-info-required string buffer */
2473                 *keyvalue[32][2];       /* Table of key/value pairs */
2474
2475
2476  /*
2477   * Load up the key value pairs...
2478   */
2479
2480   i = 0;
2481
2482   keyvalue[i  ][0] = "txtvers";
2483   keyvalue[i++][1] = "1";
2484
2485   keyvalue[i  ][0] = "qtotal";
2486   keyvalue[i++][1] = "1";
2487
2488   keyvalue[i  ][0] = "rp";
2489   keyvalue[i++][1] = rp_str;
2490   if (for_lpd)
2491     strlcpy(rp_str, p->name, sizeof(rp_str));
2492   else
2493     snprintf(rp_str, sizeof(rp_str), "%s/%s",
2494              (p->type & CUPS_PRINTER_CLASS) ? "classes" : "printers", p->name);
2495
2496   keyvalue[i  ][0] = "ty";
2497   keyvalue[i++][1] = p->make_model ? p->make_model : "Unknown";
2498
2499   snprintf(admin_hostname, sizeof(admin_hostname),
2500            "%s.local"
2501 #ifdef HAVE_DNSSD
2502            "." /* terminating dot no good for Avahi */
2503 #endif /* HAVE_DNSSD */
2504            , DNSSDHostName);
2505   httpAssembleURIf(HTTP_URI_CODING_ALL, adminurl_str, sizeof(adminurl_str),
2506                    "http", NULL, admin_hostname, DNSSDPort, "/%s/%s",
2507                    (p->type & CUPS_PRINTER_CLASS) ? "classes" : "printers",
2508                    p->name);
2509   keyvalue[i  ][0] = "adminurl";
2510   keyvalue[i++][1] = adminurl_str;
2511
2512   keyvalue[i  ][0] = "note";
2513   keyvalue[i++][1] = p->location ? p->location : "";
2514
2515   keyvalue[i  ][0] = "priority";
2516   keyvalue[i++][1] = for_lpd ? "100" : "0";
2517
2518   keyvalue[i  ][0] = "product";
2519   keyvalue[i++][1] = p->pc && p->pc->product ? p->pc->product : "Unknown";
2520
2521   keyvalue[i  ][0] = "pdl";
2522   keyvalue[i++][1] = p->pdl ? p->pdl : "application/postscript";
2523
2524   keyvalue[i  ][0] = "URF";
2525   keyvalue[i++][1] = "none";
2526
2527   if (get_auth_info_required(p, air_str, sizeof(air_str)))
2528   {
2529     keyvalue[i  ][0] = "air";
2530     keyvalue[i++][1] = air_str;
2531   }
2532
2533   keyvalue[i  ][0] = "UUID";
2534   keyvalue[i++][1] = p->uuid + 9;
2535
2536 #ifdef HAVE_SSL
2537   keyvalue[i  ][0] = "TLS";
2538   keyvalue[i++][1] = "1.2";
2539 #endif /* HAVE_SSL */
2540
2541   keyvalue[i  ][0] = "Transparent";
2542   keyvalue[i++][1] = "F";
2543
2544   keyvalue[i  ][0] = "Binary";
2545   keyvalue[i++][1] = "F";
2546
2547   keyvalue[i  ][0] = "Fax";
2548   keyvalue[i++][1] = (p->type & CUPS_PRINTER_FAX) ? "T" : "F";
2549
2550   keyvalue[i  ][0] = "Color";
2551   keyvalue[i++][1] = (p->type & CUPS_PRINTER_COLOR) ? "T" : "F";
2552
2553   keyvalue[i  ][0] = "Duplex";
2554   keyvalue[i++][1] = (p->type & CUPS_PRINTER_DUPLEX) ? "T" : "F";
2555
2556   keyvalue[i  ][0] = "Staple";
2557   keyvalue[i++][1] = (p->type & CUPS_PRINTER_STAPLE) ? "T" : "F";
2558
2559   keyvalue[i  ][0] = "Copies";
2560   keyvalue[i++][1] = (p->type & CUPS_PRINTER_COPIES) ? "T" : "F";
2561
2562   keyvalue[i  ][0] = "Collate";
2563   keyvalue[i++][1] = (p->type & CUPS_PRINTER_COLLATE) ? "T" : "F";
2564
2565   keyvalue[i  ][0] = "Punch";
2566   keyvalue[i++][1] = (p->type & CUPS_PRINTER_PUNCH) ? "T" : "F";
2567
2568   keyvalue[i  ][0] = "Bind";
2569   keyvalue[i++][1] = (p->type & CUPS_PRINTER_BIND) ? "T" : "F";
2570
2571   keyvalue[i  ][0] = "Sort";
2572   keyvalue[i++][1] = (p->type & CUPS_PRINTER_SORT) ? "T" : "F";
2573
2574   keyvalue[i  ][0] = "Scan";
2575   keyvalue[i++][1] = (p->type & CUPS_PRINTER_MFP) ? "T" : "F";
2576
2577   snprintf(type_str, sizeof(type_str), "0x%X", p->type | CUPS_PRINTER_REMOTE);
2578   snprintf(state_str, sizeof(state_str), "%d", p->state);
2579
2580   keyvalue[i  ][0] = "printer-state";
2581   keyvalue[i++][1] = state_str;
2582
2583   keyvalue[i  ][0] = "printer-type";
2584   keyvalue[i++][1] = type_str;
2585
2586  /*
2587   * Then pack them into a proper txt record...
2588   */
2589
2590 #ifdef HAVE_DNSSD
2591   return (dnssdPackTxtRecord(txt_len, keyvalue, i));
2592 #endif /* HAVE_DNSSD */
2593 #ifdef HAVE_AVAHI
2594   return (avahiPackTxtRecord(keyvalue, i));
2595 #endif /* HAVE_AVAHI */
2596 }
2597
2598
2599 /*
2600  * 'dnssdComparePrinters()' - Compare the registered names of two printers.
2601  */
2602
2603 static int                              /* O - Result of comparison */
2604 dnssdComparePrinters(cupsd_printer_t *a,/* I - First printer */
2605                      cupsd_printer_t *b)/* I - Second printer */
2606 {
2607   if (!a->reg_name)
2608     if (!b->reg_name)
2609       return 0;
2610     else
2611       return -1;
2612   else
2613     if (!b->reg_name)
2614       return 1;
2615     else
2616       return (_cups_strcasecmp(a->reg_name, b->reg_name));
2617 }
2618
2619
2620 /*
2621  * 'dnssdDeregisterPrinter()' - Stop sending broadcast information for a
2622  *                              printer.
2623  */
2624
2625 static void
2626 dnssdDeregisterPrinter(
2627     cupsd_printer_t *p)                 /* I - Printer */
2628 {
2629   cupsdLogMessage(CUPSD_LOG_DEBUG2, "dnssdDeregisterPrinter(%s)", p->name);
2630
2631 #ifdef HAVE_DNSSD
2632   if (!DNSSDRef)
2633     return;
2634
2635  /*
2636   * Closing the socket deregisters the service
2637   */
2638
2639   if (p->ipp_ref)
2640   {
2641     DNSServiceRefDeallocate(p->ipp_ref);
2642     p->ipp_ref = NULL;
2643   }
2644
2645   if (p->ipp_txt)
2646   {
2647    /*
2648     * p->ipp_txt is malloc'd, not _cupsStrAlloc'd...
2649     */
2650
2651     free(p->ipp_txt);
2652     p->ipp_txt = NULL;
2653   }
2654
2655   if (p->printer_ref)
2656   {
2657     DNSServiceRefDeallocate(p->printer_ref);
2658     p->printer_ref = NULL;
2659   }
2660
2661   if (p->printer_txt)
2662   {
2663    /*
2664     * p->printer_txt is malloc'd, not _cupsStrAlloc'd...
2665     */
2666
2667     free(p->printer_txt);
2668     p->printer_txt = NULL;
2669   }
2670 #endif /* HAVE_DNSSD */
2671
2672 #ifdef HAVE_AVAHI
2673   if (p->avahi_group)
2674     {
2675       avahi_entry_group_reset (p->avahi_group);
2676       avahi_entry_group_free (p->avahi_group);
2677       p->avahi_group = NULL;
2678
2679       if (p->ipp_txt)
2680         avahi_string_list_free (p->ipp_txt);
2681
2682       if (p->printer_txt)
2683         avahi_string_list_free (p->printer_txt);
2684
2685       p->ipp_txt = p->printer_txt = NULL;
2686     }
2687 #endif /* HAVE_AVAHI */
2688
2689  /*
2690   * Remove the printer from the array of DNS-SD printers, then clear the
2691   * registered name...
2692   */
2693
2694   cupsArrayRemove(DNSSDPrinters, p);
2695   cupsdClearString(&p->reg_name);
2696 }
2697 #endif /* defined(HAVE_DNSSD) || defined(HAVE_AVAHI) */
2698
2699
2700 #ifdef HAVE_DNSSD
2701 /*
2702  * 'dnssdPackTxtRecord()' - Pack an array of key/value pairs into the
2703  *                          TXT record format.
2704  */
2705
2706 static char *                           /* O - TXT record */
2707 dnssdPackTxtRecord(int  *txt_len,       /* O - TXT record length */
2708                    char *keyvalue[][2], /* I - Table of key value pairs */
2709                    int  count)          /* I - Items in table */
2710 {
2711   int  i;                               /* Looping var */
2712   int  length;                          /* Length of TXT record */
2713   int  length2;                         /* Length of value */
2714   char *txtRecord;                      /* TXT record buffer */
2715   char *cursor;                         /* Looping pointer */
2716
2717
2718  /*
2719   * Calculate the buffer size
2720   */
2721
2722   if (count <= 0)
2723     return (NULL);
2724
2725   for (length = i = 0; i < count; i++)
2726     length += 1 + strlen(keyvalue[i][0]) +
2727               (keyvalue[i][1] ? 1 + strlen(keyvalue[i][1]) : 0);
2728
2729  /*
2730   * Allocate and fill it
2731   */
2732
2733   txtRecord = malloc(length);
2734   if (txtRecord)
2735   {
2736     *txt_len = length;
2737
2738     for (cursor = txtRecord, i = 0; i < count; i++)
2739     {
2740      /*
2741       * Drop in the p-string style length byte followed by the data
2742       */
2743
2744       length  = strlen(keyvalue[i][0]);
2745       length2 = keyvalue[i][1] ? 1 + strlen(keyvalue[i][1]) : 0;
2746
2747       *cursor++ = (unsigned char)(length + length2);
2748
2749       memcpy(cursor, keyvalue[i][0], length);
2750       cursor += length;
2751
2752       if (length2)
2753       {
2754         length2 --;
2755         *cursor++ = '=';
2756         memcpy(cursor, keyvalue[i][1], length2);
2757         cursor += length2;
2758       }
2759     }
2760   }
2761
2762   return (txtRecord);
2763 }
2764
2765
2766 /*
2767  * 'dnssdRegisterCallback()' - DNSServiceRegister callback.
2768  */
2769
2770 static void
2771 dnssdRegisterCallback(
2772     DNSServiceRef       sdRef,          /* I - DNS Service reference */
2773     DNSServiceFlags     flags,          /* I - Reserved for future use */
2774     DNSServiceErrorType errorCode,      /* I - Error code */
2775     const char          *name,          /* I - Service name */
2776     const char          *regtype,       /* I - Service type */
2777     const char          *domain,        /* I - Domain. ".local" for now */
2778     void                *context)       /* I - User-defined context */
2779 {
2780   cupsd_printer_t *p = (cupsd_printer_t *)context;
2781                                         /* Current printer */
2782
2783
2784   (void)sdRef;
2785   (void)flags;
2786   (void)domain;
2787
2788   cupsdLogMessage(CUPSD_LOG_DEBUG2, "dnssdRegisterCallback(%s, %s) for %s (%s)",
2789                   name, regtype, p ? p->name : "Web Interface",
2790                   p ? (p->reg_name ? p->reg_name : "(null)") : "NA");
2791
2792   if (errorCode)
2793   {
2794     cupsdLogMessage(CUPSD_LOG_ERROR,
2795                     "DNSServiceRegister failed with error %d", (int)errorCode);
2796     return;
2797   }
2798   else if (p && (!p->reg_name || _cups_strcasecmp(name, p->reg_name)))
2799   {
2800     cupsdLogMessage(CUPSD_LOG_INFO, "Using service name \"%s\" for \"%s\"",
2801                     name, p->name);
2802
2803     cupsArrayRemove(DNSSDPrinters, p);
2804     cupsdSetString(&p->reg_name, name);
2805     cupsArrayAdd(DNSSDPrinters, p);
2806
2807     LastEvent |= CUPSD_EVENT_PRINTER_MODIFIED;
2808   }
2809 }
2810 #endif /* HAVE_DNSSD */
2811
2812
2813 #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
2814 /*
2815  * 'dnssdRegisterPrinter()' - Start sending broadcast information for a printer
2816  *                            or update the broadcast contents.
2817  */
2818
2819 static void
2820 dnssdRegisterPrinter(cupsd_printer_t *p)/* I - Printer */
2821 {
2822 #ifdef HAVE_DNSSD
2823   DNSServiceErrorType   se;             /* dnssd errors */
2824   char                  *ipp_txt,       /* IPP TXT record buffer */
2825                         *printer_txt,   /* LPD TXT record buffer */
2826                         name[1024];     /* Service name */
2827   int                   ipp_len,        /* IPP TXT record length */
2828                         printer_len,    /* LPD TXT record length */
2829                         printer_port;   /* LPD port number */
2830 #endif /* HAVE_DNSSD */
2831 #ifdef HAVE_AVAHI
2832   int                   ret;            /* Error code */
2833   AvahiStringList       *ipp_txt,       /* IPP TXT record */
2834                         *printer_txt;   /* LPD TXT record */
2835   char                  name[AVAHI_LABEL_MAX],  /* Service name */
2836                         fullsubtype[AVAHI_LABEL_MAX]; /* Full subtype */
2837   char                  *regtype_copy,  /* Writeable copy of reg type */
2838                         *subtype,       /* Current service sub type */
2839                         *nextsubtype;   /* Next service sub type */
2840 #endif /* HAVE_AVAHI */
2841   char                  *nameptr;       /* Pointer into name */
2842   const char            *regtype;       /* Registration type */
2843
2844
2845 #ifdef HAVE_DNSSD
2846   if (!DNSSDRef)
2847     return;
2848
2849   cupsdLogMessage(CUPSD_LOG_DEBUG2, "dnssdRegisterPrinter(%s) %s", p->name,
2850                   !p->ipp_ref ? "new" : "update");
2851 #endif /* HAVE_DNSSD */
2852 #ifdef HAVE_AVAHI
2853   cupsdLogMessage(CUPSD_LOG_DEBUG2, "dnssdRegisterPrinter(%s) %s", p->name,
2854                   !p->avahi_group ? "new" : "update");
2855 #endif /* HAVE_AVAHI */
2856  /*
2857   * If per-printer sharing was just disabled make sure we're not
2858   * registered before returning.
2859   */
2860
2861   if (!p->shared)
2862   {
2863     dnssdDeregisterPrinter(p);
2864     return;
2865   }
2866
2867  /*
2868   * The registered name takes the form of "<printer-info> @ <computer name>"...
2869   */
2870
2871   if (p->info && strlen(p->info) > 0)
2872   {
2873     if (DNSSDComputerName)
2874     {
2875      /*
2876       * Make sure there is room for at least 15 characters of
2877       * DNSSDComputerName.
2878       */
2879
2880       assert(sizeof(name) >= 15 + 4);
2881       nameptr = name + strlcpy(name, p->info,
2882                                sizeof(name) - 4 -
2883                                strnlen(DNSSDComputerName, 15));
2884       nameptr += strlcpy(nameptr, " @ ", sizeof(name) - (nameptr - name));
2885       strlcpy(nameptr, DNSSDComputerName, sizeof(name) - (nameptr - name));
2886     }
2887     else
2888       strlcpy(name, p->info, sizeof(name));
2889   }
2890   else if (DNSSDComputerName)
2891   {
2892    /*
2893     * Make sure there is room for at least 15 characters of
2894     * DNSSDComputerName.
2895     */
2896
2897     assert(sizeof(name) >= 15 + 4);
2898     nameptr = name + strlcpy(name, p->info,
2899                              sizeof(name) - 4 -
2900                              strnlen(DNSSDComputerName, 15));
2901     nameptr += strlcpy(nameptr, " @ ", sizeof(name) - (nameptr - name));
2902     strlcpy(nameptr, DNSSDComputerName, sizeof(name) - (nameptr - name));
2903   }
2904   else
2905     strlcpy(name, p->name, sizeof(name));
2906
2907  /*
2908   * If an existing printer was renamed, unregister it and start over...
2909   */
2910
2911   if (p->reg_name && strcmp(p->reg_name, name))
2912     dnssdDeregisterPrinter(p);
2913
2914   if (!p->reg_name)
2915   {
2916     cupsdSetString(&p->reg_name, name);
2917     cupsArrayAdd(DNSSDPrinters, p);
2918   }
2919
2920  /*
2921   * Register IPP and (optionally) LPD...
2922   */
2923
2924 #ifdef HAVE_DNSSD
2925   ipp_len = 0;                          /* anti-compiler-warning-code */
2926   ipp_txt = dnssdBuildTxtRecord(&ipp_len, p, 0);
2927
2928   if (p->ipp_ref &&
2929       (ipp_len != p->ipp_len || memcmp(ipp_txt, p->ipp_txt, ipp_len)))
2930   {
2931    /*
2932     * Update the existing registration...
2933     */
2934
2935     /* A TTL of 0 means use record's original value (Radar 3176248) */
2936     if ((se = DNSServiceUpdateRecord(p->ipp_ref, NULL, 0, ipp_len, ipp_txt,
2937                                      0)) == kDNSServiceErr_NoError)
2938     {
2939       if (p->ipp_txt)
2940         free(p->ipp_txt);
2941
2942       p->ipp_txt = ipp_txt;
2943       p->ipp_len = ipp_len;
2944       ipp_txt    = NULL;
2945     }
2946     else
2947     {
2948      /*
2949       * Failed to update record, lets close this reference and move on...
2950       */
2951
2952       cupsdLogMessage(CUPSD_LOG_ERROR,
2953                       "Unable to update IPP DNS-SD record for %s - %d", p->name,
2954                       se);
2955
2956       DNSServiceRefDeallocate(p->ipp_ref);
2957       p->ipp_ref = NULL;
2958     }
2959   }
2960
2961   if (!p->ipp_ref)
2962   {
2963    /*
2964     * Initial registration.  Use the _fax-ipp regtype for fax queues...
2965     */
2966
2967     regtype = (p->type & CUPS_PRINTER_FAX) ? "_fax-ipp._tcp" : DNSSDRegType;
2968
2969     cupsdLogMessage(CUPSD_LOG_DEBUG,
2970                     "Registering DNS-SD printer %s with name \"%s\" and "
2971                     "type \"%s\"", p->name, name, regtype);
2972
2973    /*
2974     * Register the queue, dropping characters as needed until we succeed...
2975     */
2976
2977     nameptr = name + strlen(name);
2978
2979     do
2980     {
2981       p->ipp_ref = DNSSDRef;
2982       if ((se = DNSServiceRegister(&p->ipp_ref, kDNSServiceFlagsShareConnection,
2983                                    0, name, regtype, NULL, NULL,
2984                                    htons(DNSSDPort), ipp_len, ipp_txt,
2985                                    dnssdRegisterCallback,
2986                                    p)) == kDNSServiceErr_BadParam)
2987       {
2988        /*
2989         * Name is too long, drop trailing characters, taking into account
2990         * UTF-8 encoding...
2991         */
2992
2993         nameptr --;
2994
2995         while (nameptr > name && (*nameptr & 0xc0) == 0x80)
2996           nameptr --;
2997
2998         if (nameptr > name)
2999           *nameptr = '\0';
3000       }
3001     }
3002     while (se == kDNSServiceErr_BadParam && nameptr > name);
3003
3004     if (se == kDNSServiceErr_NoError)
3005     {
3006       p->ipp_txt = ipp_txt;
3007       p->ipp_len = ipp_len;
3008       ipp_txt    = NULL;
3009     }
3010     else
3011       cupsdLogMessage(CUPSD_LOG_WARN,
3012                       "DNS-SD IPP registration of \"%s\" failed: %d",
3013                       p->name, se);
3014   }
3015
3016   if (ipp_txt)
3017     free(ipp_txt);
3018
3019   if (BrowseLocalProtocols & BROWSE_LPD)
3020   {
3021     printer_len  = 0;                   /* anti-compiler-warning-code */
3022     printer_port = 515;
3023     printer_txt  = dnssdBuildTxtRecord(&printer_len, p, 1);
3024   }
3025   else
3026   {
3027     printer_len  = 0;
3028     printer_port = 0;
3029     printer_txt  = NULL;
3030   }
3031
3032   if (p->printer_ref &&
3033       (printer_len != p->printer_len ||
3034        memcmp(printer_txt, p->printer_txt, printer_len)))
3035   {
3036    /*
3037     * Update the existing registration...
3038     */
3039
3040     /* A TTL of 0 means use record's original value (Radar 3176248) */
3041     if ((se = DNSServiceUpdateRecord(p->printer_ref, NULL, 0, printer_len,
3042                                      printer_txt,
3043                                      0)) == kDNSServiceErr_NoError)
3044     {
3045       if (p->printer_txt)
3046         free(p->printer_txt);
3047
3048       p->printer_txt = printer_txt;
3049       p->printer_len = printer_len;
3050       printer_txt    = NULL;
3051     }
3052     else
3053     {
3054      /*
3055       * Failed to update record, lets close this reference and move on...
3056       */
3057
3058       cupsdLogMessage(CUPSD_LOG_ERROR,
3059                       "Unable to update LPD DNS-SD record for %s - %d",
3060                       p->name, se);
3061
3062       DNSServiceRefDeallocate(p->printer_ref);
3063       p->printer_ref = NULL;
3064     }
3065   }
3066
3067   if (!p->printer_ref)
3068   {
3069    /*
3070     * Initial registration...
3071     */
3072
3073     cupsdLogMessage(CUPSD_LOG_DEBUG,
3074                     "Registering DNS-SD printer %s with name \"%s\" and "
3075                     "type \"_printer._tcp\"", p->name, name);
3076
3077     p->printer_ref = DNSSDRef;
3078     if ((se = DNSServiceRegister(&p->printer_ref,
3079                                  kDNSServiceFlagsShareConnection,
3080                                  0, name, "_printer._tcp", NULL, NULL,
3081                                  htons(printer_port), printer_len, printer_txt,
3082                                  dnssdRegisterCallback,
3083                                  p)) == kDNSServiceErr_NoError)
3084     {
3085       p->printer_txt = printer_txt;
3086       p->printer_len = printer_len;
3087       printer_txt    = NULL;
3088     }
3089     else
3090       cupsdLogMessage(CUPSD_LOG_WARN,
3091                       "DNS-SD LPD registration of \"%s\" failed: %d",
3092                       p->name, se);
3093   }
3094
3095   if (printer_txt)
3096     free(printer_txt);
3097 #endif /* HAVE_DNSSD */
3098 #ifdef HAVE_AVAHI
3099   if (!AvahiCupsClient)
3100    /*
3101     * Client not running yet.  The client callback will call us again later.
3102     */
3103     return;
3104
3105   ipp_txt = dnssdBuildTxtRecord(NULL, p, 0);
3106   printer_txt = dnssdBuildTxtRecord(NULL, p, 1);
3107   regtype = (p->type & CUPS_PRINTER_FAX) ? "_fax-ipp._tcp" : DNSSDRegType;
3108
3109   if (p->avahi_group && p->ipp_txt && ipp_txt &&
3110       !avahi_string_list_equal (p->ipp_txt, ipp_txt))
3111   {
3112    /*
3113     * Update the existing registration...
3114     */
3115
3116     avahi_string_list_free (p->ipp_txt);
3117
3118     if (p->printer_txt)
3119       avahi_string_list_free (p->printer_txt);
3120
3121    /*
3122     * Update the service group entry.
3123     */
3124
3125     regtype_copy = strdup (regtype);
3126     subtype = strchr (regtype_copy, ',');
3127     if (subtype)
3128       *subtype = '\0';
3129
3130     cupsdLogMessage (CUPSD_LOG_DEBUG,
3131                      "Updating TXT record for %s (%s)", name, regtype_copy);
3132     ret = avahi_entry_group_update_service_txt_strlst (p->avahi_group,
3133                                                        AVAHI_IF_UNSPEC,
3134                                                        AVAHI_PROTO_UNSPEC,
3135                                                        0, name,
3136                                                        regtype_copy,
3137                                                        NULL, ipp_txt);
3138     free (regtype_copy);
3139
3140     if (ret < 0)
3141       goto update_failed;
3142
3143     p->ipp_txt = ipp_txt;
3144     ipp_txt = NULL;
3145
3146     if (BrowseLocalProtocols & BROWSE_LPD)
3147     {
3148       ret = avahi_entry_group_update_service_txt_strlst (p->avahi_group,
3149                                                          AVAHI_IF_UNSPEC,
3150                                                          AVAHI_PROTO_UNSPEC,
3151                                                          0, name,
3152                                                          "_printer._tcp", NULL,
3153                                                          printer_txt);
3154       if (ret < 0)
3155         goto update_failed;
3156
3157       p->printer_txt = printer_txt;
3158       printer_txt = NULL;
3159     }
3160
3161     ret = avahi_entry_group_commit (p->avahi_group);
3162     if (ret < 0)
3163     {
3164     update_failed:
3165       cupsdLogMessage (CUPSD_LOG_ERROR,
3166                        "Failed to update TXT record for %s: %d",
3167                        name, ret);
3168       avahi_entry_group_reset (p->avahi_group);
3169       avahi_entry_group_free (p->avahi_group);
3170       p->avahi_group = NULL;
3171       ipp_txt = p->ipp_txt;
3172       p->ipp_txt = NULL;
3173     }
3174   }
3175
3176   if (!p->avahi_group)
3177   {
3178    /*
3179     * Initial registration.  Use the _fax subtype for fax queues...
3180     */
3181
3182     p->avahi_group = avahi_entry_group_new (AvahiCupsClient,
3183                                             avahi_entry_group_cb,
3184                                             p);
3185
3186     cupsdLogMessage(CUPSD_LOG_DEBUG,
3187                     "Registering Avahi printer %s with name \"%s\" and "
3188                     "type \"%s\"", p->name, name, regtype);
3189
3190     if (!p->avahi_group)
3191     {
3192       ret = 0;
3193       goto add_failed;
3194     }
3195
3196    /*
3197     * Add each service type (DNSSDRegType may contain several,
3198     * separated by commas).
3199     */
3200
3201     subtype = regtype_copy = strdup (regtype);
3202     while (subtype && *subtype)
3203     {
3204       nextsubtype = strchr (subtype, ',');
3205       if (nextsubtype)
3206         *nextsubtype++ = '\0';
3207
3208       if (subtype == regtype_copy)
3209       {
3210        /*
3211         * Main type entry.
3212         */
3213
3214         cupsdLogMessage (CUPSD_LOG_DEBUG,
3215                          "Adding TXT record for %s (%s)", name, regtype_copy);
3216         ret = avahi_entry_group_add_service_strlst (p->avahi_group,
3217                                                     AVAHI_IF_UNSPEC,
3218                                                     AVAHI_PROTO_UNSPEC,
3219                                                     0, name, regtype_copy,
3220                                                     NULL, NULL,
3221                                                     DNSSDPort,
3222                                                     ipp_txt);
3223       }
3224       else
3225       {
3226        /*
3227         * Sub-type entry.
3228         */
3229
3230         snprintf (fullsubtype, sizeof(fullsubtype),
3231                   "%s._sub.%s", subtype, regtype_copy);
3232         cupsdLogMessage (CUPSD_LOG_DEBUG,
3233                          "Adding TXT record for %s (%s)", name, fullsubtype);
3234         ret = avahi_entry_group_add_service_subtype (p->avahi_group,
3235                                                      AVAHI_IF_UNSPEC,
3236                                                      AVAHI_PROTO_UNSPEC,
3237                                                      0, name,
3238                                                      regtype_copy,
3239                                                      NULL, fullsubtype);
3240       }
3241
3242       if (ret < 0)
3243       {
3244         free (regtype_copy);
3245         goto add_failed;
3246       }
3247
3248       subtype = nextsubtype;
3249     }
3250
3251     free (regtype_copy);
3252     p->ipp_txt = ipp_txt;
3253     ipp_txt = NULL;
3254
3255     if (BrowseLocalProtocols & BROWSE_LPD)
3256     {
3257       cupsdLogMessage(CUPSD_LOG_DEBUG,
3258                       "Registering Avahi printer %s with name \"%s\" and "
3259                       "type \"_printer._tcp\"", p->name, name);
3260
3261       ret = avahi_entry_group_add_service_strlst (p->avahi_group,
3262                                                   AVAHI_IF_UNSPEC,
3263                                                   AVAHI_PROTO_UNSPEC,
3264                                                   0, name,
3265                                                   "_printer._tcp", NULL, NULL,
3266                                                   515,
3267                                                   printer_txt);
3268       if (ret < 0)
3269         goto add_failed;
3270
3271       p->printer_txt = printer_txt;
3272       printer_txt = NULL;
3273     }
3274
3275     ret = avahi_entry_group_commit (p->avahi_group);
3276
3277     if (ret < 0)
3278     {
3279     add_failed:
3280       cupsdLogMessage (CUPSD_LOG_ERROR,
3281                        "Failed to add Avahi entry for %s: %d",
3282                        name, ret);
3283       if (p->avahi_group)
3284       {
3285         avahi_entry_group_reset (p->avahi_group);
3286         avahi_entry_group_free (p->avahi_group);
3287         p->avahi_group = NULL;
3288       }
3289       ipp_txt = p->ipp_txt;
3290       p->ipp_txt = NULL;
3291     }
3292   }
3293
3294   if (ipp_txt)
3295     avahi_string_list_free (ipp_txt);
3296
3297   if (printer_txt)
3298     avahi_string_list_free (printer_txt);
3299 #endif /* HAVE_AVAHI */
3300 }
3301
3302
3303 /*
3304  * 'dnssdStop()' - Stop all DNS-SD registrations.
3305  */
3306
3307 static void
3308 dnssdStop(void)
3309 {
3310   cupsd_printer_t       *p;             /* Current printer */
3311
3312 #ifdef HAVE_DNSSD
3313   if (!DNSSDRef)
3314     return;
3315 #endif /* HAVE_DNSSD */
3316
3317  /*
3318   * De-register the individual printers
3319   */
3320
3321   for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
3322        p;
3323        p = (cupsd_printer_t *)cupsArrayNext(Printers))
3324     dnssdDeregisterPrinter(p);
3325
3326 #ifdef HAVE_DNSSD
3327  /*
3328   * Shutdown the rest of the service refs...
3329   */
3330
3331   if (WebIFRef)
3332   {
3333     DNSServiceRefDeallocate(WebIFRef);
3334     WebIFRef = NULL;
3335   }
3336
3337   if (RemoteRef)
3338   {
3339     DNSServiceRefDeallocate(RemoteRef);
3340     RemoteRef = NULL;
3341   }
3342
3343   cupsdRemoveSelect(DNSServiceRefSockFD(DNSSDRef));
3344
3345   DNSServiceRefDeallocate(DNSSDRef);
3346   DNSSDRef = NULL;
3347 #endif /* HAVE_DNSSD */
3348
3349   cupsArrayDelete(DNSSDPrinters);
3350   DNSSDPrinters = NULL;
3351
3352   DNSSDPort = 0;
3353 }
3354 #endif /* defined(HAVE_DNSSD) || defined(HAVE_AVAHI) */
3355
3356
3357 #ifdef HAVE_DNSSD
3358 /*
3359  * 'dnssdUpdate()' - Handle DNS-SD queries.
3360  */
3361
3362 static void
3363 dnssdUpdate(void)
3364 {
3365   DNSServiceErrorType   sdErr;          /* Service discovery error */
3366
3367
3368   if ((sdErr = DNSServiceProcessResult(DNSSDRef)) != kDNSServiceErr_NoError)
3369   {
3370     cupsdLogMessage(CUPSD_LOG_ERROR,
3371                     "DNS Service Discovery registration error %d!",
3372                     sdErr);
3373     dnssdStop();
3374   }
3375 }
3376 #endif /* HAVE_DNSSD */
3377
3378
3379 #ifdef HAVE_AVAHI
3380 /*
3381  * 'avahiPackTxtRecord()' - Pack an array of key/value pairs into an
3382  *                          AvahiStringList.
3383  */
3384
3385 static AvahiStringList *                /* O - new string list */
3386 avahiPackTxtRecord(char *keyvalue[][2], /* I - Table of key value pairs */
3387                    int count)           /* I - Number of items in table */
3388 {
3389   AvahiStringList *strlst = NULL;
3390   char **elements;
3391   size_t len;
3392   int i = 0;
3393
3394   elements = malloc ((1 + count) * sizeof (char *));
3395   if (!elements)
3396     goto cleanup;
3397
3398   for (i = 0; i < count; i++)
3399     {
3400       len = (1 + strlen (keyvalue[i][0]) +
3401              (keyvalue[i][1] ? 1 + strlen (keyvalue[i][1]) : 1));
3402       elements[i] = malloc (len * sizeof (char));
3403       if (!elements[i])
3404         goto cleanup;
3405
3406       snprintf (elements[i], len, "%s=%s", keyvalue[i][0], keyvalue[i][1]);
3407     }
3408
3409   strlst = avahi_string_list_new_from_array ((const char **) elements, count);
3410
3411 cleanup:
3412   while (--i >= 0)
3413     free (elements[i]);
3414
3415   free (elements);
3416   return (strlst);
3417 }
3418
3419
3420 /*
3421  * 'avahi_entry_group_cb()' - Avahi entry group callback function.
3422  */
3423 static void
3424 avahi_entry_group_cb (AvahiEntryGroup *group,
3425                       AvahiEntryGroupState state,
3426                       void *userdata)
3427 {
3428   char *name;
3429
3430   if (userdata)
3431     name = ((cupsd_printer_t *) userdata)->reg_name;
3432   else
3433     name = "CUPS web interface";
3434
3435   switch (state)
3436   {
3437   case AVAHI_ENTRY_GROUP_UNCOMMITED:
3438   case AVAHI_ENTRY_GROUP_REGISTERING:
3439     break;
3440
3441   case AVAHI_ENTRY_GROUP_ESTABLISHED:
3442     cupsdLogMessage (CUPSD_LOG_DEBUG,
3443                      "Avahi entry group established for %s", name);
3444     break;
3445
3446   default:
3447     cupsdLogMessage (CUPSD_LOG_DEBUG,
3448                      "Avahi entry group %s has state %d",
3449                      name, state);
3450     break;
3451   }
3452 }
3453
3454
3455 /*
3456  * 'avahi_client_cb()' - Avahi client callback function.
3457  */
3458 static void
3459 avahi_client_cb (AvahiClient *client,
3460                  AvahiClientState state,
3461                  void *userdata)
3462 {
3463   cupsd_printer_t *printer;
3464   switch (state)
3465   {
3466   case AVAHI_CLIENT_S_RUNNING:
3467    /*
3468     * Avahi client started successfully.
3469     */
3470     AvahiCupsClient = client;
3471     AvahiCupsClientConnecting = 0;
3472     cupsdLogMessage (CUPSD_LOG_DEBUG, "Avahi client started");
3473
3474     cupsdUpdateDNSSDName ();
3475
3476     for (printer = (cupsd_printer_t *)cupsArrayFirst(Printers);
3477          printer;
3478          printer = (cupsd_printer_t *)cupsArrayNext(Printers))
3479       if (Browsing && (BrowseLocalProtocols & BROWSE_DNSSD) &&
3480           (!(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT |
3481                               CUPS_PRINTER_SCANNER))) && printer->shared)
3482         dnssdRegisterPrinter (printer);
3483
3484     break;
3485
3486   case AVAHI_CLIENT_CONNECTING:
3487    /*
3488     * No Avahi daemon, client is waiting.
3489     */
3490     cupsdLogMessage (CUPSD_LOG_DEBUG, "Avahi client connecting");
3491     break;
3492
3493   case AVAHI_CLIENT_S_REGISTERING:
3494     /*
3495      * Not yet registered.
3496      */
3497     cupsdLogMessage (CUPSD_LOG_DEBUG, "Avahi client registering");
3498     break;
3499
3500   case AVAHI_CLIENT_FAILURE:
3501    /*
3502     * Avahi client failed, close it to allow a clean restart.
3503     */
3504     cupsdLogMessage (CUPSD_LOG_ERROR,
3505                      "Avahi client failed, "
3506                      "closing client to allow a clean restart");
3507
3508     for (printer = (cupsd_printer_t *)cupsArrayFirst(Printers);
3509          printer;
3510          printer = (cupsd_printer_t *)cupsArrayNext(Printers))
3511       dnssdDeregisterPrinter (printer);
3512
3513     avahi_client_free(client);
3514     AvahiCupsClientConnecting = 0;
3515     AvahiCupsClient = NULL;
3516
3517     break;
3518
3519   default:
3520     cupsdLogMessage (CUPSD_LOG_DEBUG, "Avahi client state: %d", state);
3521   }
3522 }
3523 #endif /* HAVE_AVAHI */
3524
3525
3526 /*
3527  * 'get_auth_info_required()' - Get the auth-info-required value to advertise.
3528  */
3529
3530 static char *                           /* O - String or NULL if none */
3531 get_auth_info_required(
3532     cupsd_printer_t *p,                 /* I - Printer */
3533     char            *buffer,            /* I - Value buffer */
3534     size_t          bufsize)            /* I - Size of value buffer */
3535 {
3536   cupsd_location_t *auth;               /* Pointer to authentication element */
3537   char          resource[1024];         /* Printer/class resource path */
3538
3539
3540  /*
3541   * If auth-info-required is set for this printer, return that...
3542   */
3543
3544   if (p->num_auth_info_required > 0 && strcmp(p->auth_info_required[0], "none"))
3545   {
3546     int         i;                      /* Looping var */
3547     char        *bufptr;                /* Pointer into buffer */
3548
3549     for (i = 0, bufptr = buffer; i < p->num_auth_info_required; i ++)
3550     {
3551       if (bufptr >= (buffer + bufsize - 2))
3552         break;
3553
3554       if (i)
3555         *bufptr++ = ',';
3556
3557       strlcpy(bufptr, p->auth_info_required[i], bufsize - (bufptr - buffer));
3558       bufptr += strlen(bufptr);
3559     }
3560
3561     return (buffer);
3562   }
3563
3564  /*
3565   * Figure out the authentication data requirements to advertise...
3566   */
3567
3568   if (p->type & CUPS_PRINTER_CLASS)
3569     snprintf(resource, sizeof(resource), "/classes/%s", p->name);
3570   else
3571     snprintf(resource, sizeof(resource), "/printers/%s", p->name);
3572
3573   if ((auth = cupsdFindBest(resource, HTTP_POST)) == NULL ||
3574       auth->type == CUPSD_AUTH_NONE)
3575     auth = cupsdFindPolicyOp(p->op_policy_ptr, IPP_PRINT_JOB);
3576
3577   if (auth)
3578   {
3579     int auth_type;                      /* Authentication type */
3580
3581     if ((auth_type = auth->type) == CUPSD_AUTH_DEFAULT)
3582       auth_type = DefaultAuthType;
3583
3584     switch (auth_type)
3585     {
3586       case CUPSD_AUTH_NONE :
3587           return (NULL);
3588
3589       case CUPSD_AUTH_NEGOTIATE :
3590           strlcpy(buffer, "negotiate", bufsize);
3591           break;
3592
3593       default :
3594           strlcpy(buffer, "username,password", bufsize);
3595           break;
3596     }
3597
3598     return (buffer);
3599   }
3600
3601   return ("none");
3602 }
3603
3604
3605 #ifdef __APPLE__
3606 /*
3607  * 'get_hostconfig()' - Get an /etc/hostconfig service setting.
3608  */
3609
3610 static int                              /* O - 1 for YES or AUTOMATIC, 0 for NO */
3611 get_hostconfig(const char *name)        /* I - Name of service */
3612 {
3613   cups_file_t   *fp;                    /* Hostconfig file */
3614   char          line[1024],             /* Line from file */
3615                 *ptr;                   /* Pointer to value */
3616   int           state = 1;              /* State of service */
3617
3618
3619  /*
3620   * Try opening the /etc/hostconfig file; if we can't open it, assume that
3621   * the service is enabled/auto.
3622   */
3623
3624   if ((fp = cupsFileOpen("/etc/hostconfig", "r")) != NULL)
3625   {
3626    /*
3627     * Read lines from the file until we find the service...
3628     */
3629
3630     while (cupsFileGets(fp, line, sizeof(line)))
3631     {
3632       if (line[0] == '#' || (ptr = strchr(line, '=')) == NULL)
3633         continue;
3634
3635       *ptr++ = '\0';
3636
3637       if (!_cups_strcasecmp(line, name))
3638       {
3639        /*
3640         * Found the service, see if it is set to "-NO-"...
3641         */
3642
3643         if (!_cups_strncasecmp(ptr, "-NO-", 4))
3644           state = 0;
3645         break;
3646       }
3647     }
3648
3649     cupsFileClose(fp);
3650   }
3651
3652   return (state);
3653 }
3654 #endif /* __APPLE__ */
3655
3656
3657 /*
3658  * 'is_local_queue()' - Determine whether the URI points at a local queue.
3659  */
3660
3661 static int                              /* O - 1 = local, 0 = remote, -1 = bad URI */
3662 is_local_queue(const char *uri,         /* I - Printer URI */
3663                char       *host,        /* O - Host string */
3664                int        hostlen,      /* I - Length of host buffer */
3665                char       *resource,    /* O - Resource string */
3666                int        resourcelen)  /* I - Length of resource buffer */
3667 {
3668   char          scheme[32],             /* Scheme portion of URI */
3669                 username[HTTP_MAX_URI]; /* Username portion of URI */
3670   int           port;                   /* Port portion of URI */
3671   cupsd_netif_t *iface;                 /* Network interface */
3672
3673
3674  /*
3675   * Pull the URI apart to see if this is a local or remote printer...
3676   */
3677
3678   if (httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, sizeof(scheme),
3679                       username, sizeof(username), host, hostlen, &port,
3680                       resource, resourcelen) < HTTP_URI_OK)
3681     return (-1);
3682
3683   DEBUG_printf(("host=\"%s\", ServerName=\"%s\"\n", host, ServerName));
3684
3685  /*
3686   * Check for local server addresses...
3687   */
3688
3689   if (!_cups_strcasecmp(host, ServerName) && port == LocalPort)
3690     return (1);
3691
3692   cupsdNetIFUpdate();
3693
3694   for (iface = (cupsd_netif_t *)cupsArrayFirst(NetIFList);
3695        iface;
3696        iface = (cupsd_netif_t *)cupsArrayNext(NetIFList))
3697     if (!_cups_strcasecmp(host, iface->hostname) && port == iface->port)
3698       return (1);
3699
3700  /*
3701   * If we get here, the printer is remote...
3702   */
3703
3704   return (0);
3705 }
3706
3707
3708 /*
3709  * 'process_browse_data()' - Process new browse data.
3710  */
3711
3712 static void
3713 process_browse_data(
3714     const char    *uri,                 /* I - URI of printer/class */
3715     const char    *host,                /* I - Hostname */
3716     const char    *resource,            /* I - Resource path */
3717     cups_ptype_t  type,                 /* I - Printer type */
3718     ipp_pstate_t  state,                /* I - Printer state */
3719     const char    *location,            /* I - Printer location */
3720     const char    *info,                /* I - Printer information */
3721     const char    *make_model,          /* I - Printer make and model */
3722     int           num_attrs,            /* I - Number of attributes */
3723     cups_option_t *attrs)               /* I - Attributes */
3724 {
3725   int           i;                      /* Looping var */
3726   int           update;                 /* Update printer attributes? */
3727   char          finaluri[HTTP_MAX_URI], /* Final URI for printer */
3728                 name[IPP_MAX_NAME],     /* Name of printer */
3729                 newname[IPP_MAX_NAME],  /* New name of printer */
3730                 *hptr,                  /* Pointer into hostname */
3731                 *sptr;                  /* Pointer into ServerName */
3732   const char    *shortname;             /* Short queue name (queue) */
3733   char          local_make_model[IPP_MAX_NAME];
3734                                         /* Local make and model */
3735   cupsd_printer_t *p;                   /* Printer information */
3736   const char    *ipp_options,           /* ipp-options value */
3737                 *lease_duration,        /* lease-duration value */
3738                 *uuid;                  /* uuid value */
3739   int           is_class;               /* Is this queue a class? */
3740
3741
3742   cupsdLogMessage(CUPSD_LOG_DEBUG2,
3743                   "process_browse_data(uri=\"%s\", host=\"%s\", "
3744                   "resource=\"%s\", type=%x, state=%d, location=\"%s\", "
3745                   "info=\"%s\", make_model=\"%s\", num_attrs=%d, attrs=%p)",
3746                   uri, host, resource, type, state,
3747                   location ? location : "(nil)", info ? info : "(nil)",
3748                   make_model ? make_model : "(nil)", num_attrs, attrs);
3749
3750  /*
3751   * Determine if the URI contains any illegal characters in it...
3752   */
3753
3754   if (strncmp(uri, "ipp://", 6) || !host[0] ||
3755       (strncmp(resource, "/printers/", 10) &&
3756        strncmp(resource, "/classes/", 9)))
3757   {
3758     cupsdLogMessage(CUPSD_LOG_ERROR, "Bad printer URI in browse data: %s", uri);
3759     return;
3760   }
3761
3762   if (strchr(resource, '?') ||
3763       (!strncmp(resource, "/printers/", 10) && strchr(resource + 10, '/')) ||
3764       (!strncmp(resource, "/classes/", 9) && strchr(resource + 9, '/')))
3765   {
3766     cupsdLogMessage(CUPSD_LOG_ERROR, "Bad resource in browse data: %s",
3767                     resource);
3768     return;
3769   }
3770
3771  /*
3772   * OK, this isn't a local printer; add any remote options...
3773   */
3774
3775   ipp_options = cupsGetOption("ipp-options", num_attrs, attrs);
3776
3777   if (BrowseRemoteOptions)
3778   {
3779     if (BrowseRemoteOptions[0] == '?')
3780     {
3781      /*
3782       * Override server-supplied options...
3783       */
3784
3785       snprintf(finaluri, sizeof(finaluri), "%s%s", uri, BrowseRemoteOptions);
3786     }
3787     else if (ipp_options)
3788     {
3789      /*
3790       * Combine the server and local options...
3791       */
3792
3793       snprintf(finaluri, sizeof(finaluri), "%s?%s+%s", uri, ipp_options,
3794                BrowseRemoteOptions);
3795     }
3796     else
3797     {
3798      /*
3799       * Just use the local options...
3800       */
3801
3802       snprintf(finaluri, sizeof(finaluri), "%s?%s", uri, BrowseRemoteOptions);
3803     }
3804
3805     uri = finaluri;
3806   }
3807   else if (ipp_options)
3808   {
3809    /*
3810     * Just use the server-supplied options...
3811     */
3812
3813     snprintf(finaluri, sizeof(finaluri), "%s?%s", uri, ipp_options);
3814     uri = finaluri;
3815   }
3816
3817  /*
3818   * See if we already have it listed in the Printers list, and add it if not...
3819   */
3820
3821   type     |= CUPS_PRINTER_REMOTE | CUPS_PRINTER_DISCOVERED;
3822   type     &= ~CUPS_PRINTER_IMPLICIT;
3823   update   = 0;
3824   hptr     = strchr(host, '.');
3825   sptr     = strchr(ServerName, '.');
3826   is_class = type & CUPS_PRINTER_CLASS;
3827   uuid     = cupsGetOption("uuid", num_attrs, attrs);
3828
3829   if (!ServerNameIsIP && sptr != NULL && hptr != NULL)
3830   {
3831    /*
3832     * Strip the common domain name components...
3833     */
3834
3835     while (hptr != NULL)
3836     {
3837       if (!_cups_strcasecmp(hptr, sptr))
3838       {
3839         *hptr = '\0';
3840         break;
3841       }
3842       else
3843         hptr = strchr(hptr + 1, '.');
3844     }
3845   }
3846
3847   if (is_class)
3848   {
3849    /*
3850     * Remote destination is a class...
3851     */
3852
3853     if (!strncmp(resource, "/classes/", 9))
3854       snprintf(name, sizeof(name), "%s@%s", resource + 9, host);
3855     else
3856       return;
3857
3858     shortname = resource + 9;
3859   }
3860   else
3861   {
3862    /*
3863     * Remote destination is a printer...
3864     */
3865
3866     if (!strncmp(resource, "/printers/", 10))
3867       snprintf(name, sizeof(name), "%s@%s", resource + 10, host);
3868     else
3869       return;
3870
3871     shortname = resource + 10;
3872   }
3873
3874   if (hptr && !*hptr)
3875     *hptr = '.';                        /* Resource FQDN */
3876
3877   if ((p = cupsdFindDest(name)) == NULL && BrowseShortNames)
3878   {
3879    /*
3880     * Long name doesn't exist, try short name...
3881     */
3882
3883     cupsdLogMessage(CUPSD_LOG_DEBUG, "process_browse_data: %s not found...",
3884                     name);
3885
3886     if ((p = cupsdFindDest(shortname)) == NULL)
3887     {
3888      /*
3889       * Short name doesn't exist, use it for this shared queue.
3890       */
3891
3892       cupsdLogMessage(CUPSD_LOG_DEBUG2, "process_browse_data: %s not found...",
3893                       shortname);
3894       strlcpy(name, shortname, sizeof(name));
3895     }
3896     else
3897     {
3898      /*
3899       * Short name exists...
3900       */
3901
3902       cupsdLogMessage(CUPSD_LOG_DEBUG2,
3903                       "process_browse_data: %s found, type=%x, hostname=%s...",
3904                       shortname, p->type, p->hostname ? p->hostname : "(nil)");
3905
3906       if (p->type & CUPS_PRINTER_IMPLICIT)
3907         p = NULL;                       /* Don't replace implicit classes */
3908       else if (p->hostname && _cups_strcasecmp(p->hostname, host))
3909       {
3910        /*
3911         * Short name exists but is for a different host.  If this is a remote
3912         * queue, rename it and use the long name...
3913         */
3914
3915         if (p->type & CUPS_PRINTER_REMOTE)
3916         {
3917           cupsdLogMessage(CUPSD_LOG_DEBUG,
3918                           "Renamed remote %s \"%s\" to \"%s@%s\"...",
3919                           is_class ? "class" : "printer", p->name, p->name,
3920                           p->hostname);
3921           cupsdAddEvent(CUPSD_EVENT_PRINTER_DELETED, p, NULL,
3922                         "%s \'%s\' deleted by directory services.",
3923                         is_class ? "Class" : "Printer", p->name);
3924
3925           snprintf(newname, sizeof(newname), "%s@%s", p->name, p->hostname);
3926           cupsdRenamePrinter(p, newname);
3927
3928           cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED, p, NULL,
3929                         "%s \'%s\' added by directory services.",
3930                         is_class ? "Class" : "Printer", p->name);
3931         }
3932
3933        /*
3934         * Force creation with long name...
3935         */
3936
3937         p = NULL;
3938       }
3939     }
3940   }
3941   else if (p)
3942     cupsdLogMessage(CUPSD_LOG_DEBUG2,
3943                     "process_browse_data: %s found, type=%x, hostname=%s...",
3944                     name, p->type, p->hostname ? p->hostname : "(nil)");
3945
3946   if (!p)
3947   {
3948    /*
3949     * Queue doesn't exist; add it...
3950     */
3951
3952     if (is_class)
3953       p = cupsdAddClass(name);
3954     else
3955       p = cupsdAddPrinter(name);
3956
3957     if (!p)
3958       return;
3959
3960     cupsdClearString(&(p->hostname));
3961
3962     cupsdLogMessage(CUPSD_LOG_DEBUG, "Added remote %s \"%s\"...",
3963                     is_class ? "class" : "printer", name);
3964
3965     cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED, p, NULL,
3966                   "%s \'%s\' added by directory services.",
3967                   is_class ? "Class" : "Printer", name);
3968
3969    /*
3970     * Force the URI to point to the real server...
3971     */
3972
3973     p->type      = type & ~CUPS_PRINTER_REJECTING;
3974     p->accepting = 1;
3975
3976     cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP);
3977   }
3978
3979   if (!p->hostname)
3980   {
3981    /*
3982     * Hostname not set, so this must be a cached remote printer
3983     * that was created for a pending print job...
3984     */
3985
3986     cupsdSetString(&p->hostname, host);
3987     cupsdSetString(&p->uri, uri);
3988     cupsdSetString(&p->device_uri, uri);
3989     update = 1;
3990
3991     cupsdMarkDirty(CUPSD_DIRTY_REMOTE);
3992   }
3993
3994  /*
3995   * Update the state...
3996   */
3997
3998   p->state       = state;
3999   p->browse_time = time(NULL);
4000
4001   if ((lease_duration = cupsGetOption("lease-duration", num_attrs,
4002                                       attrs)) != NULL)
4003   {
4004    /*
4005     * Grab the lease-duration for the browse data; anything less then 1
4006     * second or more than 1 week gets the default BrowseTimeout...
4007     */
4008
4009     i = atoi(lease_duration);
4010     if (i < 1 || i > 604800)
4011       i = BrowseTimeout;
4012
4013     p->browse_expire = p->browse_time + i;
4014   }
4015   else
4016     p->browse_expire = p->browse_time + BrowseTimeout;
4017
4018   if (type & CUPS_PRINTER_REJECTING)
4019   {
4020     type &= ~CUPS_PRINTER_REJECTING;
4021
4022     if (p->accepting)
4023     {
4024       update       = 1;
4025       p->accepting = 0;
4026     }
4027   }
4028   else if (!p->accepting)
4029   {
4030     update       = 1;
4031     p->accepting = 1;
4032   }
4033
4034   if (p->type != type)
4035   {
4036     p->type = type;
4037     update  = 1;
4038   }
4039
4040   if (uuid && strcmp(p->uuid, uuid))
4041   {
4042     cupsdSetString(&p->uuid, uuid);
4043     update = 1;
4044   }
4045
4046   if (location && (!p->location || strcmp(p->location, location)))
4047   {
4048     cupsdSetString(&p->location, location);
4049     update = 1;
4050   }
4051
4052   if (info && (!p->info || strcmp(p->info, info)))
4053   {
4054     cupsdSetString(&p->info, info);
4055     update = 1;
4056
4057     cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP | CUPSD_DIRTY_REMOTE);
4058   }
4059
4060   if (!make_model || !make_model[0])
4061   {
4062     if (is_class)
4063       snprintf(local_make_model, sizeof(local_make_model),
4064                "Remote Class on %s", host);
4065     else
4066       snprintf(local_make_model, sizeof(local_make_model),
4067                "Remote Printer on %s", host);
4068   }
4069   else
4070     snprintf(local_make_model, sizeof(local_make_model),
4071              "%s on %s", make_model, host);
4072
4073   if (!p->make_model || strcmp(p->make_model, local_make_model))
4074   {
4075     cupsdSetString(&p->make_model, local_make_model);
4076     update = 1;
4077   }
4078
4079   if (p->num_options)
4080   {
4081     if (!update && !(type & CUPS_PRINTER_DELETE))
4082     {
4083      /*
4084       * See if we need to update the attributes...
4085       */
4086
4087       if (p->num_options != num_attrs)
4088         update = 1;
4089       else
4090       {
4091         for (i = 0; i < num_attrs; i ++)
4092           if (strcmp(attrs[i].name, p->options[i].name) ||
4093               (!attrs[i].value != !p->options[i].value) ||
4094               (attrs[i].value && strcmp(attrs[i].value, p->options[i].value)))
4095           {
4096             update = 1;
4097             break;
4098           }
4099       }
4100     }
4101
4102    /*
4103     * Free the old options...
4104     */
4105
4106     cupsFreeOptions(p->num_options, p->options);
4107   }
4108
4109   p->num_options = num_attrs;
4110   p->options     = attrs;
4111
4112   if (type & CUPS_PRINTER_DELETE)
4113   {
4114     cupsdAddEvent(CUPSD_EVENT_PRINTER_DELETED, p, NULL,
4115                   "%s \'%s\' deleted by directory services.",
4116                   is_class ? "Class" : "Printer", p->name);
4117
4118     cupsdExpireSubscriptions(p, NULL);
4119
4120     cupsdDeletePrinter(p, 1);
4121     cupsdUpdateImplicitClasses();
4122     cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP | CUPSD_DIRTY_REMOTE);
4123   }
4124   else if (update)
4125   {
4126     cupsdSetPrinterAttrs(p);
4127     cupsdUpdateImplicitClasses();
4128   }
4129
4130  /*
4131   * See if we have a default printer...  If not, make the first network
4132   * default printer the default.
4133   */
4134
4135   if (DefaultPrinter == NULL && Printers != NULL && UseNetworkDefault)
4136   {
4137    /*
4138     * Find the first network default printer and use it...
4139     */
4140
4141     for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
4142          p;
4143          p = (cupsd_printer_t *)cupsArrayNext(Printers))
4144       if (p->type & CUPS_PRINTER_DEFAULT)
4145       {
4146         DefaultPrinter = p;
4147         cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP | CUPSD_DIRTY_REMOTE);
4148         break;
4149       }
4150   }
4151
4152  /*
4153   * Do auto-classing if needed...
4154   */
4155
4156   process_implicit_classes();
4157 }
4158
4159
4160 /*
4161  * 'process_implicit_classes()' - Create/update implicit classes as needed.
4162  */
4163
4164 static void
4165 process_implicit_classes(void)
4166 {
4167   int           i;                      /* Looping var */
4168   int           update;                 /* Update printer attributes? */
4169   char          name[IPP_MAX_NAME],     /* Name of printer */
4170                 *hptr;                  /* Pointer into hostname */
4171   cupsd_printer_t *p,                   /* Printer information */
4172                 *pclass,                /* Printer class */
4173                 *first;                 /* First printer in class */
4174   int           offset,                 /* Offset of name */
4175                 len;                    /* Length of name */
4176
4177
4178   if (!ImplicitClasses || !Printers)
4179     return;
4180
4181  /*
4182   * Loop through all available printers and create classes as needed...
4183   */
4184
4185   for (p = (cupsd_printer_t *)cupsArrayFirst(Printers), len = 0, offset = 0,
4186            update = 0, pclass = NULL, first = NULL;
4187        p != NULL;
4188        p = (cupsd_printer_t *)cupsArrayNext(Printers))
4189   {
4190    /*
4191     * Skip implicit classes...
4192     */
4193
4194     if (p->type & CUPS_PRINTER_IMPLICIT)
4195     {
4196       len = 0;
4197       continue;
4198     }
4199
4200    /*
4201     * If len == 0, get the length of this printer name up to the "@"
4202     * sign (if any).
4203     */
4204
4205     cupsArraySave(Printers);
4206
4207     if (len > 0 &&
4208         !_cups_strncasecmp(p->name, name + offset, len) &&
4209         (p->name[len] == '\0' || p->name[len] == '@'))
4210     {
4211      /*
4212       * We have more than one printer with the same name; see if
4213       * we have a class, and if this printer is a member...
4214       */
4215
4216       if (pclass && _cups_strcasecmp(pclass->name, name))
4217       {
4218         if (update)
4219           cupsdSetPrinterAttrs(pclass);
4220
4221         update = 0;
4222         pclass = NULL;
4223       }
4224
4225       if (!pclass && (pclass = cupsdFindDest(name)) == NULL)
4226       {
4227        /*
4228         * Need to add the class...
4229         */
4230
4231         pclass = cupsdAddPrinter(name);
4232         cupsArrayAdd(ImplicitPrinters, pclass);
4233
4234         pclass->type      |= CUPS_PRINTER_IMPLICIT;
4235         pclass->accepting = 1;
4236         pclass->state     = IPP_PRINTER_IDLE;
4237
4238         cupsdSetString(&pclass->location, p->location);
4239         cupsdSetString(&pclass->info, p->info);
4240
4241         cupsdSetString(&pclass->job_sheets[0], p->job_sheets[0]);
4242         cupsdSetString(&pclass->job_sheets[1], p->job_sheets[1]);
4243
4244         update = 1;
4245
4246         cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP | CUPSD_DIRTY_REMOTE);
4247
4248         cupsdLogMessage(CUPSD_LOG_DEBUG, "Added implicit class \"%s\"...",
4249                         name);
4250         cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED, p, NULL,
4251                       "Implicit class \'%s\' added by directory services.",
4252                       name);
4253       }
4254
4255       if (first != NULL)
4256       {
4257         for (i = 0; i < pclass->num_printers; i ++)
4258           if (pclass->printers[i] == first)
4259             break;
4260
4261         if (i >= pclass->num_printers)
4262         {
4263           first->in_implicit_class = 1;
4264           cupsdAddPrinterToClass(pclass, first);
4265         }
4266
4267         first = NULL;
4268       }
4269
4270       for (i = 0; i < pclass->num_printers; i ++)
4271         if (pclass->printers[i] == p)
4272           break;
4273
4274       if (i >= pclass->num_printers)
4275       {
4276         p->in_implicit_class = 1;
4277         cupsdAddPrinterToClass(pclass, p);
4278         update = 1;
4279       }
4280     }
4281     else
4282     {
4283      /*
4284       * First time around; just get name length and mark it as first
4285       * in the list...
4286       */
4287
4288       if ((hptr = strchr(p->name, '@')) != NULL)
4289         len = hptr - p->name;
4290       else
4291         len = strlen(p->name);
4292
4293       if (len >= sizeof(name))
4294       {
4295        /*
4296         * If the printer name length somehow is greater than we normally allow,
4297         * skip this printer...
4298         */
4299
4300         len = 0;
4301         cupsArrayRestore(Printers);
4302         continue;
4303       }
4304
4305       strncpy(name, p->name, len);
4306       name[len] = '\0';
4307       offset    = 0;
4308
4309       if ((first = (hptr ? cupsdFindDest(name) : p)) != NULL &&
4310           !(first->type & CUPS_PRINTER_IMPLICIT))
4311       {
4312        /*
4313         * Can't use same name as a local printer; add "Any" to the
4314         * front of the name, unless we have explicitly disabled
4315         * the "ImplicitAnyClasses"...
4316         */
4317
4318         if (ImplicitAnyClasses && len < (sizeof(name) - 4))
4319         {
4320          /*
4321           * Add "Any" to the class name...
4322           */
4323
4324           strcpy(name, "Any");
4325           strncpy(name + 3, p->name, len);
4326           name[len + 3] = '\0';
4327           offset        = 3;
4328         }
4329         else
4330         {
4331          /*
4332           * Don't create an implicit class if we have a local printer
4333           * with the same name...
4334           */
4335
4336           len = 0;
4337           cupsArrayRestore(Printers);
4338           continue;
4339         }
4340       }
4341
4342       first = p;
4343     }
4344
4345     cupsArrayRestore(Printers);
4346   }
4347
4348  /*
4349   * Update the last printer class as needed...
4350   */
4351
4352   if (pclass && update)
4353     cupsdSetPrinterAttrs(pclass);
4354 }
4355
4356
4357 /*
4358  * 'send_cups_browse()' - Send new browsing information using the CUPS
4359  *                        protocol.
4360  */
4361
4362 static void
4363 send_cups_browse(cupsd_printer_t *p)    /* I - Printer to send */
4364 {
4365   int                   i;              /* Looping var */
4366   cups_ptype_t          type;           /* Printer type */
4367   cupsd_dirsvc_addr_t   *b;             /* Browse address */
4368   int                   bytes;          /* Length of packet */
4369   char                  packet[1453],   /* Browse data packet */
4370                         uri[1024],      /* Printer URI */
4371                         location[1024], /* printer-location */
4372                         info[1024],     /* printer-info */
4373                         make_model[1024],
4374                                         /* printer-make-and-model */
4375                         air[1024];      /* auth-info-required */
4376   cupsd_netif_t         *iface;         /* Network interface */
4377
4378
4379  /*
4380   * Figure out the printer type value...
4381   */
4382
4383   type = p->type | CUPS_PRINTER_REMOTE;
4384
4385   if (!p->accepting)
4386     type |= CUPS_PRINTER_REJECTING;
4387
4388   if (p == DefaultPrinter)
4389     type |= CUPS_PRINTER_DEFAULT;
4390
4391  /*
4392   * Remove quotes from printer-info, printer-location, and
4393   * printer-make-and-model attributes...
4394   */
4395
4396   dequote(location, p->location, sizeof(location));
4397   dequote(info, p->info, sizeof(info));
4398
4399   if (p->make_model)
4400     dequote(make_model, p->make_model, sizeof(make_model));
4401   else if (p->type & CUPS_PRINTER_CLASS)
4402   {
4403     if (p->num_printers > 0 && p->printers[0]->make_model)
4404       strlcpy(make_model, p->printers[0]->make_model, sizeof(make_model));
4405     else
4406       strlcpy(make_model, "Local Printer Class", sizeof(make_model));
4407   }
4408   else if (p->raw)
4409     strlcpy(make_model, "Local Raw Printer", sizeof(make_model));
4410   else
4411     strlcpy(make_model, "Local System V Printer", sizeof(make_model));
4412
4413   if (get_auth_info_required(p, packet, sizeof(packet)))
4414     snprintf(air, sizeof(air), " auth-info-required=%s", packet);
4415   else
4416     air[0] = '\0';
4417
4418  /*
4419   * Send a packet to each browse address...
4420   */
4421
4422   for (i = NumBrowsers, b = Browsers; i > 0; i --, b ++)
4423     if (b->iface[0])
4424     {
4425      /*
4426       * Send the browse packet to one or more interfaces...
4427       */
4428
4429       if (!strcmp(b->iface, "*"))
4430       {
4431        /*
4432         * Send to all local interfaces...
4433         */
4434
4435         cupsdNetIFUpdate();
4436
4437         for (iface = (cupsd_netif_t *)cupsArrayFirst(NetIFList);
4438              iface;
4439              iface = (cupsd_netif_t *)cupsArrayNext(NetIFList))
4440         {
4441          /*
4442           * Only send to local, IPv4 interfaces...
4443           */
4444
4445           if (!iface->is_local || !iface->port ||
4446               iface->address.addr.sa_family != AF_INET)
4447             continue;
4448
4449           httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
4450                            iface->hostname, iface->port,
4451                            (p->type & CUPS_PRINTER_CLASS) ? "/classes/%s" :
4452                                                             "/printers/%s",
4453                            p->name);
4454           snprintf(packet, sizeof(packet),
4455                    "%x %x %s \"%s\" \"%s\" \"%s\" %s%s uuid=%s\n",
4456                    type, p->state, uri, location, info, make_model,
4457                    p->browse_attrs ? p->browse_attrs : "", air, p->uuid);
4458
4459           bytes = strlen(packet);
4460
4461           cupsdLogMessage(CUPSD_LOG_DEBUG2,
4462                           "cupsdSendBrowseList: (%d bytes to \"%s\") %s", bytes,
4463                           iface->name, packet);
4464
4465           iface->broadcast.ipv4.sin_port = htons(BrowsePort);
4466
4467           sendto(BrowseSocket, packet, bytes, 0,
4468                  (struct sockaddr *)&(iface->broadcast),
4469                  httpAddrLength(&(iface->broadcast)));
4470         }
4471       }
4472       else if ((iface = cupsdNetIFFind(b->iface)) != NULL)
4473       {
4474        /*
4475         * Send to the named interface using the IPv4 address...
4476         */
4477
4478         while (iface)
4479           if (strcmp(b->iface, iface->name))
4480           {
4481             iface = NULL;
4482             break;
4483           }
4484           else if (iface->address.addr.sa_family == AF_INET && iface->port)
4485             break;
4486           else
4487             iface = (cupsd_netif_t *)cupsArrayNext(NetIFList);
4488
4489         if (iface)
4490         {
4491           httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
4492                            iface->hostname, iface->port,
4493                            (p->type & CUPS_PRINTER_CLASS) ? "/classes/%s" :
4494                                                             "/printers/%s",
4495                            p->name);
4496           snprintf(packet, sizeof(packet),
4497                    "%x %x %s \"%s\" \"%s\" \"%s\" %s%s uuid=%s\n",
4498                    type, p->state, uri, location, info, make_model,
4499                    p->browse_attrs ? p->browse_attrs : "", air, p->uuid);
4500
4501           bytes = strlen(packet);
4502
4503           cupsdLogMessage(CUPSD_LOG_DEBUG2,
4504                           "cupsdSendBrowseList: (%d bytes to \"%s\") %s", bytes,
4505                           iface->name, packet);
4506
4507           iface->broadcast.ipv4.sin_port = htons(BrowsePort);
4508
4509           sendto(BrowseSocket, packet, bytes, 0,
4510                  (struct sockaddr *)&(iface->broadcast),
4511                  httpAddrLength(&(iface->broadcast)));
4512         }
4513       }
4514     }
4515     else
4516     {
4517      /*
4518       * Send the browse packet to the indicated address using
4519       * the default server name...
4520       */
4521
4522       snprintf(packet, sizeof(packet),
4523                "%x %x %s \"%s\" \"%s\" \"%s\" %s%s uuid=%s\n",
4524                type, p->state, p->uri, location, info, make_model,
4525                p->browse_attrs ? p->browse_attrs : "", air, p->uuid);
4526
4527       bytes = strlen(packet);
4528       cupsdLogMessage(CUPSD_LOG_DEBUG2,
4529                       "cupsdSendBrowseList: (%d bytes) %s", bytes, packet);
4530
4531       if (sendto(BrowseSocket, packet, bytes, 0,
4532                  (struct sockaddr *)&(b->to),
4533                  httpAddrLength(&(b->to))) <= 0)
4534       {
4535        /*
4536         * Unable to send browse packet, so remove this address from the
4537         * list...
4538         */
4539
4540         cupsdLogMessage(CUPSD_LOG_ERROR,
4541                         "cupsdSendBrowseList: sendto failed for browser "
4542                         "%d - %s.",
4543                         (int)(b - Browsers + 1), strerror(errno));
4544
4545         if (i > 1)
4546           memmove(b, b + 1, (i - 1) * sizeof(cupsd_dirsvc_addr_t));
4547
4548         b --;
4549         NumBrowsers --;
4550       }
4551     }
4552 }
4553
4554
4555 #ifdef HAVE_LDAP
4556 /*
4557  * 'ldap_search_rec()' - LDAP Search with reconnect
4558  */
4559
4560 static int                              /* O - Return code */
4561 ldap_search_rec(LDAP        *ld,        /* I - LDAP handler */
4562                 char        *base,      /* I - Base dn */
4563                 int         scope,      /* I - LDAP search scope */
4564                 char        *filter,    /* I - Filter string */
4565                 char        *attrs[],   /* I - Requested attributes */
4566                 int         attrsonly,  /* I - Return only attributes? */
4567                 LDAPMessage **res)      /* I - LDAP handler */
4568 {
4569   int   rc;                             /* Return code */
4570   LDAP  *ldr;                           /* LDAP handler after reconnect */
4571
4572
4573 #  if defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000
4574   rc = ldap_search_ext_s(ld, base, scope, filter, attrs, attrsonly, NULL, NULL,
4575                          NULL, LDAP_NO_LIMIT, res);
4576 #  else
4577   rc = ldap_search_s(ld, base, scope, filter, attrs, attrsonly, res);
4578 #  endif /* defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 */
4579
4580  /*
4581   * If we have a connection problem try again...
4582   */
4583
4584   if (rc == LDAP_SERVER_DOWN || rc == LDAP_CONNECT_ERROR)
4585   {
4586     cupsdLogMessage(CUPSD_LOG_ERROR,
4587                     "LDAP search failed with status %d: %s",
4588                      rc, ldap_err2string(rc));
4589     cupsdLogMessage(CUPSD_LOG_INFO,
4590                     "We try the LDAP search once again after reconnecting to "
4591                     "the server");
4592     ldap_freeres(*res);
4593     ldr = ldap_reconnect();
4594
4595 #  if defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000
4596     rc = ldap_search_ext_s(ldr, base, scope, filter, attrs, attrsonly, NULL,
4597                            NULL, NULL, LDAP_NO_LIMIT, res);
4598 #  else
4599     rc = ldap_search_s(ldr, base, scope, filter, attrs, attrsonly, res);
4600 #  endif /* defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 */
4601   }
4602
4603   if (rc == LDAP_NO_SUCH_OBJECT)
4604     cupsdLogMessage(CUPSD_LOG_DEBUG,
4605                     "ldap_search_rec: LDAP entry/object not found");
4606   else if (rc != LDAP_SUCCESS)
4607     cupsdLogMessage(CUPSD_LOG_ERROR,
4608                     "ldap_search_rec: LDAP search failed with status %d: %s",
4609                      rc, ldap_err2string(rc));
4610
4611   if (rc != LDAP_SUCCESS)
4612     ldap_freeres(*res);
4613
4614   return (rc);
4615 }
4616
4617
4618 /*
4619  * 'ldap_freeres()' - Free LDAPMessage
4620  */
4621
4622 static void
4623 ldap_freeres(LDAPMessage *entry)        /* I - LDAP handler */
4624 {
4625   int   rc;                             /* Return value */
4626
4627
4628   rc = ldap_msgfree(entry);
4629   if (rc == -1)
4630     cupsdLogMessage(CUPSD_LOG_WARN, "Can't free LDAPMessage!");
4631   else if (rc == 0)
4632     cupsdLogMessage(CUPSD_LOG_DEBUG2, "Freeing LDAPMessage was unnecessary");
4633 }
4634
4635
4636 /*
4637  * 'ldap_getval_char()' - Get first LDAP value and convert to string
4638  */
4639
4640 static int                              /* O - Return code */
4641 ldap_getval_firststring(
4642     LDAP          *ld,                  /* I - LDAP handler */
4643     LDAPMessage   *entry,               /* I - LDAP message or search result */
4644     char          *attr,                /* I - the wanted attribute  */
4645     char          *retval,              /* O - String to return */
4646     unsigned long maxsize)              /* I - Max string size */
4647 {
4648   char                  *dn;            /* LDAP DN */
4649   int                   rc = 0;         /* Return code */
4650 #  if defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000
4651   struct berval         **bval;         /* LDAP value array */
4652   unsigned long         size;           /* String size */
4653
4654
4655  /*
4656   * Get value from LDAPMessage...
4657   */
4658
4659   if ((bval = ldap_get_values_len(ld, entry, attr)) == NULL)
4660   {
4661     rc = -1;
4662     dn = ldap_get_dn(ld, entry);
4663     cupsdLogMessage(CUPSD_LOG_WARN,
4664                     "Failed to get LDAP value %s for %s!",
4665                     attr, dn);
4666     ldap_memfree(dn);
4667   }
4668   else
4669   {
4670    /*
4671     * Check size and copy value into our string...
4672     */
4673
4674     size = maxsize;
4675     if (size < (bval[0]->bv_len + 1))
4676     {
4677       rc = -1;
4678       dn = ldap_get_dn(ld, entry);
4679       cupsdLogMessage(CUPSD_LOG_WARN,
4680                       "Attribute %s is too big! (dn: %s)",
4681                       attr, dn);
4682       ldap_memfree(dn);
4683     }
4684     else
4685       size = bval[0]->bv_len + 1;
4686
4687     strlcpy(retval, bval[0]->bv_val, size);
4688     ldap_value_free_len(bval);
4689   }
4690 #  else
4691   char  **value;                        /* LDAP value */
4692
4693  /*
4694   * Get value from LDAPMessage...
4695   */
4696
4697   if ((value = (char **)ldap_get_values(ld, entry, attr)) == NULL)
4698   {
4699     rc = -1;
4700     dn = ldap_get_dn(ld, entry);
4701     cupsdLogMessage(CUPSD_LOG_WARN, "Failed to get LDAP value %s for %s!",
4702                     attr, dn);
4703     ldap_memfree(dn);
4704   }
4705   else
4706   {
4707     strlcpy(retval, *value, maxsize);
4708     ldap_value_free(value);
4709   }
4710 #  endif /* defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 */
4711
4712   return (rc);
4713 }
4714
4715
4716 /*
4717  * 'send_ldap_ou()' - Send LDAP ou registrations.
4718  */
4719
4720 static void
4721 send_ldap_ou(char *ou,                  /* I - Servername/ou to register */
4722              char *basedn,              /* I - Our base dn */
4723              char *descstring)          /* I - Description for ou */
4724 {
4725   int           i;                      /* Looping var... */
4726   LDAPMod       mods[3];                /* The 3 attributes we will be adding */
4727   LDAPMod       *pmods[4];              /* Pointers to the 3 attributes + NULL */
4728   LDAPMessage   *res,                   /* Search result token */
4729                 *e;                     /* Current entry from search */
4730   int           rc;                     /* LDAP status */
4731   int           rcmod;                  /* LDAP status for modifications */
4732   char          dn[1024],               /* DN of the organizational unit we are adding */
4733                 *desc[2],               /* Change records */
4734                 *ou_value[2];
4735   char          old_desc[1024];         /* Old description */
4736   static const char * const objectClass_values[] =
4737                 {                       /* The 2 objectClass's we use in */
4738                   "top",                /* our LDAP entries              */
4739                   "organizationalUnit",
4740                   NULL
4741                 };
4742   static const char * const ou_attrs[] =/* CUPS LDAP attributes */
4743                 {
4744                   "description",
4745                   NULL
4746                 };
4747
4748
4749   cupsdLogMessage(CUPSD_LOG_DEBUG2, "send_ldap_ou: %s", ou);
4750
4751  /*
4752   * Reconnect if LDAP Handle is invalid...
4753   */
4754
4755   if (!BrowseLDAPHandle)
4756   {
4757     cupsdLogMessage(CUPSD_LOG_DEBUG2,
4758                     "send_ldap_ou: LDAP Handle is invalid. Try reconnecting...");
4759     ldap_reconnect();
4760     return;
4761   }
4762
4763  /*
4764   * Prepare ldap search...
4765   */
4766
4767   snprintf(dn, sizeof(dn), "ou=%s, %s", ou, basedn);
4768   cupsdLogMessage(CUPSD_LOG_DEBUG2, "send_ldap_ou: dn=\"%s\"", dn);
4769
4770   ou_value[0] = ou;
4771   ou_value[1] = NULL;
4772   desc[0]     = descstring;
4773   desc[1]     = NULL;
4774
4775   mods[0].mod_type   = "ou";
4776   mods[0].mod_values = ou_value;
4777   mods[1].mod_type   = "description";
4778   mods[1].mod_values = desc;
4779   mods[2].mod_type   = "objectClass";
4780   mods[2].mod_values = (char **)objectClass_values;
4781
4782   rc = ldap_search_rec(BrowseLDAPHandle, dn, LDAP_SCOPE_BASE, NULL,
4783                        (char **)ou_attrs, 0, &res);
4784
4785  /*
4786   * If ldap search was not successfull then exit function...
4787   */
4788
4789   if (rc != LDAP_SUCCESS && rc != LDAP_NO_SUCH_OBJECT)
4790     return;
4791
4792  /*
4793   * Check if we need to insert or update the LDAP entry...
4794   */
4795
4796   if (ldap_count_entries(BrowseLDAPHandle, res) > 0 &&
4797       rc != LDAP_NO_SUCH_OBJECT)
4798   {
4799    /*
4800     * Printserver has already been registered, check if
4801     * modification is required...
4802     */
4803
4804     e = ldap_first_entry(BrowseLDAPHandle, res);
4805
4806    /*
4807     * Get the required values from this entry...
4808     */
4809
4810     if (ldap_getval_firststring(BrowseLDAPHandle, e, "description", old_desc,
4811                                 sizeof(old_desc)) == -1)
4812       old_desc[0] = '\0';
4813
4814    /*
4815     * Check if modification is required...
4816     */
4817
4818     if ( strcmp(desc[0], old_desc) == 0 )
4819     {
4820      /*
4821       * LDAP entry for the printer exists.
4822       * Printer has already been registered,
4823       * no modifications required...
4824       */
4825       cupsdLogMessage(CUPSD_LOG_DEBUG2,
4826                       "send_ldap_ou: No updates required for %s", ou);
4827     }
4828     else
4829     {
4830
4831       cupsdLogMessage(CUPSD_LOG_DEBUG2,
4832                       "send_ldap_ou: Replace entry for %s", ou);
4833
4834       for (i = 0; i < 3; i ++)
4835       {
4836         pmods[i]         = mods + i;
4837         pmods[i]->mod_op = LDAP_MOD_REPLACE;
4838       }
4839       pmods[i] = NULL;
4840
4841 #  if defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000
4842       if ((rcmod = ldap_modify_ext_s(BrowseLDAPHandle, dn, pmods, NULL,
4843                                      NULL)) != LDAP_SUCCESS)
4844 #  else
4845       if ((rcmod = ldap_modify_s(BrowseLDAPHandle, dn, pmods)) != LDAP_SUCCESS)
4846 #  endif /* defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 */
4847       {
4848         cupsdLogMessage(CUPSD_LOG_ERROR,
4849                         "LDAP modify for %s failed with status %d: %s",
4850                         ou, rcmod, ldap_err2string(rcmod));
4851         if (rcmod == LDAP_SERVER_DOWN)
4852           ldap_reconnect();
4853       }
4854     }
4855   }
4856   else
4857   {
4858    /*
4859     * Printserver has never been registered,
4860     * add registration...
4861     */
4862
4863     cupsdLogMessage(CUPSD_LOG_DEBUG2,
4864                     "send_ldap_ou: Add entry for %s", ou);
4865
4866     for (i = 0; i < 3; i ++)
4867     {
4868       pmods[i]         = mods + i;
4869       pmods[i]->mod_op = LDAP_MOD_ADD;
4870     }
4871     pmods[i] = NULL;
4872
4873 #  if defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000
4874     if ((rcmod = ldap_add_ext_s(BrowseLDAPHandle, dn, pmods, NULL,
4875                                 NULL)) != LDAP_SUCCESS)
4876 #  else
4877     if ((rcmod = ldap_add_s(BrowseLDAPHandle, dn, pmods)) != LDAP_SUCCESS)
4878 #  endif /* defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 */
4879     {
4880       cupsdLogMessage(CUPSD_LOG_ERROR,
4881                       "LDAP add for %s failed with status %d: %s",
4882                       ou, rcmod, ldap_err2string(rcmod));
4883       if (rcmod == LDAP_SERVER_DOWN)
4884         ldap_reconnect();
4885     }
4886   }
4887
4888   if (rc == LDAP_SUCCESS)
4889     ldap_freeres(res);
4890 }
4891
4892
4893 /*
4894  * 'send_ldap_browse()' - Send LDAP printer registrations.
4895  */
4896
4897 static void
4898 send_ldap_browse(cupsd_printer_t *p)    /* I - Printer to register */
4899 {
4900   int           i;                      /* Looping var... */
4901   LDAPMod       mods[7];                /* The 7 attributes we will be adding */
4902   LDAPMod       *pmods[8];              /* Pointers to the 7 attributes + NULL */
4903   LDAPMessage   *res,                   /* Search result token */
4904                 *e;                     /* Current entry from search */
4905   char          *cn_value[2],           /* Change records */
4906                 *uri[2],
4907                 *info[2],
4908                 *location[2],
4909                 *make_model[2],
4910                 *type[2],
4911                 typestring[255],        /* String to hold printer-type */
4912                 dn[1024];               /* DN of the printer we are adding */
4913   int           rc;                     /* LDAP status */
4914   int           rcmod;                  /* LDAP status for modifications */
4915   char          old_uri[HTTP_MAX_URI],  /* Printer URI */
4916                 old_location[1024],     /* Printer location */
4917                 old_info[1024],         /* Printer information */
4918                 old_make_model[1024],   /* Printer make and model */
4919                 old_type_string[30];    /* Temporary type number */
4920   int           old_type;               /* Printer type */
4921   static const char * const objectClass_values[] =
4922                 {                       /* The 3 objectClass's we use in */
4923                   "top",                /* our LDAP entries              */
4924                   "device",
4925                   "cupsPrinter",
4926                   NULL
4927                 };
4928
4929
4930   cupsdLogMessage(CUPSD_LOG_DEBUG2, "send_ldap_browse: %s", p->name);
4931
4932  /*
4933   * Exit function if LDAP updates has been disabled...
4934   */
4935
4936   if (!BrowseLDAPUpdate)
4937   {
4938     cupsdLogMessage(CUPSD_LOG_DEBUG2,
4939                     "send_ldap_browse: Updates temporary disabled; "
4940                     "skipping...");
4941     return;
4942   }
4943
4944  /*
4945   * Reconnect if LDAP Handle is invalid...
4946   */
4947
4948   if (!BrowseLDAPHandle)
4949   {
4950     cupsdLogMessage(CUPSD_LOG_DEBUG2,
4951                     "send_ldap_browse: LDAP Handle is invalid. Try "
4952                     "reconnecting...");
4953     ldap_reconnect();
4954     return;
4955   }
4956
4957  /*
4958   * Everything in ldap is ** so we fudge around it...
4959   */
4960
4961   sprintf(typestring, "%u", p->type);
4962
4963   cn_value[0]   = p->name;
4964   cn_value[1]   = NULL;
4965   info[0]       = p->info ? p->info : "Unknown";
4966   info[1]       = NULL;
4967   location[0]   = p->location ? p->location : "Unknown";
4968   location[1]   = NULL;
4969   make_model[0] = p->make_model ? p->make_model : "Unknown";
4970   make_model[1] = NULL;
4971   type[0]       = typestring;
4972   type[1]       = NULL;
4973   uri[0]        = p->uri;
4974   uri[1]        = NULL;
4975
4976  /*
4977   * Get ldap entry for printer ...
4978   */
4979
4980   snprintf(dn, sizeof(dn), "cn=%s, ou=%s, %s", p->name, ServerName,
4981            BrowseLDAPDN);
4982   cupsdLogMessage(CUPSD_LOG_DEBUG2, "send_ldap_browse: dn=\"%s\"", dn);
4983
4984   rc = ldap_search_rec(BrowseLDAPHandle, dn, LDAP_SCOPE_BASE, NULL,
4985                        (char **)ldap_attrs, 0, &res);
4986
4987  /*
4988   * If ldap search was not successfull then exit function
4989   * and temporary disable LDAP updates...
4990   */
4991
4992   if (rc != LDAP_SUCCESS && rc != LDAP_NO_SUCH_OBJECT)
4993   {
4994     if (BrowseLDAPUpdate &&
4995         (rc == LDAP_SERVER_DOWN || rc == LDAP_CONNECT_ERROR))
4996     {
4997       BrowseLDAPUpdate = FALSE;
4998       cupsdLogMessage(CUPSD_LOG_INFO,
4999                       "LDAP update temporary disabled");
5000     }
5001
5002     return;
5003   }
5004
5005  /*
5006   * Fill modification array...
5007   */
5008
5009   mods[0].mod_type   = "cn";
5010   mods[0].mod_values = cn_value;
5011   mods[1].mod_type   = "printerDescription";
5012   mods[1].mod_values = info;
5013   mods[2].mod_type   = "printerURI";
5014   mods[2].mod_values = uri;
5015   mods[3].mod_type   = "printerLocation";
5016   mods[3].mod_values = location;
5017   mods[4].mod_type   = "printerMakeAndModel";
5018   mods[4].mod_values = make_model;
5019   mods[5].mod_type   = "printerType";
5020   mods[5].mod_values = type;
5021   mods[6].mod_type   = "objectClass";
5022   mods[6].mod_values = (char **)objectClass_values;
5023
5024  /*
5025   * Check if we need to insert or update the LDAP entry...
5026   */
5027
5028   if (ldap_count_entries(BrowseLDAPHandle, res) > 0 &&
5029       rc != LDAP_NO_SUCH_OBJECT)
5030   {
5031    /*
5032     * Printer has already been registered, check if
5033     * modification is required...
5034     */
5035
5036     e = ldap_first_entry(BrowseLDAPHandle, res);
5037
5038    /*
5039     * Get the required values from this entry...
5040     */
5041
5042     if (ldap_getval_firststring(BrowseLDAPHandle, e, "printerDescription",
5043                                 old_info, sizeof(old_info)) == -1)
5044       old_info[0] = '\0';
5045
5046     if (ldap_getval_firststring(BrowseLDAPHandle, e, "printerLocation",
5047                                 old_location, sizeof(old_location)) == -1)
5048       old_info[0] = '\0';
5049
5050     if (ldap_getval_firststring(BrowseLDAPHandle, e, "printerMakeAndModel",
5051                                 old_make_model, sizeof(old_make_model)) == -1)
5052       old_info[0] = '\0';
5053
5054     if (ldap_getval_firststring(BrowseLDAPHandle, e, "printerType",
5055                                 old_type_string, sizeof(old_type_string)) == -1)
5056       old_info[0] = '\0';
5057
5058     old_type = atoi(old_type_string);
5059
5060     if (ldap_getval_firststring(BrowseLDAPHandle, e, "printerURI", old_uri,
5061                                 sizeof(old_uri)) == -1)
5062       old_info[0] = '\0';
5063
5064    /*
5065     * Check if modification is required...
5066     */
5067
5068     if (!strcmp(info[0], old_info) && !strcmp(uri[0], old_uri) &&
5069         !strcmp(location[0], old_location) &&
5070         !strcmp(make_model[0], old_make_model) && p->type == old_type)
5071     {
5072      /*
5073       * LDAP entry for the printer exists. Printer has already been registered,
5074       * no modifications required...
5075       */
5076
5077       cupsdLogMessage(CUPSD_LOG_DEBUG2,
5078                        "send_ldap_browse: No updates required for %s", p->name);
5079     }
5080     else
5081     {
5082      /*
5083       * LDAP entry for the printer exists.  Printer has already been registered,
5084       * modify the current registration...
5085       */
5086
5087       cupsdLogMessage(CUPSD_LOG_DEBUG2,
5088                       "send_ldap_browse: Replace entry for %s", p->name);
5089
5090       for (i = 0; i < 7; i ++)
5091       {
5092         pmods[i]         = mods + i;
5093         pmods[i]->mod_op = LDAP_MOD_REPLACE;
5094       }
5095       pmods[i] = NULL;
5096
5097 #  if defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000
5098       if ((rcmod = ldap_modify_ext_s(BrowseLDAPHandle, dn, pmods, NULL,
5099                                      NULL)) != LDAP_SUCCESS)
5100 #  else
5101       if ((rcmod = ldap_modify_s(BrowseLDAPHandle, dn, pmods)) != LDAP_SUCCESS)
5102 #  endif /* defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 */
5103       {
5104         cupsdLogMessage(CUPSD_LOG_ERROR,
5105                         "LDAP modify for %s failed with status %d: %s",
5106                         p->name, rcmod, ldap_err2string(rcmod));
5107         if (rcmod == LDAP_SERVER_DOWN)
5108           ldap_reconnect();
5109       }
5110     }
5111   }
5112   else
5113   {
5114    /*
5115     * No LDAP entry exists for the printer.  Printer has never been registered,
5116     * add the current registration...
5117     */
5118
5119     send_ldap_ou(ServerName, BrowseLDAPDN, "CUPS Server");
5120
5121     cupsdLogMessage(CUPSD_LOG_DEBUG2,
5122                     "send_ldap_browse: Add entry for %s", p->name);
5123
5124     for (i = 0; i < 7; i ++)
5125     {
5126       pmods[i]         = mods + i;
5127       pmods[i]->mod_op = LDAP_MOD_ADD;
5128     }
5129     pmods[i] = NULL;
5130
5131 #  if defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000
5132     if ((rcmod = ldap_add_ext_s(BrowseLDAPHandle, dn, pmods, NULL,
5133                                 NULL)) != LDAP_SUCCESS)
5134 #  else
5135     if ((rcmod = ldap_add_s(BrowseLDAPHandle, dn, pmods)) != LDAP_SUCCESS)
5136 #  endif /* defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 */
5137     {
5138       cupsdLogMessage(CUPSD_LOG_ERROR,
5139                       "LDAP add for %s failed with status %d: %s",
5140                       p->name, rcmod, ldap_err2string(rcmod));
5141       if (rcmod == LDAP_SERVER_DOWN)
5142         ldap_reconnect();
5143     }
5144   }
5145
5146   if (rc == LDAP_SUCCESS)
5147     ldap_freeres(res);
5148 }
5149
5150
5151 /*
5152  * 'ldap_dereg_printer()' - Delete printer from directory
5153  */
5154
5155 static void
5156 ldap_dereg_printer(cupsd_printer_t *p)  /* I - Printer to deregister */
5157 {
5158   char          dn[1024];               /* DN of the printer */
5159   int           rc;                     /* LDAP status */
5160
5161
5162   cupsdLogMessage(CUPSD_LOG_DEBUG2, "ldap_dereg_printer: Remove entry for %s",
5163                   p->name);
5164
5165  /*
5166   * Reconnect if LDAP Handle is invalid...
5167   */
5168
5169   if (!BrowseLDAPHandle)
5170   {
5171     ldap_reconnect();
5172     return;
5173   }
5174
5175  /*
5176   * Get dn for printer and delete LDAP entry...
5177   */
5178
5179   snprintf(dn, sizeof(dn), "cn=%s, ou=%s, %s", p->name, ServerName,
5180            BrowseLDAPDN);
5181   cupsdLogMessage(CUPSD_LOG_DEBUG2, "ldap_dereg_printer: dn=\"%s\"", dn);
5182
5183 #  if defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000
5184   if ((rc = ldap_delete_ext_s(BrowseLDAPHandle, dn, NULL,
5185                               NULL)) != LDAP_SUCCESS)
5186 #  else
5187   if ((rc = ldap_delete_s(BrowseLDAPHandle, dn)) != LDAP_SUCCESS)
5188 #  endif /* defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 */
5189   {
5190     cupsdLogMessage(CUPSD_LOG_WARN,
5191                     "LDAP delete for %s failed with status %d: %s",
5192                     p->name, rc, ldap_err2string(rc));
5193
5194    /*
5195     * If we had a connection problem (connection timed out, etc.)
5196     * we should reconnect and try again to delete the entry...
5197     */
5198
5199     if (rc == LDAP_SERVER_DOWN || rc == LDAP_CONNECT_ERROR)
5200     {
5201       cupsdLogMessage(CUPSD_LOG_INFO,
5202                       "Retry deleting LDAP entry for %s after a reconnect...", p->name);
5203       ldap_reconnect();
5204
5205 #  if defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000
5206       if ((rc = ldap_delete_ext_s(BrowseLDAPHandle, dn, NULL,
5207                                   NULL)) != LDAP_SUCCESS)
5208 #  else
5209       if ((rc = ldap_delete_s(BrowseLDAPHandle, dn)) != LDAP_SUCCESS)
5210 #  endif /* defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 */
5211         cupsdLogMessage(CUPSD_LOG_WARN,
5212                         "LDAP delete for %s failed with status %d: %s",
5213                         p->name, rc, ldap_err2string(rc));
5214     }
5215   }
5216 }
5217
5218
5219 /*
5220  * 'ldap_dereg_ou()' - Remove the organizational unit.
5221  */
5222
5223 static void
5224 ldap_dereg_ou(char *ou,                 /* I - Organizational unit (servername) */
5225               char *basedn)             /* I - Dase dn */
5226 {
5227   char          dn[1024];               /* DN of the printer */
5228   int           rc;                     /* LDAP status */
5229
5230
5231   cupsdLogMessage(CUPSD_LOG_DEBUG2, "ldap_dereg_ou: Remove entry for %s", ou);
5232
5233  /*
5234   * Reconnect if LDAP Handle is invalid...
5235   */
5236
5237   if (!BrowseLDAPHandle)
5238   {
5239     ldap_reconnect();
5240     return;
5241   }
5242
5243  /*
5244   * Get dn for printer and delete LDAP entry...
5245   */
5246
5247   snprintf(dn, sizeof(dn), "ou=%s, %s", ou, basedn);
5248   cupsdLogMessage(CUPSD_LOG_DEBUG2, "ldap_dereg_ou: dn=\"%s\"", dn);
5249
5250 #  if defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000
5251   if ((rc = ldap_delete_ext_s(BrowseLDAPHandle, dn, NULL,
5252                               NULL)) != LDAP_SUCCESS)
5253 #  else
5254   if ((rc = ldap_delete_s(BrowseLDAPHandle, dn)) != LDAP_SUCCESS)
5255 #  endif /* defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 */
5256   {
5257     cupsdLogMessage(CUPSD_LOG_WARN,
5258                     "LDAP delete for %s failed with status %d: %s",
5259                     ou, rc, ldap_err2string(rc));
5260
5261    /*
5262     * If we had a connection problem (connection timed out, etc.)
5263     * we should reconnect and try again to delete the entry...
5264     */
5265
5266     if (rc == LDAP_SERVER_DOWN || rc == LDAP_CONNECT_ERROR)
5267     {
5268       cupsdLogMessage(CUPSD_LOG_INFO,
5269                       "Retry deleting LDAP entry for %s after a reconnect...", ou);
5270       ldap_reconnect();
5271 #  if defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000
5272       if ((rc = ldap_delete_ext_s(BrowseLDAPHandle, dn, NULL,
5273                                   NULL)) != LDAP_SUCCESS)
5274 #  else
5275       if ((rc = ldap_delete_s(BrowseLDAPHandle, dn)) != LDAP_SUCCESS)
5276 #  endif /* defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 */
5277         cupsdLogMessage(CUPSD_LOG_WARN,
5278                         "LDAP delete for %s failed with status %d: %s",
5279                         ou, rc, ldap_err2string(rc));
5280     }
5281   }
5282 }
5283 #endif /* HAVE_LDAP */
5284
5285
5286 #ifdef HAVE_LIBSLP
5287 /*
5288  * 'send_slp_browse()' - Register the specified printer with SLP.
5289  */
5290
5291 static void
5292 send_slp_browse(cupsd_printer_t *p)     /* I - Printer to register */
5293 {
5294   char          srvurl[HTTP_MAX_URI],   /* Printer service URI */
5295                 attrs[8192],            /* Printer attributes */
5296                 finishings[1024],       /* Finishings to support */
5297                 make_model[IPP_MAX_NAME * 2],
5298                                         /* Make and model, quoted */
5299                 location[IPP_MAX_NAME * 2],
5300                                         /* Location, quoted */
5301                 info[IPP_MAX_NAME * 2], /* Info, quoted */
5302                 *src,                   /* Pointer to original string */
5303                 *dst;                   /* Pointer to destination string */
5304   ipp_attribute_t *authentication;      /* uri-authentication-supported value */
5305   SLPError      error;                  /* SLP error, if any */
5306
5307
5308   cupsdLogMessage(CUPSD_LOG_DEBUG, "send_slp_browse(%p = \"%s\")", p,
5309                   p->name);
5310
5311  /*
5312   * Make the SLP service URL that conforms to the IANA
5313   * 'printer:' template.
5314   */
5315
5316   snprintf(srvurl, sizeof(srvurl), SLP_CUPS_SRVTYPE ":%s", p->uri);
5317
5318   cupsdLogMessage(CUPSD_LOG_DEBUG2, "Service URL = \"%s\"", srvurl);
5319
5320  /*
5321   * Figure out the finishings string...
5322   */
5323
5324   if (p->type & CUPS_PRINTER_STAPLE)
5325     strcpy(finishings, "staple");
5326   else
5327     finishings[0] = '\0';
5328
5329   if (p->type & CUPS_PRINTER_BIND)
5330   {
5331     if (finishings[0])
5332       strlcat(finishings, ",bind", sizeof(finishings));
5333     else
5334       strcpy(finishings, "bind");
5335   }
5336
5337   if (p->type & CUPS_PRINTER_PUNCH)
5338   {
5339     if (finishings[0])
5340       strlcat(finishings, ",punch", sizeof(finishings));
5341     else
5342       strcpy(finishings, "punch");
5343   }
5344
5345   if (p->type & CUPS_PRINTER_COVER)
5346   {
5347     if (finishings[0])
5348       strlcat(finishings, ",cover", sizeof(finishings));
5349     else
5350       strcpy(finishings, "cover");
5351   }
5352
5353   if (p->type & CUPS_PRINTER_SORT)
5354   {
5355     if (finishings[0])
5356       strlcat(finishings, ",sort", sizeof(finishings));
5357     else
5358       strcpy(finishings, "sort");
5359   }
5360
5361   if (!finishings[0])
5362     strcpy(finishings, "none");
5363
5364  /*
5365   * Quote any commas in the make and model, location, and info strings...
5366   */
5367
5368   for (src = p->make_model, dst = make_model;
5369        src && *src && dst < (make_model + sizeof(make_model) - 2);)
5370   {
5371     if (*src == ',' || *src == '\\' || *src == ')')
5372       *dst++ = '\\';
5373
5374     *dst++ = *src++;
5375   }
5376
5377   *dst = '\0';
5378
5379   if (!make_model[0])
5380     strcpy(make_model, "Unknown");
5381
5382   for (src = p->location, dst = location;
5383        src && *src && dst < (location + sizeof(location) - 2);)
5384   {
5385     if (*src == ',' || *src == '\\' || *src == ')')
5386       *dst++ = '\\';
5387
5388     *dst++ = *src++;
5389   }
5390
5391   *dst = '\0';
5392
5393   if (!location[0])
5394     strcpy(location, "Unknown");
5395
5396   for (src = p->info, dst = info;
5397        src && *src && dst < (info + sizeof(info) - 2);)
5398   {
5399     if (*src == ',' || *src == '\\' || *src == ')')
5400       *dst++ = '\\';
5401
5402     *dst++ = *src++;
5403   }
5404
5405   *dst = '\0';
5406
5407   if (!info[0])
5408     strcpy(info, "Unknown");
5409
5410  /*
5411   * Get the authentication value...
5412   */
5413
5414   authentication = ippFindAttribute(p->attrs, "uri-authentication-supported",
5415                                     IPP_TAG_KEYWORD);
5416
5417  /*
5418   * Make the SLP attribute string list that conforms to
5419   * the IANA 'printer:' template.
5420   */
5421
5422   snprintf(attrs, sizeof(attrs),
5423            "(printer-uri-supported=%s),"
5424            "(uri-authentication-supported=%s>),"
5425 #ifdef HAVE_SSL
5426            "(uri-security-supported=tls>),"
5427 #else
5428            "(uri-security-supported=none>),"
5429 #endif /* HAVE_SSL */
5430            "(printer-name=%s),"
5431            "(printer-location=%s),"
5432            "(printer-info=%s),"
5433            "(printer-more-info=%s),"
5434            "(printer-make-and-model=%s),"
5435            "(printer-type=%d),"
5436            "(charset-supported=utf-8),"
5437            "(natural-language-configured=%s),"
5438            "(natural-language-supported=de,en,es,fr,it),"
5439            "(color-supported=%s),"
5440            "(finishings-supported=%s),"
5441            "(sides-supported=one-sided%s),"
5442            "(multiple-document-jobs-supported=true)"
5443            "(ipp-versions-supported=1.0,1.1)",
5444            p->uri, authentication->values[0].string.text, p->name, location,
5445            info, p->uri, make_model, p->type, DefaultLanguage,
5446            p->type & CUPS_PRINTER_COLOR ? "true" : "false",
5447            finishings,
5448            p->type & CUPS_PRINTER_DUPLEX ?
5449                ",two-sided-long-edge,two-sided-short-edge" : "");
5450
5451   cupsdLogMessage(CUPSD_LOG_DEBUG2, "Attributes = \"%s\"", attrs);
5452
5453  /*
5454   * Register the printer with the SLP server...
5455   */
5456
5457   error = SLPReg(BrowseSLPHandle, srvurl, BrowseTimeout,
5458                  SLP_CUPS_SRVTYPE, attrs, SLP_TRUE, slp_reg_callback, 0);
5459
5460   if (error != SLP_OK)
5461     cupsdLogMessage(CUPSD_LOG_ERROR, "SLPReg of \"%s\" failed with status %d!", p->name,
5462                     error);
5463 }
5464
5465
5466 /*
5467  * 'slp_attr_callback()' - SLP attribute callback
5468  */
5469
5470 static SLPBoolean                       /* O - SLP_TRUE for success */
5471 slp_attr_callback(
5472     SLPHandle  hslp,                    /* I - SLP handle */
5473     const char *attrlist,               /* I - Attribute list */
5474     SLPError   errcode,                 /* I - Parsing status for this attr */
5475     void       *cookie)                 /* I - Current printer */
5476 {
5477   char                  *tmp = 0;       /* Temporary string */
5478   cupsd_printer_t       *p = (cupsd_printer_t*)cookie;
5479                                         /* Current printer */
5480
5481
5482   (void)hslp;                           /* anti-compiler-warning-code */
5483
5484  /*
5485   * Bail if there was an error
5486   */
5487
5488   if (errcode != SLP_OK)
5489     return (SLP_TRUE);
5490
5491  /*
5492   * Parse the attrlist to obtain things needed to build CUPS browse packet
5493   */
5494
5495   memset(p, 0, sizeof(cupsd_printer_t));
5496
5497   if (slp_get_attr(attrlist, "(printer-location=", &(p->location)))
5498     return (SLP_FALSE);
5499   if (slp_get_attr(attrlist, "(printer-info=", &(p->info)))
5500     return (SLP_FALSE);
5501   if (slp_get_attr(attrlist, "(printer-make-and-model=", &(p->make_model)))
5502     return (SLP_FALSE);
5503   if (!slp_get_attr(attrlist, "(printer-type=", &tmp))
5504     p->type = atoi(tmp);
5505   else
5506     p->type = CUPS_PRINTER_REMOTE;
5507
5508   cupsdClearString(&tmp);
5509
5510   return (SLP_TRUE);
5511 }
5512
5513
5514 /*
5515  * 'slp_dereg_printer()' - SLPDereg() the specified printer
5516  */
5517
5518 static void
5519 slp_dereg_printer(cupsd_printer_t *p)   /* I - Printer */
5520 {
5521   char  srvurl[HTTP_MAX_URI];           /* Printer service URI */
5522
5523
5524   cupsdLogMessage(CUPSD_LOG_DEBUG, "slp_dereg_printer: printer=\"%s\"", p->name);
5525
5526   if (!(p->type & CUPS_PRINTER_REMOTE))
5527   {
5528    /*
5529     * Make the SLP service URL that conforms to the IANA
5530     * 'printer:' template.
5531     */
5532
5533     snprintf(srvurl, sizeof(srvurl), SLP_CUPS_SRVTYPE ":%s", p->uri);
5534
5535    /*
5536     * Deregister the printer...
5537     */
5538
5539     SLPDereg(BrowseSLPHandle, srvurl, slp_reg_callback, 0);
5540   }
5541 }
5542
5543
5544 /*
5545  * 'slp_get_attr()' - Get an attribute from an SLP registration.
5546  */
5547
5548 static int                              /* O - 0 on success */
5549 slp_get_attr(const char *attrlist,      /* I - Attribute list string */
5550              const char *tag,           /* I - Name of attribute */
5551              char       **valbuf)       /* O - Value */
5552 {
5553   char  *ptr1,                          /* Pointer into string */
5554         *ptr2;                          /* ... */
5555
5556
5557   cupsdClearString(valbuf);
5558
5559   if ((ptr1 = strstr(attrlist, tag)) != NULL)
5560   {
5561     ptr1 += strlen(tag);
5562
5563     if ((ptr2 = strchr(ptr1,')')) != NULL)
5564     {
5565      /*
5566       * Copy the value...
5567       */
5568
5569       *valbuf = calloc(ptr2 - ptr1 + 1, 1);
5570       strncpy(*valbuf, ptr1, ptr2 - ptr1);
5571
5572      /*
5573       * Dequote the value...
5574       */
5575
5576       for (ptr1 = *valbuf; *ptr1; ptr1 ++)
5577         if (*ptr1 == '\\' && ptr1[1])
5578           _cups_strcpy(ptr1, ptr1 + 1);
5579
5580       return (0);
5581     }
5582   }
5583
5584   return (-1);
5585 }
5586
5587
5588 /*
5589  * 'slp_reg_callback()' - Empty SLPRegReport.
5590  */
5591
5592 static void
5593 slp_reg_callback(SLPHandle hslp,        /* I - SLP handle */
5594                  SLPError  errcode,     /* I - Error code, if any */
5595                  void      *cookie)     /* I - App data */
5596 {
5597   (void)hslp;
5598   (void)errcode;
5599   (void)cookie;
5600
5601   return;
5602 }
5603
5604
5605 /*
5606  * 'slp_url_callback()' - SLP service url callback
5607  */
5608
5609 static SLPBoolean                       /* O - TRUE = OK, FALSE = error */
5610 slp_url_callback(
5611     SLPHandle      hslp,                /* I - SLP handle */
5612     const char     *srvurl,             /* I - URL of service */
5613     unsigned short lifetime,            /* I - Life of service */
5614     SLPError       errcode,             /* I - Existing error code */
5615     void           *cookie)             /* I - Pointer to service list */
5616 {
5617   slpsrvurl_t   *s,                     /* New service entry */
5618                 **head;                 /* Pointer to head of entry */
5619
5620
5621  /*
5622   * Let the compiler know we won't be using these vars...
5623   */
5624
5625   (void)hslp;
5626   (void)lifetime;
5627
5628  /*
5629   * Bail if there was an error
5630   */
5631
5632   if (errcode != SLP_OK)
5633     return (SLP_TRUE);
5634
5635  /*
5636   * Grab the head of the list...
5637   */
5638
5639   head = (slpsrvurl_t**)cookie;
5640
5641  /*
5642   * Allocate a *temporary* slpsrvurl_t to hold this entry.
5643   */
5644
5645   if ((s = (slpsrvurl_t *)calloc(1, sizeof(slpsrvurl_t))) == NULL)
5646     return (SLP_FALSE);
5647
5648  /*
5649   * Copy the SLP service URL...
5650   */
5651
5652   strlcpy(s->url, srvurl, sizeof(s->url));
5653
5654  /*
5655   * Link the SLP service URL into the head of the list
5656   */
5657
5658   if (*head)
5659     s->next = *head;
5660
5661   *head = s;
5662
5663   return (SLP_TRUE);
5664 }
5665 #endif /* HAVE_LIBSLP */
5666
5667
5668 /*
5669  * 'update_cups_browse()' - Update the browse lists using the CUPS protocol.
5670  */
5671
5672 static void
5673 update_cups_browse(void)
5674 {
5675   int           i;                      /* Looping var */
5676   int           auth;                   /* Authorization status */
5677   int           len;                    /* Length of name string */
5678   int           bytes;                  /* Number of bytes left */
5679   char          packet[1541],           /* Broadcast packet */
5680                 *pptr;                  /* Pointer into packet */
5681   socklen_t     srclen;                 /* Length of source address */
5682   http_addr_t   srcaddr;                /* Source address */
5683   char          srcname[1024];          /* Source hostname */
5684   unsigned      address[4];             /* Source address */
5685   unsigned      type;                   /* Printer type */
5686   unsigned      state;                  /* Printer state */
5687   char          uri[HTTP_MAX_URI],      /* Printer URI */
5688                 host[HTTP_MAX_URI],     /* Host portion of URI */
5689                 resource[HTTP_MAX_URI], /* Resource portion of URI */
5690                 info[IPP_MAX_NAME],     /* Information string */
5691                 location[IPP_MAX_NAME], /* Location string */
5692                 make_model[IPP_MAX_NAME];/* Make and model string */
5693   int           num_attrs;              /* Number of attributes */
5694   cups_option_t *attrs;                 /* Attributes */
5695
5696
5697  /*
5698   * Read a packet from the browse socket...
5699   */
5700
5701   srclen = sizeof(srcaddr);
5702   if ((bytes = recvfrom(BrowseSocket, packet, sizeof(packet) - 1, 0,
5703                         (struct sockaddr *)&srcaddr, &srclen)) < 0)
5704   {
5705    /*
5706     * "Connection refused" is returned under Linux if the destination port
5707     * or address is unreachable from a previous sendto(); check for the
5708     * error here and ignore it for now...
5709     */
5710
5711     if (errno != ECONNREFUSED && errno != EAGAIN)
5712     {
5713       cupsdLogMessage(CUPSD_LOG_ERROR, "Browse recv failed - %s.",
5714                       strerror(errno));
5715       cupsdLogMessage(CUPSD_LOG_ERROR, "CUPS browsing turned off.");
5716
5717 #ifdef WIN32
5718       closesocket(BrowseSocket);
5719 #else
5720       close(BrowseSocket);
5721 #endif /* WIN32 */
5722
5723       cupsdRemoveSelect(BrowseSocket);
5724       BrowseSocket = -1;
5725
5726       BrowseLocalProtocols  &= ~BROWSE_CUPS;
5727       BrowseRemoteProtocols &= ~BROWSE_CUPS;
5728     }
5729
5730     return;
5731   }
5732
5733   packet[bytes] = '\0';
5734
5735  /*
5736   * If we're about to sleep, ignore incoming browse packets.
5737   */
5738
5739   if (Sleeping)
5740     return;
5741
5742  /*
5743   * Figure out where it came from...
5744   */
5745
5746 #ifdef AF_INET6
5747   if (srcaddr.addr.sa_family == AF_INET6)
5748   {
5749     address[0] = ntohl(srcaddr.ipv6.sin6_addr.s6_addr32[0]);
5750     address[1] = ntohl(srcaddr.ipv6.sin6_addr.s6_addr32[1]);
5751     address[2] = ntohl(srcaddr.ipv6.sin6_addr.s6_addr32[2]);
5752     address[3] = ntohl(srcaddr.ipv6.sin6_addr.s6_addr32[3]);
5753   }
5754   else
5755 #endif /* AF_INET6 */
5756   {
5757     address[0] = 0;
5758     address[1] = 0;
5759     address[2] = 0;
5760     address[3] = ntohl(srcaddr.ipv4.sin_addr.s_addr);
5761   }
5762
5763   if (HostNameLookups)
5764     httpAddrLookup(&srcaddr, srcname, sizeof(srcname));
5765   else
5766     httpAddrString(&srcaddr, srcname, sizeof(srcname));
5767
5768   len = strlen(srcname);
5769
5770  /*
5771   * Do ACL stuff...
5772   */
5773
5774   if (BrowseACL)
5775   {
5776     if (httpAddrLocalhost(&srcaddr) || !_cups_strcasecmp(srcname, "localhost"))
5777     {
5778      /*
5779       * Access from localhost (127.0.0.1) is always allowed...
5780       */
5781
5782       auth = CUPSD_AUTH_ALLOW;
5783     }
5784     else
5785     {
5786      /*
5787       * Do authorization checks on the domain/address...
5788       */
5789
5790       switch (BrowseACL->order_type)
5791       {
5792         default :
5793             auth = CUPSD_AUTH_DENY;     /* anti-compiler-warning-code */
5794             break;
5795
5796         case CUPSD_AUTH_ALLOW : /* Order Deny,Allow */
5797             auth = CUPSD_AUTH_ALLOW;
5798
5799             if (cupsdCheckAuth(address, srcname, len, BrowseACL->deny))
5800               auth = CUPSD_AUTH_DENY;
5801
5802             if (cupsdCheckAuth(address, srcname, len, BrowseACL->allow))
5803               auth = CUPSD_AUTH_ALLOW;
5804             break;
5805
5806         case CUPSD_AUTH_DENY : /* Order Allow,Deny */
5807             auth = CUPSD_AUTH_DENY;
5808
5809             if (cupsdCheckAuth(address, srcname, len, BrowseACL->allow))
5810               auth = CUPSD_AUTH_ALLOW;
5811
5812             if (cupsdCheckAuth(address, srcname, len, BrowseACL->deny))
5813               auth = CUPSD_AUTH_DENY;
5814             break;
5815       }
5816     }
5817   }
5818   else
5819     auth = CUPSD_AUTH_ALLOW;
5820
5821   if (auth == CUPSD_AUTH_DENY)
5822   {
5823     cupsdLogMessage(CUPSD_LOG_DEBUG,
5824                     "update_cups_browse: Refused %d bytes from %s", bytes,
5825                     srcname);
5826     return;
5827   }
5828
5829   cupsdLogMessage(CUPSD_LOG_DEBUG2,
5830                   "update_cups_browse: (%d bytes from %s) %s", bytes,
5831                   srcname, packet);
5832
5833  /*
5834   * Parse packet...
5835   */
5836
5837   if (sscanf(packet, "%x%x%1023s", &type, &state, uri) < 3)
5838   {
5839     cupsdLogMessage(CUPSD_LOG_WARN,
5840                     "update_cups_browse: Garbled browse packet - %s", packet);
5841     return;
5842   }
5843
5844   strcpy(location, "Location Unknown");
5845   strcpy(info, "No Information Available");
5846   make_model[0] = '\0';
5847   num_attrs     = 0;
5848   attrs         = NULL;
5849
5850   if ((pptr = strchr(packet, '\"')) != NULL)
5851   {
5852    /*
5853     * Have extended information; can't use sscanf for it because not all
5854     * sscanf's allow empty strings with %[^\"]...
5855     */
5856
5857     for (i = 0, pptr ++;
5858          i < (sizeof(location) - 1) && *pptr && *pptr != '\"';
5859          i ++, pptr ++)
5860       location[i] = *pptr;
5861
5862     if (i)
5863       location[i] = '\0';
5864
5865     if (*pptr == '\"')
5866       pptr ++;
5867
5868     while (*pptr && isspace(*pptr & 255))
5869       pptr ++;
5870
5871     if (*pptr == '\"')
5872     {
5873       for (i = 0, pptr ++;
5874            i < (sizeof(info) - 1) && *pptr && *pptr != '\"';
5875            i ++, pptr ++)
5876         info[i] = *pptr;
5877
5878       info[i] = '\0';
5879
5880       if (*pptr == '\"')
5881         pptr ++;
5882
5883       while (*pptr && isspace(*pptr & 255))
5884         pptr ++;
5885
5886       if (*pptr == '\"')
5887       {
5888         for (i = 0, pptr ++;
5889              i < (sizeof(make_model) - 1) && *pptr && *pptr != '\"';
5890              i ++, pptr ++)
5891           make_model[i] = *pptr;
5892
5893         if (*pptr == '\"')
5894           pptr ++;
5895
5896         make_model[i] = '\0';
5897
5898         if (*pptr)
5899           num_attrs = cupsParseOptions(pptr, num_attrs, &attrs);
5900       }
5901     }
5902   }
5903
5904   DEBUG_puts(packet);
5905   DEBUG_printf(("type=%x, state=%x, uri=\"%s\"\n"
5906                 "location=\"%s\", info=\"%s\", make_model=\"%s\"\n",
5907                 type, state, uri, location, info, make_model));
5908
5909  /*
5910   * Pull the URI apart to see if this is a local or remote printer...
5911   */
5912
5913   if (is_local_queue(uri, host, sizeof(host), resource, sizeof(resource)))
5914   {
5915     cupsFreeOptions(num_attrs, attrs);
5916     return;
5917   }
5918
5919  /*
5920   * Do relaying...
5921   */
5922
5923   for (i = 0; i < NumRelays; i ++)
5924     if (cupsdCheckAuth(address, srcname, len, Relays[i].from))
5925       if (sendto(BrowseSocket, packet, bytes, 0,
5926                  (struct sockaddr *)&(Relays[i].to),
5927                  httpAddrLength(&(Relays[i].to))) <= 0)
5928       {
5929         cupsdLogMessage(CUPSD_LOG_ERROR,
5930                         "update_cups_browse: sendto failed for relay %d - %s.",
5931                         i + 1, strerror(errno));
5932         cupsFreeOptions(num_attrs, attrs);
5933         return;
5934       }
5935
5936  /*
5937   * Process the browse data...
5938   */
5939
5940   process_browse_data(uri, host, resource, (cups_ptype_t)type,
5941                       (ipp_pstate_t)state, location, info, make_model,
5942                       num_attrs, attrs);
5943 }
5944
5945
5946 /*
5947  * 'update_lpd()' - Update the LPD configuration as needed.
5948  */
5949
5950 static void
5951 update_lpd(int onoff)                   /* - 1 = turn on, 0 = turn off */
5952 {
5953   if (!LPDConfigFile)
5954     return;
5955
5956 #ifdef __APPLE__
5957  /*
5958   * Allow /etc/hostconfig CUPS_LPD service setting to override cupsd.conf
5959   * setting for backwards-compatibility.
5960   */
5961
5962   if (onoff && !get_hostconfig("CUPS_LPD"))
5963     onoff = 0;
5964 #endif /* __APPLE__ */
5965
5966   if (!strncmp(LPDConfigFile, "xinetd:///", 10))
5967   {
5968    /*
5969     * Enable/disable LPD via the xinetd.d config file for cups-lpd...
5970     */
5971
5972     char        newfile[1024];          /* New cups-lpd.N file */
5973     cups_file_t *ofp,                   /* Original file pointer */
5974                 *nfp;                   /* New file pointer */
5975     char        line[1024];             /* Line from file */
5976
5977
5978     snprintf(newfile, sizeof(newfile), "%s.N", LPDConfigFile + 9);
5979
5980     if ((ofp = cupsFileOpen(LPDConfigFile + 9, "r")) == NULL)
5981     {
5982       cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open \"%s\" - %s",
5983                       LPDConfigFile + 9, strerror(errno));
5984       return;
5985     }
5986
5987     if ((nfp = cupsFileOpen(newfile, "w")) == NULL)
5988     {
5989       cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create \"%s\" - %s",
5990                       newfile, strerror(errno));
5991       cupsFileClose(ofp);
5992       return;
5993     }
5994
5995    /*
5996     * Copy all of the lines from the cups-lpd file...
5997     */
5998
5999     while (cupsFileGets(ofp, line, sizeof(line)))
6000     {
6001       if (line[0] == '{')
6002       {
6003         cupsFilePrintf(nfp, "%s\n", line);
6004         snprintf(line, sizeof(line), "\tdisable = %s",
6005                  onoff ? "no" : "yes");
6006       }
6007       else if (!strstr(line, "disable ="))
6008         cupsFilePrintf(nfp, "%s\n", line);
6009     }
6010
6011     cupsFileClose(nfp);
6012     cupsFileClose(ofp);
6013     rename(newfile, LPDConfigFile + 9);
6014   }
6015 #ifdef __APPLE__
6016   else if (!strncmp(LPDConfigFile, "launchd:///", 11))
6017   {
6018    /*
6019     * Enable/disable LPD via the launchctl command...
6020     */
6021
6022     char        *argv[5],               /* Arguments for command */
6023                 *envp[MAX_ENV];         /* Environment for command */
6024     int         pid;                    /* Process ID */
6025
6026
6027     cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
6028     argv[0] = (char *)"launchctl";
6029     argv[1] = (char *)(onoff ? "load" : "unload");
6030     argv[2] = (char *)"-w";
6031     argv[3] = LPDConfigFile + 10;
6032     argv[4] = NULL;
6033
6034     cupsdStartProcess("/bin/launchctl", argv, envp, -1, -1, -1, -1, -1, 1,
6035                       NULL, NULL, &pid);
6036   }
6037 #endif /* __APPLE__ */
6038   else
6039     cupsdLogMessage(CUPSD_LOG_INFO, "Unknown LPDConfigFile scheme!");
6040 }
6041
6042
6043 /*
6044  * 'update_polling()' - Read status messages from the poll daemons.
6045  */
6046
6047 static void
6048 update_polling(void)
6049 {
6050   char          *ptr,                   /* Pointer to end of line in buffer */
6051                 message[1024];          /* Pointer to message text */
6052   int           loglevel;               /* Log level for message */
6053
6054
6055   while ((ptr = cupsdStatBufUpdate(PollStatusBuffer, &loglevel,
6056                                    message, sizeof(message))) != NULL)
6057   {
6058     if (loglevel == CUPSD_LOG_INFO)
6059       cupsdLogMessage(CUPSD_LOG_INFO, "%s", message);
6060
6061     if (!strchr(PollStatusBuffer->buffer, '\n'))
6062       break;
6063   }
6064
6065   if (ptr == NULL && !PollStatusBuffer->bufused)
6066   {
6067    /*
6068     * All polling processes have died; stop polling...
6069     */
6070
6071     cupsdLogMessage(CUPSD_LOG_ERROR,
6072                     "update_polling: all polling processes have exited!");
6073     cupsdStopPolling();
6074   }
6075 }
6076
6077
6078 /*
6079  * 'update_smb()' - Update the SMB configuration as needed.
6080  */
6081
6082 static void
6083 update_smb(int onoff)                   /* I - 1 = turn on, 0 = turn off */
6084 {
6085   if (!SMBConfigFile)
6086     return;
6087
6088   if (!strncmp(SMBConfigFile, "samba:///", 9))
6089   {
6090    /*
6091     * Enable/disable SMB via the specified smb.conf config file...
6092     */
6093
6094     char        newfile[1024];          /* New smb.conf.N file */
6095     cups_file_t *ofp,                   /* Original file pointer */
6096                 *nfp;                   /* New file pointer */
6097     char        line[1024];             /* Line from file */
6098     int         in_printers;            /* In [printers] section? */
6099
6100
6101     snprintf(newfile, sizeof(newfile), "%s.N", SMBConfigFile + 8);
6102
6103     if ((ofp = cupsFileOpen(SMBConfigFile + 8, "r")) == NULL)
6104     {
6105       cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open \"%s\" - %s",
6106                       SMBConfigFile + 8, strerror(errno));
6107       return;
6108     }
6109
6110     if ((nfp = cupsFileOpen(newfile, "w")) == NULL)
6111     {
6112       cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create \"%s\" - %s",
6113                       newfile, strerror(errno));
6114       cupsFileClose(ofp);
6115       return;
6116     }
6117
6118    /*
6119     * Copy all of the lines from the smb.conf file...
6120     */
6121
6122     in_printers = 0;
6123
6124     while (cupsFileGets(ofp, line, sizeof(line)))
6125     {
6126       if (in_printers && strstr(line, "printable ="))
6127         snprintf(line, sizeof(line), "    printable = %s",
6128                  onoff ? "yes" : "no");
6129
6130       cupsFilePrintf(nfp, "%s\n", line);
6131
6132       if (line[0] == '[')
6133         in_printers = !strcmp(line, "[printers]");
6134     }
6135
6136     cupsFileClose(nfp);
6137     cupsFileClose(ofp);
6138     rename(newfile, SMBConfigFile + 8);
6139   }
6140   else
6141     cupsdLogMessage(CUPSD_LOG_INFO, "Unknown SMBConfigFile scheme!");
6142 }
6143
6144
6145 /*
6146  * End of "$Id: dirsvc.c 10243 2012-02-11 02:05:21Z mike $".
6147  */