Load cups into easysw/current.
[thirdparty/cups.git] / scheduler / dirsvc.c
1 /*
2  * "$Id: dirsvc.c 4906 2006-01-10 20:53:28Z mike $"
3  *
4  *   Directory services routines for the Common UNIX Printing System (CUPS).
5  *
6  *   Copyright 1997-2005 by Easy Software Products, all rights reserved.
7  *
8  *   These coded instructions, statements, and computer programs are the
9  *   property of Easy Software Products and are protected by Federal
10  *   copyright law.  Distribution and use rights are outlined in the file
11  *   "LICENSE.txt" which should have been included with this file.  If this
12  *   file is missing or damaged please contact Easy Software Products
13  *   at:
14  *
15  *       Attn: CUPS Licensing Information
16  *       Easy Software Products
17  *       44141 Airport View Drive, Suite 204
18  *       Hollywood, Maryland 20636 USA
19  *
20  *       Voice: (301) 373-9600
21  *       EMail: cups-info@cups.org
22  *         WWW: http://www.cups.org
23  *
24  * Contents:
25  *
26  *   cupsdLoadRemoteCache()        - Load the remote printer cache.
27  *   cupsdProcessBrowseData()      - Process new browse data.
28  *   cupsdProcessImplicitClasses() - Create/update implicit classes as needed.
29  *   cupsdSaveRemoteCache()        - Save the remote printer cache.
30  *   cupsdSendBrowseDelete()       - Send a "browse delete" message for a
31  *                                   printer.
32  *   cupsdSendBrowseList()         - Send new browsing information as necessary.
33  *   cupsdSendCUPSBrowse()         - Send new browsing information using the
34  *                                   CUPS protocol.
35  *   cupsdSendSLPBrowse()          - Register the specified printer with SLP.
36  *   cupsdStartBrowsing()          - Start sending and receiving broadcast
37  *                                   information.
38  *   cupsdStartPolling()           - Start polling servers as needed.
39  *   cupsdStopBrowsing()           - Stop sending and receiving broadcast
40  *                                   information.
41  *   cupsdStopPolling()            - Stop polling servers as needed.
42  *   cupsdUpdateCUPSBrowse()       - Update the browse lists using the CUPS
43  *                                   protocol.
44  *   cupsdUpdatePolling()          - Read status messages from the poll daemons.
45  *   cupsdUpdateSLPBrowse()        - Get browsing information via SLP.
46  *   slp_attr_callback()           - SLP attribute callback 
47  *   slp_dereg_printer()           - SLPDereg() the specified printer
48  *   slp_get_attr()                - Get an attribute from an SLP registration.
49  *   slp_reg_callback()            - Empty SLPRegReport.
50  *   slp_url_callback()            - SLP service url callback
51  */
52
53 /*
54  * Include necessary headers...
55  */
56
57 #include "cupsd.h"
58 #include <grp.h>
59
60
61 /*
62  * SLP definitions...
63  */
64
65 #ifdef HAVE_LIBSLP 
66 /*
67  * SLP service name for CUPS...
68  */
69
70 #  define SLP_CUPS_SRVTYPE      "service:printer"
71 #  define SLP_CUPS_SRVLEN       15
72
73
74 /* 
75  * Printer service URL structure
76  */
77
78 typedef struct _slpsrvurl_s             /**** SLP URL list ****/
79 {
80   struct _slpsrvurl_s   *next;          /* Next URL in list */
81   char                  url[HTTP_MAX_URI];
82                                         /* URL */
83 } slpsrvurl_t;
84
85
86 /*
87  * Local functions...
88  */
89
90 static SLPBoolean       slp_attr_callback(SLPHandle hslp, const char *attrlist,
91                                           SLPError errcode, void *cookie);
92 static void             slp_dereg_printer(cupsd_printer_t *p);
93 static int              slp_get_attr(const char *attrlist, const char *tag,
94                                      char **valbuf);
95 static void             slp_reg_callback(SLPHandle hslp, SLPError errcode,
96                                          void *cookie);
97 static SLPBoolean       slp_url_callback(SLPHandle hslp, const char *srvurl,
98                                          unsigned short lifetime,
99                                          SLPError errcode, void *cookie);
100 #endif /* HAVE_LIBSLP */
101
102
103 /*
104  * 'cupsdLoadRemoteCache()' - Load the remote printer cache.
105  */
106
107 void
108 cupsdLoadRemoteCache(void)
109 {
110   cups_file_t           *fp;            /* remote.cache file */
111   int                   linenum;        /* Current line number */
112   char                  line[1024],     /* Line from file */
113                         *value,         /* Pointer to value */
114                         *valueptr;      /* Pointer into value */
115   cupsd_printer_t       *p;             /* Current printer */
116   time_t                now;            /* Current time */
117
118
119  /*
120   * Open the remote.cache file...
121   */
122
123   snprintf(line, sizeof(line), "%s/remote.cache", CacheDir);
124   if ((fp = cupsFileOpen(line, "r")) == NULL)
125     return;
126
127  /*
128   * Read printer configurations until we hit EOF...
129   */
130
131   linenum = 0;
132   p       = NULL;
133   now     = time(NULL);
134
135   while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
136   {
137    /*
138     * Decode the directive...
139     */
140
141     if (!strcasecmp(line, "<Printer") ||
142         !strcasecmp(line, "<DefaultPrinter"))
143     {
144      /*
145       * <Printer name> or <DefaultPrinter name>
146       */
147
148       if (p == NULL && value)
149       {
150        /*
151         * Add the printer and a base file type...
152         */
153
154         cupsdLogMessage(CUPSD_LOG_DEBUG,
155                         "cupsdLoadRemoteCache: Loading printer %s...", value);
156
157         p = cupsdAddPrinter(value);
158         p->accepting   = 1;
159         p->state       = IPP_PRINTER_IDLE;
160         p->type        |= CUPS_PRINTER_REMOTE;
161         p->browse_time = now + BrowseTimeout;
162
163        /*
164         * Set the default printer as needed...
165         */
166
167         if (!strcasecmp(line, "<DefaultPrinter"))
168           DefaultPrinter = p;
169       }
170       else
171       {
172         cupsdLogMessage(CUPSD_LOG_ERROR,
173                         "Syntax error on line %d of remote.cache.", linenum);
174         return;
175       }
176     }
177     else if (!strcasecmp(line, "<Class") ||
178              !strcasecmp(line, "<DefaultClass"))
179     {
180      /*
181       * <Class name> or <DefaultClass name>
182       */
183
184       if (p == NULL && value)
185       {
186        /*
187         * Add the printer and a base file type...
188         */
189
190         cupsdLogMessage(CUPSD_LOG_DEBUG,
191                         "cupsdLoadRemoteCache: Loading class %s...", value);
192
193         p = cupsdAddClass(value);
194         p->accepting   = 1;
195         p->state       = IPP_PRINTER_IDLE;
196         p->type        |= CUPS_PRINTER_REMOTE;
197         p->browse_time = now + BrowseTimeout;
198
199        /*
200         * Set the default printer as needed...
201         */
202
203         if (!strcasecmp(line, "<DefaultClass"))
204           DefaultPrinter = p;
205       }
206       else
207       {
208         cupsdLogMessage(CUPSD_LOG_ERROR,
209                         "Syntax error on line %d of remote.cache.", linenum);
210         return;
211       }
212     }
213     else if (!strcasecmp(line, "</Printer>") ||
214              !strcasecmp(line, "</Class>"))
215     {
216       if (p != NULL)
217       {
218        /*
219         * Close out the current printer...
220         */
221
222         cupsdSetPrinterAttrs(p);
223
224         p = NULL;
225       }
226       else
227       {
228         cupsdLogMessage(CUPSD_LOG_ERROR,
229                         "Syntax error on line %d of remote.cache.", linenum);
230         return;
231       }
232     }
233     else if (!p)
234     {
235       cupsdLogMessage(CUPSD_LOG_ERROR,
236                       "Syntax error on line %d of remote.cache.", linenum);
237       return;
238     }
239     else if (!strcasecmp(line, "Info"))
240     {
241       if (value)
242         cupsdSetString(&p->info, value);
243     }
244     else if (!strcasecmp(line, "MakeModel"))
245     {
246       if (value)
247         cupsdSetString(&p->make_model, value);
248     }
249     else if (!strcasecmp(line, "Location"))
250     {
251       if (value)
252         cupsdSetString(&p->location, value);
253     }
254     else if (!strcasecmp(line, "DeviceURI"))
255     {
256       if (value)
257         cupsdSetString(&p->device_uri, value);
258       else
259       {
260         cupsdLogMessage(CUPSD_LOG_ERROR,
261                         "Syntax error on line %d of remote.cache.", linenum);
262         return;
263       }
264     }
265     else if (!strcasecmp(line, "State"))
266     {
267      /*
268       * Set the initial queue state...
269       */
270
271       if (value && !strcasecmp(value, "idle"))
272         p->state = IPP_PRINTER_IDLE;
273       else if (value && !strcasecmp(value, "stopped"))
274         p->state = IPP_PRINTER_STOPPED;
275       else
276       {
277         cupsdLogMessage(CUPSD_LOG_ERROR,
278                         "Syntax error on line %d of remote.cache.", linenum);
279         return;
280       }
281     }
282     else if (!strcasecmp(line, "StateMessage"))
283     {
284      /*
285       * Set the initial queue state message...
286       */
287
288       if (value)
289         strlcpy(p->state_message, value, sizeof(p->state_message));
290     }
291     else if (!strcasecmp(line, "Accepting"))
292     {
293      /*
294       * Set the initial accepting state...
295       */
296
297       if (value &&
298           (!strcasecmp(value, "yes") ||
299            !strcasecmp(value, "on") ||
300            !strcasecmp(value, "true")))
301         p->accepting = 1;
302       else if (value &&
303                (!strcasecmp(value, "no") ||
304                 !strcasecmp(value, "off") ||
305                 !strcasecmp(value, "false")))
306         p->accepting = 0;
307       else
308       {
309         cupsdLogMessage(CUPSD_LOG_ERROR,
310                         "Syntax error on line %d of remote.cache.", linenum);
311         return;
312       }
313     }
314     else if (!strcasecmp(line, "Type"))
315     {
316       if (value)
317         p->type = atoi(value);
318       else
319       {
320         cupsdLogMessage(CUPSD_LOG_ERROR,
321                         "Syntax error on line %d of remote.cache.", linenum);
322         return;
323       }
324     }
325     else if (!strcasecmp(line, "BrowseTime"))
326     {
327       if (value)
328       {
329         time_t t = atoi(value);
330
331         if (t > (now + BrowseInterval))
332           p->browse_time = t;
333       }
334       else
335       {
336         cupsdLogMessage(CUPSD_LOG_ERROR,
337                         "Syntax error on line %d of remote.cache.", linenum);
338         return;
339       }
340     }
341     else if (!strcasecmp(line, "JobSheets"))
342     {
343      /*
344       * Set the initial job sheets...
345       */
346
347       if (value)
348       {
349         for (valueptr = value; *valueptr && !isspace(*valueptr & 255); valueptr ++);
350
351         if (*valueptr)
352           *valueptr++ = '\0';
353
354         cupsdSetString(&p->job_sheets[0], value);
355
356         while (isspace(*valueptr & 255))
357           valueptr ++;
358
359         if (*valueptr)
360         {
361           for (value = valueptr; *valueptr && !isspace(*valueptr & 255); valueptr ++);
362
363           if (*valueptr)
364             *valueptr++ = '\0';
365
366           cupsdSetString(&p->job_sheets[1], value);
367         }
368       }
369       else
370       {
371         cupsdLogMessage(CUPSD_LOG_ERROR,
372                         "Syntax error on line %d of remote.cache.", linenum);
373         return;
374       }
375     }
376     else if (!strcasecmp(line, "AllowUser"))
377     {
378       if (value)
379       {
380         p->deny_users = 0;
381         cupsdAddPrinterUser(p, value);
382       }
383       else
384       {
385         cupsdLogMessage(CUPSD_LOG_ERROR,
386                         "Syntax error on line %d of remote.cache.", linenum);
387         return;
388       }
389     }
390     else if (!strcasecmp(line, "DenyUser"))
391     {
392       if (value)
393       {
394         p->deny_users = 1;
395         cupsdAddPrinterUser(p, value);
396       }
397       else
398       {
399         cupsdLogMessage(CUPSD_LOG_ERROR,
400                         "Syntax error on line %d of remote.cache.", linenum);
401         return;
402       }
403     }
404     else
405     {
406      /*
407       * Something else we don't understand...
408       */
409
410       cupsdLogMessage(CUPSD_LOG_ERROR,
411                       "Unknown configuration directive %s on line %d of remote.cache.",
412                       line, linenum);
413     }
414   }
415
416   cupsFileClose(fp);
417
418  /*
419   * Do auto-classing if needed...
420   */
421
422   cupsdProcessImplicitClasses();
423 }
424
425
426 /*
427  * 'cupsdProcessBrowseData()' - Process new browse data.
428  */
429
430 void
431 cupsdProcessBrowseData(
432     const char    *uri,                 /* I - URI of printer/class */
433     cups_ptype_t  type,                 /* I - Printer type */
434     ipp_pstate_t  state,                /* I - Printer state */
435     const char    *location,            /* I - Printer location */
436     const char    *info,                /* I - Printer information */
437     const char    *make_model,          /* I - Printer make and model */
438     int           num_attrs,            /* I - Number of attributes */
439     cups_option_t *attrs)               /* I - Attributes */
440 {
441   int           update;                 /* Update printer attributes? */
442   char          finaluri[HTTP_MAX_URI], /* Final URI for printer */
443                 method[HTTP_MAX_URI],   /* Method portion of URI */
444                 username[HTTP_MAX_URI], /* Username portion of URI */
445                 host[HTTP_MAX_URI],     /* Host portion of URI */
446                 resource[HTTP_MAX_URI]; /* Resource portion of URI */
447   int           port;                   /* Port portion of URI */
448   char          name[IPP_MAX_NAME],     /* Name of printer */
449                 *hptr,                  /* Pointer into hostname */
450                 *sptr;                  /* Pointer into ServerName */
451   char          local_make_model[IPP_MAX_NAME];
452                                         /* Local make and model */
453   cupsd_printer_t *p;                   /* Printer information */
454   const char    *ipp_options;           /* ipp-options value */
455
456
457  /*
458   * Pull the URI apart to see if this is a local or remote printer...
459   */
460
461   httpSeparateURI(uri, method, sizeof(method), username, sizeof(username),
462                   host, sizeof(host), &port, resource, sizeof(resource));
463
464  /*
465   * Determine if the URI contains any illegal characters in it...
466   */
467
468   if (strncmp(uri, "ipp://", 6) || !host[0] ||
469       (strncmp(resource, "/printers/", 10) &&
470        strncmp(resource, "/classes/", 9)))
471   {
472     cupsdLogMessage(CUPSD_LOG_ERROR,
473                     "cupsdProcessBrowseData: Bad printer URI in browse data: %s",
474                     uri);
475     return;
476   }
477
478   if (strchr(resource, '?') ||
479       (!strncmp(resource, "/printers/", 10) && strchr(resource + 10, '/')) ||
480       (!strncmp(resource, "/classes/", 9) && strchr(resource + 9, '/')))
481   {
482     cupsdLogMessage(CUPSD_LOG_ERROR,
483                     "cupsdProcessBrowseData: Bad resource in browse data: %s",
484                     resource);
485     return;
486   }
487
488  /*
489   * OK, this isn't a local printer; add any remote options...
490   */
491
492   ipp_options = cupsGetOption("ipp-options", num_attrs, attrs);
493
494   if (BrowseRemoteOptions)
495   {
496     if (BrowseRemoteOptions[0] == '?')
497     {
498      /*
499       * Override server-supplied options...
500       */
501
502       snprintf(finaluri, sizeof(finaluri), "%s%s", uri, BrowseRemoteOptions);
503     }
504     else if (ipp_options)
505     {
506      /*
507       * Combine the server and local options...
508       */
509
510       snprintf(finaluri, sizeof(finaluri), "%s?%s+%s", uri, ipp_options,
511                BrowseRemoteOptions);
512     }
513     else
514     {
515      /*
516       * Just use the local options...
517       */
518
519       snprintf(finaluri, sizeof(finaluri), "%s?%s", uri, BrowseRemoteOptions);
520     }
521
522     uri = finaluri;
523   }
524   else if (ipp_options)
525   {
526    /*
527     * Just use the server-supplied options...
528     */
529
530     snprintf(finaluri, sizeof(finaluri), "%s?%s", uri, ipp_options);
531     uri = finaluri;
532   }
533
534  /*
535   * See if we already have it listed in the Printers list, and add it if not...
536   */
537
538   type   |= CUPS_PRINTER_REMOTE;
539   type   &= ~CUPS_PRINTER_IMPLICIT;
540   update = 0;
541   hptr   = strchr(host, '.');
542   sptr   = strchr(ServerName, '.');
543
544   if (sptr != NULL && hptr != NULL)
545   {
546    /*
547     * Strip the common domain name components...
548     */
549
550     while (hptr != NULL)
551     {
552       if (!strcasecmp(hptr, sptr))
553       {
554         *hptr = '\0';
555         break;
556       }
557       else
558         hptr = strchr(hptr + 1, '.');
559     }
560   }
561
562   if (type & CUPS_PRINTER_CLASS)
563   {
564    /*
565     * Remote destination is a class...
566     */
567
568     if (!strncmp(resource, "/classes/", 9))
569       snprintf(name, sizeof(name), "%s@%s", resource + 9, host);
570     else
571       return;
572
573     if ((p = cupsdFindClass(name)) == NULL && BrowseShortNames)
574     {
575       if ((p = cupsdFindClass(resource + 9)) != NULL)
576       {
577         if (p->hostname && strcasecmp(p->hostname, host))
578         {
579          /*
580           * Nope, this isn't the same host; if the hostname isn't the local host,
581           * add it to the other class and then find a class using the full host
582           * name...
583           */
584
585           if (p->type & CUPS_PRINTER_REMOTE)
586           {
587             cupsdLogMessage(CUPSD_LOG_INFO,
588                             "Renamed remote class \"%s\" to \"%s@%s\"...",
589                             p->name, p->name, p->hostname);
590             cupsdAddEvent(CUPSD_EVENT_PRINTER_DELETED, p, NULL,
591                           "Class \'%s\' deleted by directory services.",
592                           p->name);
593
594             cupsArrayRemove(Printers, p);
595             cupsdSetStringf(&p->name, "%s@%s", p->name, p->hostname);
596             cupsdSetPrinterAttrs(p);
597             cupsArrayAdd(Printers, p);
598
599             cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED, p, NULL,
600                           "Class \'%s\' added by directory services.",
601                           p->name);
602           }
603
604           p = NULL;
605         }
606         else if (!p->hostname)
607         {
608          /*
609           * Hostname not set, so this must be a cached remote printer
610           * that was created for a pending print job...
611           */
612
613           cupsdSetString(&p->hostname, host);
614           cupsdSetString(&p->uri, uri);
615           cupsdSetString(&p->device_uri, uri);
616           update = 1;
617         }
618       }
619       else
620       {
621        /*
622         * Use the short name for this shared class.
623         */
624
625         strlcpy(name, resource + 9, sizeof(name));
626       }
627     }
628     else if (p && !p->hostname)
629     {
630      /*
631       * Hostname not set, so this must be a cached remote printer
632       * that was created for a pending print job...
633       */
634
635       cupsdSetString(&p->hostname, host);
636       cupsdSetString(&p->uri, uri);
637       cupsdSetString(&p->device_uri, uri);
638       update = 1;
639     }
640
641     if (!p)
642     {
643      /*
644       * Class doesn't exist; add it...
645       */
646
647       p = cupsdAddClass(name);
648
649       cupsdLogMessage(CUPSD_LOG_INFO, "Added remote class \"%s\"...", name);
650
651       cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED, p, NULL,
652                     "Class \'%s\' added by directory services.", name);
653
654      /*
655       * Force the URI to point to the real server...
656       */
657
658       p->type      = type & ~CUPS_PRINTER_REJECTING;
659       p->accepting = 1;
660       cupsdSetString(&p->uri, uri);
661       cupsdSetString(&p->device_uri, uri);
662       cupsdSetString(&p->hostname, host);
663
664       update = 1;
665     }
666   }
667   else
668   {
669    /*
670     * Remote destination is a printer...
671     */
672
673     if (strncmp(resource, "/printers/", 10) == 0)
674       snprintf(name, sizeof(name), "%s@%s", resource + 10, host);
675     else
676       return;
677
678     if ((p = cupsdFindPrinter(name)) == NULL && BrowseShortNames)
679     {
680       if ((p = cupsdFindPrinter(resource + 10)) != NULL)
681       {
682         if (p->hostname && strcasecmp(p->hostname, host))
683         {
684          /*
685           * Nope, this isn't the same host; if the hostname isn't the local host,
686           * add it to the other printer and then find a printer using the full host
687           * name...
688           */
689
690           if (p->type & CUPS_PRINTER_REMOTE)
691           {
692             cupsdLogMessage(CUPSD_LOG_INFO,
693                             "Renamed remote printer \"%s\" to \"%s@%s\"...",
694                             p->name, p->name, p->hostname);
695             cupsdAddEvent(CUPSD_EVENT_PRINTER_DELETED, p, NULL,
696                           "Printer \'%s\' deleted by directory services.",
697                           p->name);
698
699             cupsArrayRemove(Printers, p);
700             cupsdSetStringf(&p->name, "%s@%s", p->name, p->hostname);
701             cupsdSetPrinterAttrs(p);
702             cupsArrayAdd(Printers, p);
703
704             cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED, p, NULL,
705                           "Printer \'%s\' added by directory services.",
706                           p->name);
707           }
708
709           p = NULL;
710         }
711         else if (!p->hostname)
712         {
713          /*
714           * Hostname not set, so this must be a cached remote printer
715           * that was created for a pending print job...
716           */
717
718           cupsdSetString(&p->hostname, host);
719           cupsdSetString(&p->uri, uri);
720           cupsdSetString(&p->device_uri, uri);
721           update = 1;
722         }
723       }
724       else
725       {
726        /*
727         * Use the short name for this shared printer.
728         */
729
730         strlcpy(name, resource + 10, sizeof(name));
731       }
732     }
733     else if (p && !p->hostname)
734     {
735      /*
736       * Hostname not set, so this must be a cached remote printer
737       * that was created for a pending print job...
738       */
739
740       cupsdSetString(&p->hostname, host);
741       cupsdSetString(&p->uri, uri);
742       cupsdSetString(&p->device_uri, uri);
743       update = 1;
744     }
745
746     if (!p)
747     {
748      /*
749       * Printer doesn't exist; add it...
750       */
751
752       p = cupsdAddPrinter(name);
753
754       cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED, p, NULL,
755                     "Printer \'%s\' added by directory services.", name);
756
757       cupsdLogMessage(CUPSD_LOG_INFO, "Added remote printer \"%s\"...", name);
758
759      /*
760       * Force the URI to point to the real server...
761       */
762
763       p->type      = type & ~CUPS_PRINTER_REJECTING;
764       p->accepting = 1;
765       cupsdSetString(&p->hostname, host);
766       cupsdSetString(&p->uri, uri);
767       cupsdSetString(&p->device_uri, uri);
768
769       update = 1;
770     }
771   }
772
773  /*
774   * Update the state...
775   */
776
777   p->state       = state;
778   p->browse_time = time(NULL);
779
780   if (type & CUPS_PRINTER_REJECTING)
781   {
782     type &= ~CUPS_PRINTER_REJECTING;
783
784     if (p->accepting)
785     {
786       update       = 1;
787       p->accepting = 0;
788     }
789   }
790   else if (!p->accepting)
791   {
792     update       = 1;
793     p->accepting = 1;
794   }
795
796   if (p->type != type)
797   {
798     p->type = type;
799     update  = 1;
800   }
801
802   if (location && (!p->location || strcmp(p->location, location)))
803   {
804     cupsdSetString(&p->location, location);
805     update = 1;
806   }
807
808   if (info && (!p->info || strcmp(p->info, info)))
809   {
810     cupsdSetString(&p->info, info);
811     update = 1;
812   }
813
814   if (!make_model || !make_model[0])
815   {
816     if (type & CUPS_PRINTER_CLASS)
817       snprintf(local_make_model, sizeof(local_make_model),
818                "Remote Class on %s", host);
819     else
820       snprintf(local_make_model, sizeof(local_make_model),
821                "Remote Printer on %s", host);
822   }
823   else
824     snprintf(local_make_model, sizeof(local_make_model),
825              "%s on %s", make_model, host);
826
827   if (!p->make_model || strcmp(p->make_model, local_make_model))
828   {
829     cupsdSetString(&p->make_model, local_make_model);
830     update = 1;
831   }
832
833   if (type & CUPS_PRINTER_DELETE)
834   {
835     cupsdAddEvent(CUPSD_EVENT_PRINTER_DELETED, p, NULL,
836                   "%s \'%s\' deleted by directory services.",
837                   (type & CUPS_PRINTER_CLASS) ? "Class" : "Printer", p->name);
838
839     cupsdExpireSubscriptions(p, NULL);
840  
841     cupsdDeletePrinter(p, 1);
842     cupsdUpdateImplicitClasses();
843   }
844   else if (update)
845   {
846     cupsdSetPrinterAttrs(p);
847     cupsdUpdateImplicitClasses();
848   }
849
850  /*
851   * See if we have a default printer...  If not, make the first printer the
852   * default.
853   */
854
855   if (DefaultPrinter == NULL && Printers != NULL)
856     DefaultPrinter = (cupsd_printer_t *)cupsArrayFirst(Printers);
857
858  /*
859   * Do auto-classing if needed...
860   */
861
862   cupsdProcessImplicitClasses();
863
864  /*
865   * Update the printcap file...
866   */
867
868   cupsdWritePrintcap();
869 }
870
871
872 /*
873  * 'cupsdProcessImplicitClasses()' - Create/update implicit classes as needed.
874  */
875
876 void
877 cupsdProcessImplicitClasses(void)
878 {
879   int           i;                      /* Looping var */
880   int           update;                 /* Update printer attributes? */
881   char          name[IPP_MAX_NAME],     /* Name of printer */
882                 *hptr;                  /* Pointer into hostname */
883   cupsd_printer_t *p,                   /* Printer information */
884                 *pclass,                /* Printer class */
885                 *first;                 /* First printer in class */
886   int           offset,                 /* Offset of name */
887                 len;                    /* Length of name */
888
889
890   if (!ImplicitClasses || !Printers)
891     return;
892
893  /*
894   * Loop through all available printers and create classes as needed...
895   */
896
897   for (p = (cupsd_printer_t *)cupsArrayFirst(Printers), len = 0, offset = 0,
898            update = 0, pclass = NULL, first = NULL;
899        p != NULL;
900        p = (cupsd_printer_t *)cupsArrayNext(Printers))
901   {
902    /*
903     * Skip implicit classes...
904     */
905
906     if (p->type & CUPS_PRINTER_IMPLICIT)
907     {
908       len = 0;
909       continue;
910     }
911
912    /*
913     * If len == 0, get the length of this printer name up to the "@"
914     * sign (if any).
915     */
916
917     cupsArraySave(Printers);
918
919     if (len > 0 &&
920         strncasecmp(p->name, name + offset, len) == 0 &&
921         (p->name[len] == '\0' || p->name[len] == '@'))
922     {
923      /*
924       * We have more than one printer with the same name; see if
925       * we have a class, and if this printer is a member...
926       */
927
928       if (pclass && strcasecmp(pclass->name, name))
929       {
930         if (update)
931           cupsdSetPrinterAttrs(pclass);
932
933         update = 0;
934         pclass = NULL;
935       }
936
937       if (!pclass && (pclass = cupsdFindDest(name)) == NULL)
938       {
939        /*
940         * Need to add the class...
941         */
942
943         pclass = cupsdAddPrinter(name);
944         cupsArrayAdd(ImplicitPrinters, pclass);
945
946         pclass->type      |= CUPS_PRINTER_IMPLICIT;
947         pclass->accepting = 1;
948         pclass->state     = IPP_PRINTER_IDLE;
949
950         cupsdSetString(&pclass->location, p->location);
951         cupsdSetString(&pclass->info, p->info);
952
953         update = 1;
954
955         cupsdLogMessage(CUPSD_LOG_INFO, "Added implicit class \"%s\"...",
956                         name);
957       }
958
959       if (first != NULL)
960       {
961         for (i = 0; i < pclass->num_printers; i ++)
962           if (pclass->printers[i] == first)
963             break;
964
965         if (i >= pclass->num_printers)
966         {
967           first->in_implicit_class = 1;
968           cupsdAddPrinterToClass(pclass, first);
969         }
970
971         first = NULL;
972       }
973
974       for (i = 0; i < pclass->num_printers; i ++)
975         if (pclass->printers[i] == p)
976           break;
977
978       if (i >= pclass->num_printers)
979       {
980         p->in_implicit_class = 1;
981         cupsdAddPrinterToClass(pclass, p);
982         update = 1;
983       }
984     }
985     else
986     {
987      /*
988       * First time around; just get name length and mark it as first
989       * in the list...
990       */
991
992       if ((hptr = strchr(p->name, '@')) != NULL)
993         len = hptr - p->name;
994       else
995         len = strlen(p->name);
996
997       strncpy(name, p->name, len);
998       name[len] = '\0';
999       offset    = 0;
1000
1001       if ((first = (hptr ? cupsdFindDest(name) : p)) != NULL &&
1002           !(first->type & CUPS_PRINTER_IMPLICIT))
1003       {
1004        /*
1005         * Can't use same name as a local printer; add "Any" to the
1006         * front of the name, unless we have explicitly disabled
1007         * the "ImplicitAnyClasses"...
1008         */
1009
1010         if (ImplicitAnyClasses && len < (sizeof(name) - 4))
1011         {
1012          /*
1013           * Add "Any" to the class name...
1014           */
1015
1016           strcpy(name, "Any");
1017           strncpy(name + 3, p->name, len);
1018           name[len + 3] = '\0';
1019           offset        = 3;
1020         }
1021         else
1022         {
1023          /*
1024           * Don't create an implicit class if we have a local printer
1025           * with the same name...
1026           */
1027
1028           len = 0;
1029           cupsArrayRestore(Printers);
1030           continue;
1031         }
1032       }
1033
1034       first = p;
1035     }
1036
1037     cupsArrayRestore(Printers);
1038   }
1039
1040  /*
1041   * Update the last printer class as needed...
1042   */
1043
1044   if (pclass && update)
1045     cupsdSetPrinterAttrs(pclass);
1046 }
1047
1048
1049 /*
1050  * 'cupsdSaveRemoteCache()' - Save the remote printer cache.
1051  */
1052
1053 void
1054 cupsdSaveRemoteCache(void)
1055 {
1056   int                   i;              /* Looping var */
1057   cups_file_t           *fp;            /* printers.conf file */
1058   char                  temp[1024];     /* Temporary string */
1059   cupsd_printer_t       *printer;       /* Current printer class */
1060   time_t                curtime;        /* Current time */
1061   struct tm             *curdate;       /* Current date */
1062
1063
1064  /*
1065   * Create the remote.cache file...
1066   */
1067
1068   snprintf(temp, sizeof(temp), "%s/remote.cache", CacheDir);
1069
1070   if ((fp = cupsFileOpen(temp, "w")) == NULL)
1071   {
1072     cupsdLogMessage(CUPSD_LOG_ERROR,
1073                     "Unable to save remote.cache - %s", strerror(errno));
1074     return;
1075   }
1076   else
1077     cupsdLogMessage(CUPSD_LOG_INFO, "Saving remote.cache...");
1078
1079  /*
1080   * Restrict access to the file...
1081   */
1082
1083   fchown(cupsFileNumber(fp), getuid(), Group);
1084   fchmod(cupsFileNumber(fp), ConfigFilePerm);
1085
1086  /*
1087   * Write a small header to the file...
1088   */
1089
1090   curtime = time(NULL);
1091   curdate = localtime(&curtime);
1092   strftime(temp, sizeof(temp) - 1, "%Y-%m-%d %H:%M", curdate);
1093
1094   cupsFilePuts(fp, "# Remote cache file for " CUPS_SVERSION "\n");
1095   cupsFilePrintf(fp, "# Written by cupsd on %s\n", temp);
1096
1097  /*
1098   * Write each local printer known to the system...
1099   */
1100
1101   for (printer = (cupsd_printer_t *)cupsArrayFirst(Printers);
1102        printer;
1103        printer = (cupsd_printer_t *)cupsArrayNext(Printers))
1104   {
1105    /*
1106     * Skip local destinations...
1107     */
1108
1109     if (!(printer->type & CUPS_PRINTER_REMOTE))
1110       continue;
1111
1112    /*
1113     * Write printers as needed...
1114     */
1115
1116     if (printer == DefaultPrinter)
1117       cupsFilePuts(fp, "<Default");
1118     else
1119       cupsFilePutChar(fp, '<');
1120
1121     if (printer->type & CUPS_PRINTER_CLASS)
1122       cupsFilePrintf(fp, "Class %s>\n", printer->name);
1123     else
1124       cupsFilePrintf(fp, "Printer %s>\n", printer->name);
1125
1126     cupsFilePrintf(fp, "Type %d\n", printer->type);
1127
1128     cupsFilePrintf(fp, "BrowseTime %d\n", (int)printer->browse_time);
1129
1130     if (printer->info)
1131       cupsFilePrintf(fp, "Info %s\n", printer->info);
1132
1133     if (printer->make_model)
1134       cupsFilePrintf(fp, "MakeModel %s\n", printer->make_model);
1135
1136     if (printer->location)
1137       cupsFilePrintf(fp, "Location %s\n", printer->location);
1138
1139     if (printer->device_uri)
1140       cupsFilePrintf(fp, "DeviceURI %s\n", printer->device_uri);
1141
1142     if (printer->state == IPP_PRINTER_STOPPED)
1143     {
1144       cupsFilePuts(fp, "State Stopped\n");
1145       cupsFilePrintf(fp, "StateMessage %s\n", printer->state_message);
1146     }
1147     else
1148       cupsFilePuts(fp, "State Idle\n");
1149
1150     if (printer->accepting)
1151       cupsFilePuts(fp, "Accepting Yes\n");
1152     else
1153       cupsFilePuts(fp, "Accepting No\n");
1154
1155     cupsFilePrintf(fp, "JobSheets %s %s\n", printer->job_sheets[0],
1156             printer->job_sheets[1]);
1157
1158     for (i = 0; i < printer->num_users; i ++)
1159       cupsFilePrintf(fp, "%sUser %s\n", printer->deny_users ? "Deny" : "Allow",
1160               printer->users[i]);
1161
1162     if (printer->type & CUPS_PRINTER_CLASS)
1163       cupsFilePuts(fp, "</Class>\n");
1164     else
1165       cupsFilePuts(fp, "</Printer>\n");
1166   }
1167
1168   cupsFileClose(fp);
1169 }
1170
1171
1172 /*
1173  * 'cupsdSendBrowseDelete()' - Send a "browse delete" message for a printer.
1174  */
1175
1176 void
1177 cupsdSendBrowseDelete(
1178     cupsd_printer_t *p)                 /* I - Printer to delete */
1179 {
1180  /*
1181   * Only announce if browsing is enabled...
1182   */
1183
1184   if (!Browsing || !p->shared)
1185     return;
1186
1187  /*
1188   * First mark the printer for deletion...
1189   */
1190
1191   p->type |= CUPS_PRINTER_DELETE;
1192
1193  /*
1194   * Announce the deletion...
1195   */
1196
1197   if (BrowseLocalProtocols & BROWSE_CUPS)
1198     cupsdSendCUPSBrowse(p);
1199 #ifdef HAVE_LIBSLP
1200   if (BrowseLocalProtocols & BROWSE_SLP)
1201     slp_dereg_printer(p);
1202 #endif /* HAVE_LIBSLP */
1203 }
1204
1205
1206 /*
1207  * 'cupsdSendBrowseList()' - Send new browsing information as necessary.
1208  */
1209
1210 void
1211 cupsdSendBrowseList(void)
1212 {
1213   int                   count;          /* Number of dests to update */
1214   cupsd_printer_t       *p;             /* Current printer */
1215   time_t                ut,             /* Minimum update time */
1216                         to;             /* Timeout time */
1217
1218
1219   if (!Browsing || !BrowseLocalProtocols || !Printers)
1220     return;
1221
1222  /*
1223   * Compute the update and timeout times...
1224   */
1225
1226   ut = time(NULL) - BrowseInterval;
1227   to = time(NULL) - BrowseTimeout;
1228
1229  /*
1230   * Figure out how many printers need an update...
1231   */
1232
1233   if (BrowseInterval > 0)
1234   {
1235     int max_count;                      /* Maximum number to update */
1236
1237
1238    /*
1239     * Throttle the number of printers we'll be updating this time
1240     * around based on the number of queues that need updating and
1241     * the maximum number of queues to update each second...
1242     */
1243
1244     max_count = 2 * cupsArrayCount(Printers) / BrowseInterval + 1;
1245
1246     for (count = 0, p = (cupsd_printer_t *)cupsArrayFirst(Printers);
1247          count < max_count && p != NULL;
1248          p = (cupsd_printer_t *)cupsArrayNext(Printers))
1249       if (!(p->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)) &&
1250           p->shared && p->browse_time < ut)
1251         count ++;
1252
1253    /*
1254     * Loop through all of the printers and send local updates as needed...
1255     */
1256
1257     if (BrowseNext)
1258       p = (cupsd_printer_t *)cupsArrayFind(Printers, BrowseNext);
1259     else
1260       p = (cupsd_printer_t *)cupsArrayFirst(Printers);
1261
1262     for (;
1263          count > 0;
1264          p = (cupsd_printer_t *)cupsArrayNext(Printers))
1265     {
1266      /*
1267       * Check for wraparound...
1268       */
1269
1270       if (!p)
1271         p = (cupsd_printer_t *)cupsArrayFirst(Printers);
1272
1273       if (!p)
1274         break;
1275       else if ((p->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)) ||
1276                !p->shared)
1277         continue;
1278       else if (p->browse_time < ut)
1279       {
1280        /*
1281         * Need to send an update...
1282         */
1283
1284         count --;
1285
1286         p->browse_time = time(NULL);
1287
1288         if (BrowseLocalProtocols & BROWSE_CUPS)
1289           cupsdSendCUPSBrowse(p);
1290
1291 #ifdef HAVE_LIBSLP
1292         if (BrowseLocalProtocols & BROWSE_SLP)
1293           cupsdSendSLPBrowse(p);
1294 #endif /* HAVE_LIBSLP */
1295       }
1296     }
1297
1298    /*
1299     * Save where we left off so that all printers get updated...
1300     */
1301
1302     BrowseNext = p;
1303   }
1304
1305  /*
1306   * Loop through all of the printers and send local updates as needed...
1307   */
1308
1309   for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
1310        p;
1311        p = (cupsd_printer_t *)cupsArrayNext(Printers))
1312   {
1313    /*
1314     * If this is a remote queue, see if it needs to be timed out...
1315     */
1316
1317     if (p->type & CUPS_PRINTER_REMOTE)
1318     {
1319       if (p->browse_time < to)
1320       {
1321         cupsdAddEvent(CUPSD_EVENT_PRINTER_DELETED, p, NULL,
1322                       "%s \'%s\' deleted by directory services (timeout).",
1323                       (p->type & CUPS_PRINTER_CLASS) ? "Class" : "Printer",
1324                       p->name);
1325
1326         cupsdLogMessage(CUPSD_LOG_INFO,
1327                         "Remote destination \"%s\" has timed out; deleting it...",
1328                         p->name);
1329
1330         cupsArraySave(Printers);
1331         cupsdDeletePrinter(p, 1);
1332         cupsArrayRestore(Printers);
1333       }
1334     }
1335   }
1336 }
1337
1338
1339 /*
1340  * 'cupsdSendCUPSBrowse()' - Send new browsing information using the CUPS protocol.
1341  */
1342
1343 void
1344 cupsdSendCUPSBrowse(cupsd_printer_t *p) /* I - Printer to send */
1345 {
1346   int                   i;              /* Looping var */
1347   cups_ptype_t          type;           /* Printer type */
1348   cupsd_dirsvc_addr_t   *b;             /* Browse address */
1349   int                   bytes;          /* Length of packet */
1350   char                  packet[1453];   /* Browse data packet */
1351   char                  uri[1024];      /* Printer URI */
1352   char                  options[1024];  /* Browse local options */
1353   cupsd_netif_t         *iface;         /* Network interface */
1354
1355
1356  /*
1357   * Figure out the printer type value...
1358   */
1359
1360   type = p->type | CUPS_PRINTER_REMOTE;
1361
1362   if (!p->accepting)
1363     type |= CUPS_PRINTER_REJECTING;
1364
1365  /*
1366   * Initialize the browse options...
1367   */
1368
1369   if (BrowseLocalOptions)
1370     snprintf(options, sizeof(options), " ipp-options=%s", BrowseLocalOptions);
1371   else
1372     options[0] = '\0';
1373
1374  /*
1375   * Send a packet to each browse address...
1376   */
1377
1378   for (i = NumBrowsers, b = Browsers; i > 0; i --, b ++)
1379     if (b->iface[0])
1380     {
1381      /*
1382       * Send the browse packet to one or more interfaces...
1383       */
1384
1385       if (strcmp(b->iface, "*") == 0)
1386       {
1387        /*
1388         * Send to all local interfaces...
1389         */
1390
1391         cupsdNetIFUpdate();
1392
1393         for (iface = NetIFList; iface != NULL; iface = iface->next)
1394         {
1395          /*
1396           * Only send to local, IPv4 interfaces...
1397           */
1398
1399           if (!iface->is_local || !iface->port ||
1400               iface->address.addr.sa_family != AF_INET)
1401             continue;
1402
1403           httpAssembleURIf(uri, sizeof(uri), "ipp", NULL, iface->hostname,
1404                            iface->port,
1405                            (p->type & CUPS_PRINTER_CLASS) ? "/classes/%s%s" :
1406                                                             "/printers/%s",
1407                            p->name);
1408           snprintf(packet, sizeof(packet), "%x %x %s \"%s\" \"%s\" \"%s\"%s\n",
1409                    type, p->state, uri, p->location ? p->location : "",
1410                    p->info ? p->info : "",
1411                    p->make_model ? p->make_model : "Unknown", options);
1412
1413           bytes = strlen(packet);
1414
1415           cupsdLogMessage(CUPSD_LOG_DEBUG2,
1416                           "cupsdSendBrowseList: (%d bytes to \"%s\") %s", bytes,
1417                           iface->name, packet);
1418
1419           iface->broadcast.ipv4.sin_port = htons(BrowsePort);
1420
1421           sendto(BrowseSocket, packet, bytes, 0,
1422                  (struct sockaddr *)&(iface->broadcast),
1423                  sizeof(struct sockaddr_in));
1424         }
1425       }
1426       else if ((iface = cupsdNetIFFind(b->iface)) != NULL)
1427       {
1428        /*
1429         * Send to the named interface using the IPv4 address...
1430         */
1431
1432         while (iface)
1433           if (strcasecmp(b->iface, iface->name))
1434           {
1435             iface = NULL;
1436             break;
1437           }
1438           else if (iface->address.addr.sa_family == AF_INET && iface->port)
1439             break;
1440           else
1441             iface = iface->next;
1442
1443         if (iface)
1444         {
1445           httpAssembleURIf(uri, sizeof(uri), "ipp", NULL, iface->hostname,
1446                            iface->port,
1447                            (p->type & CUPS_PRINTER_CLASS) ? "/classes/%s%s" :
1448                                                             "/printers/%s",
1449                            p->name);
1450           snprintf(packet, sizeof(packet), "%x %x %s \"%s\" \"%s\" \"%s\"%s\n",
1451                    type, p->state, uri, p->location ? p->location : "",
1452                    p->info ? p->info : "",
1453                    p->make_model ? p->make_model : "Unknown", options);
1454
1455           bytes = strlen(packet);
1456
1457           cupsdLogMessage(CUPSD_LOG_DEBUG2,
1458                           "cupsdSendBrowseList: (%d bytes to \"%s\") %s", bytes,
1459                           iface->name, packet);
1460
1461           iface->broadcast.ipv4.sin_port = htons(BrowsePort);
1462
1463           sendto(BrowseSocket, packet, bytes, 0,
1464                  (struct sockaddr *)&(iface->broadcast),
1465                  sizeof(struct sockaddr_in));
1466         }
1467       }
1468     }
1469     else
1470     {
1471      /*
1472       * Send the browse packet to the indicated address using
1473       * the default server name...
1474       */
1475
1476       snprintf(packet, sizeof(packet), "%x %x %s \"%s\" \"%s\" \"%s\"%s\n",
1477                type, p->state, p->uri,
1478                p->location ? p->location : "",
1479                p->info ? p->info : "",
1480                p->make_model ? p->make_model : "Unknown", options);
1481
1482       bytes = strlen(packet);
1483       cupsdLogMessage(CUPSD_LOG_DEBUG2,
1484                       "cupsdSendBrowseList: (%d bytes) %s", bytes, packet);
1485
1486       if (sendto(BrowseSocket, packet, bytes, 0,
1487                  (struct sockaddr *)&(b->to),
1488                  sizeof(struct sockaddr_in)) <= 0)
1489       {
1490        /*
1491         * Unable to send browse packet, so remove this address from the
1492         * list...
1493         */
1494
1495         cupsdLogMessage(CUPSD_LOG_ERROR,
1496                         "cupsdSendBrowseList: sendto failed for browser %d - %s.",
1497                         b - Browsers + 1, strerror(errno));
1498
1499         if (i > 1)
1500           memcpy(b, b + 1, (i - 1) * sizeof(cupsd_dirsvc_addr_t));
1501
1502         b --;
1503         NumBrowsers --;
1504       }
1505     }
1506 }
1507
1508
1509 #ifdef HAVE_LIBSLP
1510 /*
1511  * 'cupsdSendSLPBrowse()' - Register the specified printer with SLP.
1512  */
1513
1514 void 
1515 cupsdSendSLPBrowse(cupsd_printer_t *p)  /* I - Printer to register */
1516 {
1517   char          srvurl[HTTP_MAX_URI],   /* Printer service URI */
1518                 attrs[8192],            /* Printer attributes */
1519                 finishings[1024],       /* Finishings to support */
1520                 make_model[IPP_MAX_NAME * 2],
1521                                         /* Make and model, quoted */
1522                 location[IPP_MAX_NAME * 2],
1523                                         /* Location, quoted */
1524                 info[IPP_MAX_NAME * 2], /* Info, quoted */
1525                 *src,                   /* Pointer to original string */
1526                 *dst;                   /* Pointer to destination string */
1527   ipp_attribute_t *authentication;      /* uri-authentication-supported value */
1528   SLPError      error;                  /* SLP error, if any */
1529
1530
1531   cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdSendSLPBrowse(%p = \"%s\")", p,
1532                   p->name);
1533
1534  /*
1535   * Make the SLP service URL that conforms to the IANA 
1536   * 'printer:' template.
1537   */
1538
1539   snprintf(srvurl, sizeof(srvurl), SLP_CUPS_SRVTYPE ":%s", p->uri);
1540
1541   cupsdLogMessage(CUPSD_LOG_DEBUG2, "Service URL = \"%s\"", srvurl);
1542
1543  /*
1544   * Figure out the finishings string...
1545   */
1546
1547   if (p->type & CUPS_PRINTER_STAPLE)
1548     strcpy(finishings, "staple");
1549   else
1550     finishings[0] = '\0';
1551
1552   if (p->type & CUPS_PRINTER_BIND)
1553   {
1554     if (finishings[0])
1555       strlcat(finishings, ",bind", sizeof(finishings));
1556     else
1557       strcpy(finishings, "bind");
1558   }
1559
1560   if (p->type & CUPS_PRINTER_PUNCH)
1561   {
1562     if (finishings[0])
1563       strlcat(finishings, ",punch", sizeof(finishings));
1564     else
1565       strcpy(finishings, "punch");
1566   }
1567
1568   if (p->type & CUPS_PRINTER_COVER)
1569   {
1570     if (finishings[0])
1571       strlcat(finishings, ",cover", sizeof(finishings));
1572     else
1573       strcpy(finishings, "cover");
1574   }
1575
1576   if (p->type & CUPS_PRINTER_SORT)
1577   {
1578     if (finishings[0])
1579       strlcat(finishings, ",sort", sizeof(finishings));
1580     else
1581       strcpy(finishings, "sort");
1582   }
1583
1584   if (!finishings[0])
1585     strcpy(finishings, "none");
1586
1587  /*
1588   * Quote any commas in the make and model, location, and info strings...
1589   */
1590
1591   for (src = p->make_model, dst = make_model;
1592        src && *src && dst < (make_model + sizeof(make_model) - 2);)
1593   {
1594     if (*src == ',' || *src == '\\' || *src == ')')
1595       *dst++ = '\\';
1596
1597     *dst++ = *src++;
1598   }
1599
1600   *dst = '\0';
1601
1602   if (!make_model[0])
1603     strcpy(make_model, "Unknown");
1604
1605   for (src = p->location, dst = location;
1606        src && *src && dst < (location + sizeof(location) - 2);)
1607   {
1608     if (*src == ',' || *src == '\\' || *src == ')')
1609       *dst++ = '\\';
1610
1611     *dst++ = *src++;
1612   }
1613
1614   *dst = '\0';
1615
1616   if (!location[0])
1617     strcpy(location, "Unknown");
1618
1619   for (src = p->info, dst = info;
1620        src && *src && dst < (info + sizeof(info) - 2);)
1621   {
1622     if (*src == ',' || *src == '\\' || *src == ')')
1623       *dst++ = '\\';
1624
1625     *dst++ = *src++;
1626   }
1627
1628   *dst = '\0';
1629
1630   if (!info[0])
1631     strcpy(info, "Unknown");
1632
1633  /*
1634   * Get the authentication value...
1635   */
1636
1637   authentication = ippFindAttribute(p->attrs, "uri-authentication-supported",
1638                                     IPP_TAG_KEYWORD);
1639
1640  /*
1641   * Make the SLP attribute string list that conforms to
1642   * the IANA 'printer:' template.
1643   */
1644
1645   snprintf(attrs, sizeof(attrs),
1646            "(printer-uri-supported=%s),"
1647            "(uri-authentication-supported=%s>),"
1648 #ifdef HAVE_SSL
1649            "(uri-security-supported=tls>),"
1650 #else
1651            "(uri-security-supported=none>),"
1652 #endif /* HAVE_SSL */
1653            "(printer-name=%s),"
1654            "(printer-location=%s),"
1655            "(printer-info=%s),"
1656            "(printer-more-info=%s),"
1657            "(printer-make-and-model=%s),"
1658            "(charset-supported=utf-8),"
1659            "(natural-language-configured=%s),"
1660            "(natural-language-supported=de,en,es,fr,it),"
1661            "(color-supported=%s),"
1662            "(finishings-supported=%s),"
1663            "(sides-supported=one-sided%s),"
1664            "(multiple-document-jobs-supported=true)"
1665            "(ipp-versions-supported=1.0,1.1)",
1666            p->uri, authentication->values[0].string.text, p->name, location,
1667            info, p->uri, make_model, DefaultLanguage,
1668            p->type & CUPS_PRINTER_COLOR ? "true" : "false",
1669            finishings,
1670            p->type & CUPS_PRINTER_DUPLEX ?
1671                ",two-sided-long-edge,two-sided-short-edge" : "");
1672
1673   cupsdLogMessage(CUPSD_LOG_DEBUG2, "Attributes = \"%s\"", attrs);
1674
1675  /*
1676   * Register the printer with the SLP server...
1677   */
1678
1679   error = SLPReg(BrowseSLPHandle, srvurl, BrowseTimeout,
1680                  SLP_CUPS_SRVTYPE, attrs, SLP_TRUE, slp_reg_callback, 0);
1681
1682   if (error != SLP_OK)
1683     cupsdLogMessage(CUPSD_LOG_ERROR, "SLPReg of \"%s\" failed with status %d!", p->name,
1684                     error);
1685 }
1686 #endif /* HAVE_LIBSLP */
1687
1688
1689 /*
1690  * 'cupsdStartBrowsing()' - Start sending and receiving broadcast information.
1691  */
1692
1693 void
1694 cupsdStartBrowsing(void)
1695 {
1696   int                   val;            /* Socket option value */
1697   struct sockaddr_in    addr;           /* Broadcast address */
1698
1699
1700   BrowseNext = NULL;
1701
1702   if (!Browsing || !(BrowseLocalProtocols | BrowseRemoteProtocols))
1703     return;
1704
1705   if ((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_CUPS)
1706   {
1707    /*
1708     * Create the broadcast socket...
1709     */
1710
1711     if ((BrowseSocket = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
1712     {
1713       cupsdLogMessage(CUPSD_LOG_ERROR,
1714                       "cupsdStartBrowsing: Unable to create broadcast socket - %s.",
1715                       strerror(errno));
1716       BrowseLocalProtocols &= ~BROWSE_CUPS;
1717       BrowseRemoteProtocols &= ~BROWSE_CUPS;
1718       return;
1719     }
1720
1721    /*
1722     * Set the "broadcast" flag...
1723     */
1724
1725     val = 1;
1726     if (setsockopt(BrowseSocket, SOL_SOCKET, SO_BROADCAST, &val, sizeof(val)))
1727     {
1728       cupsdLogMessage(CUPSD_LOG_ERROR,
1729                       "cupsdStartBrowsing: Unable to set broadcast mode - %s.",
1730                       strerror(errno));
1731
1732 #ifdef WIN32
1733       closesocket(BrowseSocket);
1734 #else
1735       close(BrowseSocket);
1736 #endif /* WIN32 */
1737
1738       BrowseSocket = -1;
1739       BrowseLocalProtocols &= ~BROWSE_CUPS;
1740       BrowseRemoteProtocols &= ~BROWSE_CUPS;
1741       return;
1742     }
1743
1744    /*
1745     * Bind the socket to browse port...
1746     */
1747
1748     memset(&addr, 0, sizeof(addr));
1749     addr.sin_addr.s_addr = htonl(INADDR_ANY);
1750     addr.sin_family      = AF_INET;
1751     addr.sin_port        = htons(BrowsePort);
1752
1753     if (bind(BrowseSocket, (struct sockaddr *)&addr, sizeof(addr)))
1754     {
1755       cupsdLogMessage(CUPSD_LOG_ERROR,
1756                       "cupsdStartBrowsing: Unable to bind broadcast socket - %s.",
1757                       strerror(errno));
1758
1759 #ifdef WIN32
1760       closesocket(BrowseSocket);
1761 #else
1762       close(BrowseSocket);
1763 #endif /* WIN32 */
1764
1765       BrowseSocket = -1;
1766       BrowseLocalProtocols &= ~BROWSE_CUPS;
1767       BrowseRemoteProtocols &= ~BROWSE_CUPS;
1768       return;
1769     }
1770
1771    /*
1772     * Close the socket on exec...
1773     */
1774
1775     fcntl(BrowseSocket, F_SETFD, fcntl(BrowseSocket, F_GETFD) | FD_CLOEXEC);
1776
1777    /*
1778     * Finally, add the socket to the input selection set...
1779     */
1780
1781     cupsdLogMessage(CUPSD_LOG_DEBUG2,
1782                     "cupsdStartBrowsing: Adding fd %d to InputSet...",
1783                     BrowseSocket);
1784
1785     FD_SET(BrowseSocket, InputSet);
1786   }
1787   else
1788     BrowseSocket = -1;
1789
1790 #ifdef HAVE_LIBSLP
1791   if ((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_SLP)
1792   {
1793    /* 
1794     * Open SLP handle...
1795     */
1796
1797     if (SLPOpen("en", SLP_FALSE, &BrowseSLPHandle) != SLP_OK)
1798     {
1799       cupsdLogMessage(CUPSD_LOG_ERROR,
1800                       "Unable to open an SLP handle; disabling SLP browsing!");
1801       BrowseLocalProtocols &= ~BROWSE_SLP;
1802       BrowseRemoteProtocols &= ~BROWSE_SLP;
1803     }
1804
1805     BrowseSLPRefresh = 0;
1806   }
1807 #endif /* HAVE_LIBSLP */
1808 }
1809
1810
1811 /*
1812  * 'cupsdStartPolling()' - Start polling servers as needed.
1813  */
1814
1815 void
1816 cupsdStartPolling(void)
1817 {
1818   int                   i;              /* Looping var */
1819   cupsd_dirsvc_poll_t   *pollp;         /* Current polling server */
1820   char                  polld[1024];    /* Poll daemon path */
1821   char                  sport[10];      /* Server port */
1822   char                  bport[10];      /* Browser port */
1823   char                  interval[10];   /* Poll interval */
1824   int                   statusfds[2];   /* Status pipe */
1825   char                  *argv[6];       /* Arguments */
1826   char                  *envp[100];     /* Environment */
1827
1828
1829  /*
1830   * Don't do anything if we aren't polling...
1831   */
1832
1833   if (NumPolled == 0)
1834   {
1835     PollPipe         = -1;
1836     PollStatusBuffer = NULL;
1837     return;
1838   }
1839
1840  /*
1841   * Setup string arguments for polld, port and interval options.
1842   */
1843
1844   snprintf(polld, sizeof(polld), "%s/daemon/cups-polld", ServerBin);
1845
1846   sprintf(bport, "%d", BrowsePort);
1847
1848   if (BrowseInterval)
1849     sprintf(interval, "%d", BrowseInterval);
1850   else
1851     strcpy(interval, "30");
1852
1853   argv[0] = "cups-polld";
1854   argv[2] = sport;
1855   argv[3] = interval;
1856   argv[4] = bport;
1857   argv[5] = NULL;
1858
1859   cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
1860
1861  /*
1862   * Create a pipe that receives the status messages from each
1863   * polling daemon...
1864   */
1865
1866   if (cupsdOpenPipe(statusfds))
1867   {
1868     cupsdLogMessage(CUPSD_LOG_ERROR,
1869                     "Unable to create polling status pipes - %s.",
1870                     strerror(errno));
1871     PollPipe         = -1;
1872     PollStatusBuffer = NULL;
1873     return;
1874   }
1875
1876   PollPipe         = statusfds[0];
1877   PollStatusBuffer = cupsdStatBufNew(PollPipe, "[Poll]");
1878
1879  /*
1880   * Run each polling daemon, redirecting stderr to the polling pipe...
1881   */
1882
1883   for (i = 0, pollp = Polled; i < NumPolled; i ++, pollp ++)
1884   {
1885     sprintf(sport, "%d", pollp->port);
1886
1887     argv[1] = pollp->hostname;
1888
1889     if (cupsdStartProcess(polld, argv, envp, -1, -1, statusfds[1], -1,
1890                           0, &(pollp->pid)) < 0)
1891     {
1892       cupsdLogMessage(CUPSD_LOG_ERROR,
1893                       "cupsdStartPolling: Unable to fork polling daemon - %s",
1894                       strerror(errno));
1895       pollp->pid = 0;
1896       break;
1897     }
1898     else
1899       cupsdLogMessage(CUPSD_LOG_DEBUG,
1900                       "cupsdStartPolling: Started polling daemon for %s:%d, pid = %d",
1901                       pollp->hostname, pollp->port, pollp->pid);
1902   }
1903
1904   close(statusfds[1]);
1905
1906  /*
1907   * Finally, add the pipe to the input selection set...
1908   */
1909
1910   cupsdLogMessage(CUPSD_LOG_DEBUG2,
1911                   "cupsdStartPolling: Adding fd %d to InputSet...", PollPipe);
1912
1913   FD_SET(PollPipe, InputSet);
1914 }
1915
1916
1917 /*
1918  * 'cupsdStopBrowsing()' - Stop sending and receiving broadcast information.
1919  */
1920
1921 void
1922 cupsdStopBrowsing(void)
1923 {
1924   if (!Browsing || !(BrowseLocalProtocols | BrowseRemoteProtocols))
1925     return;
1926
1927   if ((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_CUPS)
1928   {
1929    /*
1930     * Close the socket and remove it from the input selection set.
1931     */
1932
1933     if (BrowseSocket >= 0)
1934     {
1935 #ifdef WIN32
1936       closesocket(BrowseSocket);
1937 #else
1938       close(BrowseSocket);
1939 #endif /* WIN32 */
1940
1941       cupsdLogMessage(CUPSD_LOG_DEBUG2,
1942                       "cupsdStopBrowsing: Removing fd %d from InputSet...",
1943                       BrowseSocket);
1944
1945       FD_CLR(BrowseSocket, InputSet);
1946       BrowseSocket = -1;
1947     }
1948   }
1949
1950 #ifdef HAVE_LIBSLP
1951   if ((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_SLP)
1952   {
1953    /* 
1954     * Close SLP handle...
1955     */
1956
1957     SLPClose(BrowseSLPHandle);
1958   }
1959 #endif /* HAVE_LIBSLP */
1960 }
1961
1962
1963 /*
1964  * 'cupsdStopPolling()' - Stop polling servers as needed.
1965  */
1966
1967 void
1968 cupsdStopPolling(void)
1969 {
1970   int                   i;              /* Looping var */
1971   cupsd_dirsvc_poll_t   *pollp;         /* Current polling server */
1972
1973
1974   if (PollPipe >= 0)
1975   {
1976     cupsdStatBufDelete(PollStatusBuffer);
1977     close(PollPipe);
1978
1979     cupsdLogMessage(CUPSD_LOG_DEBUG2,
1980                     "cupsdStopPolling: removing fd %d from InputSet.", PollPipe);
1981     FD_CLR(PollPipe, InputSet);
1982
1983     PollPipe         = -1;
1984     PollStatusBuffer = NULL;
1985   }
1986
1987   for (i = 0, pollp = Polled; i < NumPolled; i ++, pollp ++)
1988     if (pollp->pid)
1989       cupsdEndProcess(pollp->pid, 0);
1990 }
1991
1992
1993 /*
1994  * 'cupsdUpdateCUPSBrowse()' - Update the browse lists using the CUPS protocol.
1995  */
1996
1997 void
1998 cupsdUpdateCUPSBrowse(void)
1999 {
2000   int           i;                      /* Looping var */
2001   int           auth;                   /* Authorization status */
2002   int           len;                    /* Length of name string */
2003   int           bytes;                  /* Number of bytes left */
2004   char          packet[1541],           /* Broadcast packet */
2005                 *pptr;                  /* Pointer into packet */
2006   socklen_t     srclen;                 /* Length of source address */
2007   http_addr_t   srcaddr;                /* Source address */
2008   char          srcname[1024];          /* Source hostname */
2009   unsigned      address[4];             /* Source address */
2010   unsigned      type;                   /* Printer type */
2011   unsigned      state;                  /* Printer state */
2012   char          uri[HTTP_MAX_URI],      /* Printer URI */
2013                 method[HTTP_MAX_URI],   /* Method portion of URI */
2014                 username[HTTP_MAX_URI], /* Username portion of URI */
2015                 host[HTTP_MAX_URI],     /* Host portion of URI */
2016                 resource[HTTP_MAX_URI], /* Resource portion of URI */
2017                 info[IPP_MAX_NAME],     /* Information string */
2018                 location[IPP_MAX_NAME], /* Location string */
2019                 make_model[IPP_MAX_NAME];/* Make and model string */
2020   int           port;                   /* Port portion of URI */
2021   cupsd_netif_t *iface;                 /* Network interface */
2022   int           num_attrs;              /* Number of attributes */
2023   cups_option_t *attrs;                 /* Attributes */
2024
2025
2026  /*
2027   * Read a packet from the browse socket...
2028   */
2029
2030   srclen = sizeof(srcaddr);
2031   if ((bytes = recvfrom(BrowseSocket, packet, sizeof(packet) - 1, 0, 
2032                         (struct sockaddr *)&srcaddr, &srclen)) < 0)
2033   {
2034    /*
2035     * "Connection refused" is returned under Linux if the destination port
2036     * or address is unreachable from a previous sendto(); check for the
2037     * error here and ignore it for now...
2038     */
2039
2040     if (errno != ECONNREFUSED && errno != EAGAIN)
2041     {
2042       cupsdLogMessage(CUPSD_LOG_ERROR, "Browse recv failed - %s.",
2043                       strerror(errno));
2044       cupsdLogMessage(CUPSD_LOG_ERROR, "Browsing turned off.");
2045
2046       cupsdStopBrowsing();
2047       Browsing = 0;
2048     }
2049
2050     return;
2051   }
2052
2053   packet[bytes] = '\0';
2054
2055  /*
2056   * Figure out where it came from...
2057   */
2058
2059 #ifdef AF_INET6
2060   if (srcaddr.addr.sa_family == AF_INET6)
2061   {
2062     address[0] = ntohl(srcaddr.ipv6.sin6_addr.s6_addr32[0]);
2063     address[1] = ntohl(srcaddr.ipv6.sin6_addr.s6_addr32[1]);
2064     address[2] = ntohl(srcaddr.ipv6.sin6_addr.s6_addr32[2]);
2065     address[3] = ntohl(srcaddr.ipv6.sin6_addr.s6_addr32[3]);
2066   }
2067   else
2068 #endif /* AF_INET6 */
2069   {
2070     address[0] = 0;
2071     address[1] = 0;
2072     address[2] = 0;
2073     address[3] = ntohl(srcaddr.ipv4.sin_addr.s_addr);
2074   }
2075
2076   if (HostNameLookups)
2077     httpAddrLookup(&srcaddr, srcname, sizeof(srcname));
2078   else
2079     httpAddrString(&srcaddr, srcname, sizeof(srcname));
2080
2081   len = strlen(srcname);
2082
2083  /*
2084   * Do ACL stuff...
2085   */
2086
2087   if (BrowseACL)
2088   {
2089     if (httpAddrLocalhost(&srcaddr) || !strcasecmp(srcname, "localhost"))
2090     {
2091      /*
2092       * Access from localhost (127.0.0.1) is always allowed...
2093       */
2094
2095       auth = AUTH_ALLOW;
2096     }
2097     else
2098     {
2099      /*
2100       * Do authorization checks on the domain/address...
2101       */
2102
2103       switch (BrowseACL->order_type)
2104       {
2105         default :
2106             auth = AUTH_DENY;   /* anti-compiler-warning-code */
2107             break;
2108
2109         case AUTH_ALLOW : /* Order Deny,Allow */
2110             auth = AUTH_ALLOW;
2111
2112             if (cupsdCheckAuth(address, srcname, len,
2113                           BrowseACL->num_deny, BrowseACL->deny))
2114               auth = AUTH_DENY;
2115
2116             if (cupsdCheckAuth(address, srcname, len,
2117                           BrowseACL->num_allow, BrowseACL->allow))
2118               auth = AUTH_ALLOW;
2119             break;
2120
2121         case AUTH_DENY : /* Order Allow,Deny */
2122             auth = AUTH_DENY;
2123
2124             if (cupsdCheckAuth(address, srcname, len,
2125                           BrowseACL->num_allow, BrowseACL->allow))
2126               auth = AUTH_ALLOW;
2127
2128             if (cupsdCheckAuth(address, srcname, len,
2129                           BrowseACL->num_deny, BrowseACL->deny))
2130               auth = AUTH_DENY;
2131             break;
2132       }
2133     }
2134   }
2135   else
2136     auth = AUTH_ALLOW;
2137
2138   if (auth == AUTH_DENY)
2139   {
2140     cupsdLogMessage(CUPSD_LOG_DEBUG,
2141                     "cupsdUpdateCUPSBrowse: Refused %d bytes from %s", bytes,
2142                     srcname);
2143     return;
2144   }
2145
2146   cupsdLogMessage(CUPSD_LOG_DEBUG2,
2147                   "cupsdUpdateCUPSBrowse: (%d bytes from %s) %s", bytes,
2148                   srcname, packet);
2149
2150  /*
2151   * Parse packet...
2152   */
2153
2154   if (sscanf(packet, "%x%x%1023s", &type, &state, uri) < 3)
2155   {
2156     cupsdLogMessage(CUPSD_LOG_WARN,
2157                     "cupsdUpdateCUPSBrowse: Garbled browse packet - %s", packet);
2158     return;
2159   }
2160
2161   strcpy(location, "Location Unknown");
2162   strcpy(info, "No Information Available");
2163   make_model[0] = '\0';
2164   num_attrs     = 0;
2165   attrs         = NULL;
2166
2167   if ((pptr = strchr(packet, '\"')) != NULL)
2168   {
2169    /*
2170     * Have extended information; can't use sscanf for it because not all
2171     * sscanf's allow empty strings with %[^\"]...
2172     */
2173
2174     for (i = 0, pptr ++;
2175          i < (sizeof(location) - 1) && *pptr && *pptr != '\"';
2176          i ++, pptr ++)
2177       location[i] = *pptr;
2178
2179     if (i)
2180       location[i] = '\0';
2181
2182     if (*pptr == '\"')
2183       pptr ++;
2184
2185     while (*pptr && isspace(*pptr & 255))
2186       pptr ++;
2187
2188     if (*pptr == '\"')
2189     {
2190       for (i = 0, pptr ++;
2191            i < (sizeof(info) - 1) && *pptr && *pptr != '\"';
2192            i ++, pptr ++)
2193         info[i] = *pptr;
2194
2195       info[i] = '\0';
2196
2197       if (*pptr == '\"')
2198         pptr ++;
2199
2200       while (*pptr && isspace(*pptr & 255))
2201         pptr ++;
2202
2203       if (*pptr == '\"')
2204       {
2205         for (i = 0, pptr ++;
2206              i < (sizeof(make_model) - 1) && *pptr && *pptr != '\"';
2207              i ++, pptr ++)
2208           make_model[i] = *pptr;
2209
2210         if (*pptr == '\"')
2211           pptr ++;
2212
2213         make_model[i] = '\0';
2214
2215         if (*pptr)
2216           num_attrs = cupsParseOptions(pptr, num_attrs, &attrs);
2217       }
2218     }
2219   }
2220
2221   DEBUG_puts(packet);
2222   DEBUG_printf(("type=%x, state=%x, uri=\"%s\"\n"
2223                 "location=\"%s\", info=\"%s\", make_model=\"%s\"\n",
2224                 type, state, uri, location, info, make_model));
2225
2226  /*
2227   * Pull the URI apart to see if this is a local or remote printer...
2228   */
2229
2230   httpSeparateURI(uri, method, sizeof(method), username, sizeof(username),
2231                   host, sizeof(host), &port, resource, sizeof(resource));
2232
2233   DEBUG_printf(("host=\"%s\", ServerName=\"%s\"\n", host, ServerName));
2234
2235  /*
2236   * Check for packets from the local server...
2237   */
2238
2239   if (!strcasecmp(host, ServerName) && port == LocalPort)
2240   {
2241     cupsFreeOptions(num_attrs, attrs);
2242     return;
2243   }
2244
2245   cupsdNetIFUpdate();
2246
2247   for (iface = NetIFList; iface != NULL; iface = iface->next)
2248     if (!strcasecmp(host, iface->hostname) && port == iface->port)
2249     {
2250       cupsFreeOptions(num_attrs, attrs);
2251       return;
2252     }
2253
2254  /*
2255   * Do relaying...
2256   */
2257
2258   for (i = 0; i < NumRelays; i ++)
2259     if (cupsdCheckAuth(address, srcname, len, 1, &(Relays[i].from)))
2260       if (sendto(BrowseSocket, packet, bytes, 0,
2261                  (struct sockaddr *)&(Relays[i].to),
2262                  sizeof(http_addr_t)) <= 0)
2263       {
2264         cupsdLogMessage(CUPSD_LOG_ERROR,
2265                         "cupsdUpdateCUPSBrowse: sendto failed for relay %d - %s.",
2266                         i + 1, strerror(errno));
2267         cupsFreeOptions(num_attrs, attrs);
2268         return;
2269       }
2270
2271  /*
2272   * Process the browse data...
2273   */
2274
2275   cupsdProcessBrowseData(uri, (cups_ptype_t)type, (ipp_pstate_t)state, location,
2276                          info, make_model, num_attrs, attrs);
2277   cupsFreeOptions(num_attrs, attrs);
2278 }
2279
2280
2281 /*
2282  * 'cupsdUpdatePolling()' - Read status messages from the poll daemons.
2283  */
2284
2285 void
2286 cupsdUpdatePolling(void)
2287 {
2288   char          *ptr,                   /* Pointer to end of line in buffer */
2289                 message[1024];          /* Pointer to message text */
2290   int           loglevel;               /* Log level for message */
2291
2292
2293   while ((ptr = cupsdStatBufUpdate(PollStatusBuffer, &loglevel,
2294                                    message, sizeof(message))) != NULL)
2295     if (!strchr(PollStatusBuffer->buffer, '\n'))
2296       break;
2297
2298   if (ptr == NULL)
2299   {
2300    /*
2301     * All polling processes have died; stop polling...
2302     */
2303
2304     cupsdLogMessage(CUPSD_LOG_ERROR,
2305                     "cupsdUpdatePolling: all polling processes have exited!");
2306     cupsdStopPolling();
2307   }
2308 }
2309
2310
2311 #ifdef HAVE_LIBSLP 
2312 /*
2313  * 'cupsdUpdateSLPBrowse()' - Get browsing information via SLP.
2314  */
2315
2316 void
2317 cupsdUpdateSLPBrowse(void)
2318 {
2319   slpsrvurl_t           *s,             /* Temporary list of service URLs */
2320                         *next;          /* Next service in list */
2321   cupsd_printer_t       p;              /* Printer information */
2322   const char            *uri;           /* Pointer to printer URI */
2323   char                  method[HTTP_MAX_URI],
2324                                         /* Method portion of URI */
2325                         username[HTTP_MAX_URI],
2326                                         /* Username portion of URI */
2327                         host[HTTP_MAX_URI],
2328                                         /* Host portion of URI */
2329                         resource[HTTP_MAX_URI];
2330                                         /* Resource portion of URI */
2331   int                   port;           /* Port portion of URI */
2332
2333
2334   cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdUpdateSLPBrowse() Start...");
2335
2336  /*
2337   * Reset the refresh time...
2338   */
2339
2340   BrowseSLPRefresh = time(NULL) + BrowseInterval;
2341
2342  /* 
2343   * Poll for remote printers using SLP...
2344   */
2345
2346   s = NULL;
2347
2348   SLPFindSrvs(BrowseSLPHandle, SLP_CUPS_SRVTYPE, "", "",
2349               slp_url_callback, &s);
2350
2351  /*
2352   * Loop through the list of available printers...
2353   */
2354
2355   for (; s; s = next)
2356   {
2357    /*
2358     * Save the "next" pointer...
2359     */
2360
2361     next = s->next;
2362
2363    /* 
2364     * Load a cupsd_printer_t structure with the SLP service attributes...
2365     */
2366
2367     SLPFindAttrs(BrowseSLPHandle, s->url, "", "", slp_attr_callback, &p);
2368
2369    /*
2370     * Process this printer entry...
2371     */
2372
2373     uri = s->url + SLP_CUPS_SRVLEN + 1;
2374
2375     if (!strncmp(uri, "http://", 7) || !strncmp(uri, "ipp://", 6))
2376     {
2377      /*
2378       * Pull the URI apart to see if this is a local or remote printer...
2379       */
2380
2381       httpSeparateURI(uri, method, sizeof(method), username, sizeof(username),
2382                       host, sizeof(host), &port, resource, sizeof(resource));
2383
2384       if (strcasecmp(host, ServerName) == 0)
2385         continue;
2386
2387      /*
2388       * OK, at least an IPP printer, see if it is a CUPS printer or
2389       * class...
2390       */
2391
2392       if (strstr(uri, "/printers/") != NULL)
2393         cupsdProcessBrowseData(uri, p.type, IPP_PRINTER_IDLE, p.location,
2394                           p.info, p.make_model, 0, NULL);
2395       else if (strstr(uri, "/classes/") != NULL)
2396         cupsdProcessBrowseData(uri, p.type | CUPS_PRINTER_CLASS, IPP_PRINTER_IDLE,
2397                           p.location, p.info, p.make_model, 0, NULL);
2398     }
2399
2400    /*
2401     * Free this listing...
2402     */
2403
2404     free(s);
2405   }       
2406
2407   cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdUpdateSLPBrowse() End...");
2408 }
2409
2410
2411 /*
2412  * 'slp_attr_callback()' - SLP attribute callback 
2413  */
2414
2415 static SLPBoolean                       /* O - SLP_TRUE for success */
2416 slp_attr_callback(
2417     SLPHandle  hslp,                    /* I - SLP handle */
2418     const char *attrlist,               /* I - Attribute list */
2419     SLPError   errcode,                 /* I - Parsing status for this attr */
2420     void       *cookie)                 /* I - Current printer */
2421 {
2422   char                  *tmp = 0;
2423   cupsd_printer_t       *p = (cupsd_printer_t*)cookie;
2424
2425
2426  /*
2427   * Let the compiler know we won't be using these...
2428   */
2429
2430   (void)hslp;
2431
2432  /*
2433   * Bail if there was an error
2434   */
2435
2436   if (errcode != SLP_OK)
2437     return (SLP_TRUE);
2438
2439  /*
2440   * Parse the attrlist to obtain things needed to build CUPS browse packet
2441   */
2442
2443   memset(p, 0, sizeof(cupsd_printer_t));
2444
2445   p->type = CUPS_PRINTER_REMOTE;
2446
2447   if (slp_get_attr(attrlist, "(printer-location=", &(p->location)))
2448     return (SLP_FALSE);
2449   if (slp_get_attr(attrlist, "(printer-info=", &(p->info)))
2450     return (SLP_FALSE);
2451   if (slp_get_attr(attrlist, "(printer-make-and-model=", &(p->make_model)))
2452     return (SLP_FALSE);
2453
2454   if (slp_get_attr(attrlist, "(color-supported=", &tmp))
2455     return (SLP_FALSE);
2456   if (strcasecmp(tmp, "true") == 0)
2457     p->type |= CUPS_PRINTER_COLOR;
2458
2459   if (slp_get_attr(attrlist, "(finishings-supported=", &tmp))
2460     return (SLP_FALSE);
2461   if (strstr(tmp, "staple"))
2462     p->type |= CUPS_PRINTER_STAPLE;
2463   if (strstr(tmp, "bind"))
2464     p->type |= CUPS_PRINTER_BIND;
2465   if (strstr(tmp, "punch"))
2466     p->type |= CUPS_PRINTER_PUNCH;
2467
2468   if (slp_get_attr(attrlist, "(sides-supported=", &tmp))
2469     return (SLP_FALSE);
2470   if (strstr(tmp,"two-sided"))
2471     p->type |= CUPS_PRINTER_DUPLEX;
2472
2473   cupsdClearString(&tmp);
2474
2475   return (SLP_TRUE);
2476 }
2477
2478
2479 /*
2480  * 'slp_dereg_printer()' - SLPDereg() the specified printer
2481  */
2482
2483 static void 
2484 slp_dereg_printer(cupsd_printer_t *p)   /* I - Printer */
2485 {
2486   char  srvurl[HTTP_MAX_URI];           /* Printer service URI */
2487
2488
2489   cupsdLogMessage(CUPSD_LOG_DEBUG, "slp_dereg_printer: printer=\"%s\"", p->name);
2490
2491   if (!(p->type & CUPS_PRINTER_REMOTE))
2492   {
2493    /*
2494     * Make the SLP service URL that conforms to the IANA 
2495     * 'printer:' template.
2496     */
2497
2498     snprintf(srvurl, sizeof(srvurl), SLP_CUPS_SRVTYPE ":%s", p->uri);
2499
2500    /*
2501     * Deregister the printer...
2502     */
2503
2504     SLPDereg(BrowseSLPHandle, srvurl, slp_reg_callback, 0);
2505   }
2506 }
2507
2508
2509 /*
2510  * 'slp_get_attr()' - Get an attribute from an SLP registration.
2511  */
2512
2513 static int                              /* O - 0 on success */
2514 slp_get_attr(const char *attrlist,      /* I - Attribute list string */
2515              const char *tag,           /* I - Name of attribute */
2516              char       **valbuf)       /* O - Value */
2517 {
2518   char  *ptr1,                          /* Pointer into string */
2519         *ptr2;                          /* ... */
2520
2521
2522   cupsdClearString(valbuf);
2523
2524   if ((ptr1 = strstr(attrlist, tag)) != NULL)
2525   {
2526     ptr1 += strlen(tag);
2527
2528     if ((ptr2 = strchr(ptr1,')')) != NULL)
2529     {
2530      /*
2531       * Copy the value...
2532       */
2533
2534       *valbuf = calloc(ptr2 - ptr1 + 1, 1);
2535       strncpy(*valbuf, ptr1, ptr2 - ptr1);
2536
2537      /*
2538       * Dequote the value...
2539       */
2540
2541       for (ptr1 = *valbuf; *ptr1; ptr1 ++)
2542         if (*ptr1 == '\\' && ptr1[1])
2543           _cups_strcpy(ptr1, ptr1 + 1);
2544
2545       return (0);
2546     }
2547   }
2548
2549   return (-1);
2550 }
2551
2552
2553 /*
2554  * 'slp_reg_callback()' - Empty SLPRegReport.
2555  */
2556
2557 static void
2558 slp_reg_callback(SLPHandle hslp,        /* I - SLP handle */
2559                  SLPError  errcode,     /* I - Error code, if any */
2560                  void      *cookie)     /* I - App data */
2561 {
2562   (void)hslp;
2563   (void)errcode;
2564   (void)cookie;
2565
2566   return;
2567 }
2568
2569
2570 /*
2571  * 'slp_url_callback()' - SLP service url callback
2572  */
2573
2574 static SLPBoolean                       /* O - TRUE = OK, FALSE = error */
2575 slp_url_callback(
2576     SLPHandle      hslp,                /* I - SLP handle */
2577     const char     *srvurl,             /* I - URL of service */
2578     unsigned short lifetime,            /* I - Life of service */
2579     SLPError       errcode,             /* I - Existing error code */
2580     void           *cookie)             /* I - Pointer to service list */
2581 {
2582   slpsrvurl_t   *s,                     /* New service entry */
2583                 **head;                 /* Pointer to head of entry */
2584
2585
2586  /*
2587   * Let the compiler know we won't be using these vars...
2588   */
2589
2590   (void)hslp;
2591   (void)lifetime;
2592
2593  /*
2594   * Bail if there was an error
2595   */
2596
2597   if (errcode != SLP_OK)
2598     return (SLP_TRUE);
2599
2600  /*
2601   * Grab the head of the list...
2602   */
2603
2604   head = (slpsrvurl_t**)cookie;
2605
2606  /*
2607   * Allocate a *temporary* slpsrvurl_t to hold this entry.
2608   */
2609
2610   if ((s = (slpsrvurl_t *)calloc(1, sizeof(slpsrvurl_t))) == NULL)
2611     return (SLP_FALSE);
2612
2613  /*
2614   * Copy the SLP service URL...
2615   */
2616
2617   strlcpy(s->url, srvurl, sizeof(s->url));
2618
2619  /* 
2620   * Link the SLP service URL into the head of the list
2621   */
2622
2623   if (*head)
2624     s->next = *head;
2625
2626   *head = s;
2627
2628   return (SLP_TRUE);
2629 }
2630 #endif /* HAVE_LIBSLP */
2631
2632
2633 /*
2634  * End of "$Id: dirsvc.c 4906 2006-01-10 20:53:28Z mike $".
2635  */