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