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