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