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