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