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