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