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