]> git.ipfire.org Git - thirdparty/cups.git/blame - backend/dnssd.c
Merge changes from CUPS 1.6svn-r9968.
[thirdparty/cups.git] / backend / dnssd.c
CommitLineData
7a14d768
MS
1/*
2 * "$Id$"
3 *
0268488e 4 * DNS-SD discovery backend for CUPS.
7a14d768 5 *
0268488e 6 * Copyright 2008-2011 by Apple Inc.
7a14d768
MS
7 *
8 * These coded instructions, statements, and computer programs are the
9 * property of Apple Inc. and are protected by Federal copyright
10 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
11 * "LICENSE" which should have been included with this file. If this
12 * file is missing or damaged, see the license at "http://www.cups.org/".
13 *
14 * This file is subject to the Apple OS-Developed Software exception.
15 *
16 * Contents:
17 *
18 * main() - Browse for printers.
19 * browse_callback() - Browse devices.
20 * browse_local_callback() - Browse local devices.
21 * compare_devices() - Compare two devices.
1340db2d
MS
22 * exec_backend() - Execute the backend that corresponds to the
23 * resolved service name.
7a14d768
MS
24 * get_device() - Create or update a device.
25 * query_callback() - Process query data.
38e73f87 26 * sigterm_handler() - Handle termination signals...
7a14d768
MS
27 * unquote() - Unquote a name string.
28 */
29
30/*
31 * Include necessary headers.
32 */
33
34#include "backend-private.h"
35#include <cups/array.h>
36#include <dns_sd.h>
37
38
39/*
40 * Device structure...
41 */
42
43typedef enum
44{
45 CUPS_DEVICE_PRINTER = 0, /* lpd://... */
46 CUPS_DEVICE_IPP, /* ipp://... */
d4d03398 47 CUPS_DEVICE_IPPS, /* ipps://... */
7a14d768
MS
48 CUPS_DEVICE_FAX_IPP, /* ipp://... */
49 CUPS_DEVICE_PDL_DATASTREAM, /* socket://... */
50 CUPS_DEVICE_RIOUSBPRINT /* riousbprint://... */
51} cups_devtype_t;
52
53
54typedef struct
55{
56 DNSServiceRef ref; /* Service reference for resolve */
57 char *name, /* Service name */
58 *domain, /* Domain name */
59 *fullName, /* Full name */
238c3832
MS
60 *make_and_model, /* Make and model from TXT record */
61 *device_id; /* 1284 device ID from TXT record */
7a14d768 62 cups_devtype_t type; /* Device registration type */
1f0275e3
MS
63 int priority, /* Priority associated with type */
64 cups_shared, /* CUPS shared printer? */
7a14d768
MS
65 sent; /* Did we list the device? */
66} cups_device_t;
67
68
38e73f87
MS
69/*
70 * Local globals...
71 */
72
73static int job_canceled = 0;
74 /* Set to 1 on SIGTERM */
75
76
7a14d768
MS
77/*
78 * Local functions...
79 */
80
81static void browse_callback(DNSServiceRef sdRef,
82 DNSServiceFlags flags,
83 uint32_t interfaceIndex,
84 DNSServiceErrorType errorCode,
85 const char *serviceName,
86 const char *regtype,
87 const char *replyDomain, void *context);
88static void browse_local_callback(DNSServiceRef sdRef,
89 DNSServiceFlags flags,
90 uint32_t interfaceIndex,
91 DNSServiceErrorType errorCode,
92 const char *serviceName,
93 const char *regtype,
94 const char *replyDomain,
95 void *context);
96static int compare_devices(cups_device_t *a, cups_device_t *b);
97static void exec_backend(char **argv);
98static cups_device_t *get_device(cups_array_t *devices,
99 const char *serviceName,
100 const char *regtype,
101 const char *replyDomain);
102static void query_callback(DNSServiceRef sdRef,
103 DNSServiceFlags flags,
104 uint32_t interfaceIndex,
105 DNSServiceErrorType errorCode,
106 const char *fullName, uint16_t rrtype,
107 uint16_t rrclass, uint16_t rdlen,
108 const void *rdata, uint32_t ttl,
109 void *context);
38e73f87 110static void sigterm_handler(int sig);
7a14d768
MS
111static void unquote(char *dst, const char *src, size_t dstsize);
112
113
114/*
115 * 'main()' - Browse for printers.
116 */
117
118int /* O - Exit status */
119main(int argc, /* I - Number of command-line args */
120 char *argv[]) /* I - Command-line arguments */
121{
c5571a1d 122 const char *name; /* Backend name */
7a14d768
MS
123 DNSServiceRef main_ref, /* Main service reference */
124 fax_ipp_ref, /* IPP fax service reference */
125 ipp_ref, /* IPP service reference */
126 ipp_tls_ref, /* IPP w/TLS service reference */
d4d03398 127 ipps_ref, /* IPP service reference */
7a14d768
MS
128 local_fax_ipp_ref, /* Local IPP fax service reference */
129 local_ipp_ref, /* Local IPP service reference */
130 local_ipp_tls_ref, /* Local IPP w/TLS service reference */
d4d03398 131 local_ipps_ref, /* Local IPP service reference */
7a14d768
MS
132 local_printer_ref, /* Local LPD service reference */
133 pdl_datastream_ref, /* AppSocket service reference */
134 printer_ref, /* LPD service reference */
135 riousbprint_ref; /* Remote IO service reference */
136 int fd; /* Main file descriptor */
137 fd_set input; /* Input set for select() */
138 struct timeval timeout; /* Timeout for select() */
139 cups_array_t *devices; /* Device array */
140 cups_device_t *device; /* Current device */
bf3816c7 141 char uriName[1024]; /* Unquoted fullName for URI */
38e73f87
MS
142#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
143 struct sigaction action; /* Actions for POSIX signals */
144#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
7a14d768
MS
145
146
147 /*
38e73f87 148 * Don't buffer stderr, and catch SIGTERM...
7a14d768
MS
149 */
150
151 setbuf(stderr, NULL);
152
38e73f87
MS
153#ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
154 sigset(SIGTERM, sigterm_handler);
155#elif defined(HAVE_SIGACTION)
156 memset(&action, 0, sizeof(action));
157
158 sigemptyset(&action.sa_mask);
ee6ddad2
MS
159 action.sa_handler = sigterm_handler;
160 sigaction(SIGTERM, &action, NULL);
38e73f87
MS
161#else
162 signal(SIGTERM, sigterm_handler);
163#endif /* HAVE_SIGSET */
164
165 /*
166 * Check command-line...
167 */
168
7a14d768
MS
169 if (argc >= 6)
170 exec_backend(argv);
171 else if (argc != 1)
172 {
0837b7e8
MS
173 _cupsLangPrintf(stderr,
174 _("Usage: %s job-id user title copies options [file]"),
175 argv[0]);
7a14d768
MS
176 return (1);
177 }
178
c5571a1d
MS
179 /*
180 * Only do discovery when run as "dnssd"...
181 */
182
183 if ((name = strrchr(argv[0], '/')) != NULL)
184 name ++;
185 else
186 name = argv[0];
187
188 if (strcmp(name, "dnssd"))
189 return (0);
190
7a14d768
MS
191 /*
192 * Create an array to track devices...
193 */
194
195 devices = cupsArrayNew((cups_array_func_t)compare_devices, NULL);
196
197 /*
198 * Browse for different kinds of printers...
199 */
200
201 if (DNSServiceCreateConnection(&main_ref) != kDNSServiceErr_NoError)
202 {
203 perror("ERROR: Unable to create service connection");
204 return (1);
205 }
206
207 fd = DNSServiceRefSockFD(main_ref);
208
209 fax_ipp_ref = main_ref;
210 DNSServiceBrowse(&fax_ipp_ref, kDNSServiceFlagsShareConnection, 0,
211 "_fax-ipp._tcp", NULL, browse_callback, devices);
212
213 ipp_ref = main_ref;
214 DNSServiceBrowse(&ipp_ref, kDNSServiceFlagsShareConnection, 0,
215 "_ipp._tcp", NULL, browse_callback, devices);
216
217 ipp_tls_ref = main_ref;
218 DNSServiceBrowse(&ipp_tls_ref, kDNSServiceFlagsShareConnection, 0,
219 "_ipp-tls._tcp", NULL, browse_callback, devices);
220
d4d03398 221 ipps_ref = main_ref;
85dda01c 222 DNSServiceBrowse(&ipps_ref, kDNSServiceFlagsShareConnection, 0,
d4d03398
MS
223 "_ipps._tcp", NULL, browse_callback, devices);
224
7a14d768
MS
225 local_fax_ipp_ref = main_ref;
226 DNSServiceBrowse(&local_fax_ipp_ref, kDNSServiceFlagsShareConnection,
227 kDNSServiceInterfaceIndexLocalOnly,
228 "_fax-ipp._tcp", NULL, browse_local_callback, devices);
229
230 local_ipp_ref = main_ref;
231 DNSServiceBrowse(&local_ipp_ref, kDNSServiceFlagsShareConnection,
232 kDNSServiceInterfaceIndexLocalOnly,
233 "_ipp._tcp", NULL, browse_local_callback, devices);
234
235 local_ipp_tls_ref = main_ref;
236 DNSServiceBrowse(&local_ipp_tls_ref, kDNSServiceFlagsShareConnection,
237 kDNSServiceInterfaceIndexLocalOnly,
238 "_ipp-tls._tcp", NULL, browse_local_callback, devices);
239
d4d03398 240 local_ipps_ref = main_ref;
85dda01c 241 DNSServiceBrowse(&local_ipps_ref, kDNSServiceFlagsShareConnection,
d4d03398
MS
242 kDNSServiceInterfaceIndexLocalOnly,
243 "_ipps._tcp", NULL, browse_local_callback, devices);
244
7a14d768
MS
245 local_printer_ref = main_ref;
246 DNSServiceBrowse(&local_printer_ref, kDNSServiceFlagsShareConnection,
247 kDNSServiceInterfaceIndexLocalOnly,
248 "_printer._tcp", NULL, browse_local_callback, devices);
249
88f9aafc 250 pdl_datastream_ref = main_ref;
7a14d768
MS
251 DNSServiceBrowse(&pdl_datastream_ref, kDNSServiceFlagsShareConnection, 0,
252 "_pdl-datastream._tcp", NULL, browse_callback, devices);
253
254 printer_ref = main_ref;
255 DNSServiceBrowse(&printer_ref, kDNSServiceFlagsShareConnection, 0,
256 "_printer._tcp", NULL, browse_callback, devices);
257
258 riousbprint_ref = main_ref;
259 DNSServiceBrowse(&riousbprint_ref, kDNSServiceFlagsShareConnection, 0,
260 "_riousbprint._tcp", NULL, browse_callback, devices);
261
262 /*
263 * Loop until we are killed...
264 */
265
38e73f87 266 while (!job_canceled)
7a14d768
MS
267 {
268 FD_ZERO(&input);
269 FD_SET(fd, &input);
270
d4d03398
MS
271 timeout.tv_sec = 0;
272 timeout.tv_usec = 250000;
7a14d768
MS
273
274 if (select(fd + 1, &input, NULL, NULL, &timeout) < 0)
275 continue;
276
277 if (FD_ISSET(fd, &input))
278 {
279 /*
280 * Process results of our browsing...
281 */
282
283 DNSServiceProcessResult(main_ref);
284 }
285 else
286 {
287 /*
288 * Announce any devices we've found...
289 */
290
58dc1933 291 DNSServiceErrorType status; /* DNS query status */
1f0275e3 292 cups_device_t *best; /* Best matching device */
7a14d768 293 char device_uri[1024]; /* Device URI */
5eb9da71 294 int count; /* Number of queries */
d4d03398 295 int sent; /* Number of sent */
7a14d768 296
1f0275e3 297 for (device = (cups_device_t *)cupsArrayFirst(devices),
d4d03398 298 best = NULL, count = 0, sent = 0;
7a14d768
MS
299 device;
300 device = (cups_device_t *)cupsArrayNext(devices))
d4d03398
MS
301 {
302 if (device->sent)
303 sent ++;
304
305 if (device->ref)
306 count ++;
307
7a14d768
MS
308 if (!device->ref && !device->sent)
309 {
310 /*
311 * Found the device, now get the TXT record(s) for it...
312 */
313
d4d03398 314 if (count < 20)
5eb9da71
MS
315 {
316 device->ref = main_ref;
317
318 fprintf(stderr, "DEBUG: Querying \"%s\"...\n", device->fullName);
319
58dc1933
MS
320 status = DNSServiceQueryRecord(&(device->ref),
321 kDNSServiceFlagsShareConnection,
322 0, device->fullName,
323 kDNSServiceType_TXT,
324 kDNSServiceClass_IN, query_callback,
325 devices);
326 if (status != kDNSServiceErr_NoError)
327 {
5eb9da71 328 fputs("ERROR: Unable to query for TXT records!\n", stderr);
58dc1933
MS
329 fprintf(stderr, "DEBUG: DNSServiceQueryRecord returned %d\n",
330 status);
331 }
332 else
5eb9da71
MS
333 count ++;
334 }
7a14d768
MS
335 }
336 else if (!device->sent)
337 {
338 /*
339 * Got the TXT records, now report the device...
340 */
341
342 DNSServiceRefDeallocate(device->ref);
343 device->ref = 0;
344
1f0275e3
MS
345 if (!best)
346 best = device;
88f9aafc
MS
347 else if (_cups_strcasecmp(best->name, device->name) ||
348 _cups_strcasecmp(best->domain, device->domain))
1f0275e3 349 {
bf3816c7
MS
350 unquote(uriName, best->fullName, sizeof(uriName));
351
1f0275e3 352 httpAssembleURI(HTTP_URI_CODING_ALL, device_uri, sizeof(device_uri),
38e73f87 353 "dnssd", NULL, uriName, 0,
1f0275e3
MS
354 best->cups_shared ? "/cups" : "/");
355
749b1e90 356 cupsBackendReport("network", device_uri, best->make_and_model,
238c3832 357 best->name, best->device_id, NULL);
1f0275e3
MS
358 best->sent = 1;
359 best = device;
d4d03398
MS
360
361 sent ++;
1f0275e3
MS
362 }
363 else if (best->priority > device->priority ||
364 (best->priority == device->priority &&
365 best->type < device->type))
366 {
367 best->sent = 1;
368 best = device;
d4d03398
MS
369
370 sent ++;
1f0275e3
MS
371 }
372 else
d4d03398 373 {
1f0275e3 374 device->sent = 1;
d4d03398
MS
375
376 sent ++;
377 }
1f0275e3 378 }
d4d03398 379 }
7a14d768 380
1f0275e3
MS
381 if (best)
382 {
bf3816c7
MS
383 unquote(uriName, best->fullName, sizeof(uriName));
384
1f0275e3 385 httpAssembleURI(HTTP_URI_CODING_ALL, device_uri, sizeof(device_uri),
38e73f87 386 "dnssd", NULL, uriName, 0,
1f0275e3 387 best->cups_shared ? "/cups" : "/");
7a14d768 388
749b1e90 389 cupsBackendReport("network", device_uri, best->make_and_model,
238c3832 390 best->name, best->device_id, NULL);
1f0275e3 391 best->sent = 1;
d4d03398 392 sent ++;
1f0275e3 393 }
d4d03398
MS
394
395 if (sent == cupsArrayCount(devices))
396 break;
7a14d768
MS
397 }
398 }
38e73f87
MS
399
400 return (CUPS_BACKEND_OK);
7a14d768
MS
401}
402
403
404/*
405 * 'browse_callback()' - Browse devices.
406 */
407
408static void
409browse_callback(
410 DNSServiceRef sdRef, /* I - Service reference */
411 DNSServiceFlags flags, /* I - Option flags */
412 uint32_t interfaceIndex, /* I - Interface number */
413 DNSServiceErrorType errorCode, /* I - Error, if any */
414 const char *serviceName, /* I - Name of service/device */
415 const char *regtype, /* I - Type of service */
416 const char *replyDomain, /* I - Service domain */
417 void *context) /* I - Devices array */
418{
419 fprintf(stderr, "DEBUG2: browse_callback(sdRef=%p, flags=%x, "
420 "interfaceIndex=%d, errorCode=%d, serviceName=\"%s\", "
421 "regtype=\"%s\", replyDomain=\"%s\", context=%p)\n",
422 sdRef, flags, interfaceIndex, errorCode,
423 serviceName ? serviceName : "(null)",
424 regtype ? regtype : "(null)",
425 replyDomain ? replyDomain : "(null)",
426 context);
427
428 /*
429 * Only process "add" data...
430 */
431
432 if (errorCode != kDNSServiceErr_NoError || !(flags & kDNSServiceFlagsAdd))
433 return;
434
435 /*
436 * Get the device...
437 */
438
439 get_device((cups_array_t *)context, serviceName, regtype, replyDomain);
440}
441
442
443/*
444 * 'browse_local_callback()' - Browse local devices.
445 */
446
447static void
448browse_local_callback(
449 DNSServiceRef sdRef, /* I - Service reference */
450 DNSServiceFlags flags, /* I - Option flags */
451 uint32_t interfaceIndex, /* I - Interface number */
452 DNSServiceErrorType errorCode, /* I - Error, if any */
453 const char *serviceName, /* I - Name of service/device */
454 const char *regtype, /* I - Type of service */
455 const char *replyDomain, /* I - Service domain */
456 void *context) /* I - Devices array */
457{
458 cups_device_t *device; /* Device */
459
460
461 fprintf(stderr, "DEBUG2: browse_local_callback(sdRef=%p, flags=%x, "
462 "interfaceIndex=%d, errorCode=%d, serviceName=\"%s\", "
463 "regtype=\"%s\", replyDomain=\"%s\", context=%p)\n",
464 sdRef, flags, interfaceIndex, errorCode,
465 serviceName ? serviceName : "(null)",
466 regtype ? regtype : "(null)",
467 replyDomain ? replyDomain : "(null)",
468 context);
469
470 /*
471 * Only process "add" data...
472 */
473
474 if (errorCode != kDNSServiceErr_NoError || !(flags & kDNSServiceFlagsAdd))
475 return;
476
477 /*
478 * Get the device...
479 */
480
481 device = get_device((cups_array_t *)context, serviceName, regtype,
482 replyDomain);
483
484 /*
485 * Hide locally-registered devices...
486 */
487
488 fprintf(stderr, "DEBUG: Hiding local printer \"%s\"...\n",
489 device->fullName);
490 device->sent = 1;
491}
492
493
494/*
495 * 'compare_devices()' - Compare two devices.
496 */
497
498static int /* O - Result of comparison */
499compare_devices(cups_device_t *a, /* I - First device */
500 cups_device_t *b) /* I - Second device */
501{
38e73f87 502 return (strcmp(a->name, b->name));
7a14d768
MS
503}
504
505
506/*
507 * 'exec_backend()' - Execute the backend that corresponds to the
508 * resolved service name.
509 */
510
511static void
512exec_backend(char **argv) /* I - Command-line arguments */
513{
514 const char *resolved_uri, /* Resolved device URI */
515 *cups_serverbin; /* Location of programs */
516 char scheme[1024], /* Scheme from URI */
517 *ptr, /* Pointer into scheme */
518 filename[1024]; /* Backend filename */
519
520
521 /*
522 * Resolve the device URI...
523 */
524
e07d4801
MS
525 job_canceled = -1;
526
0268488e
MS
527 while ((resolved_uri = cupsBackendDeviceURI(argv)) == NULL)
528 {
529 _cupsLangPrintFilter(stderr, "INFO", _("Unable to locate printer."));
530 sleep(10);
531
532 if (getenv("CLASS") != NULL)
533 exit(CUPS_BACKEND_FAILED);
534 }
7a14d768
MS
535
536 /*
537 * Extract the scheme from the URI...
538 */
539
540 strlcpy(scheme, resolved_uri, sizeof(scheme));
541 if ((ptr = strchr(scheme, ':')) != NULL)
542 *ptr = '\0';
543
544 /*
545 * Get the filename of the backend...
546 */
547
548 if ((cups_serverbin = getenv("CUPS_SERVERBIN")) == NULL)
549 cups_serverbin = CUPS_SERVERBIN;
550
551 snprintf(filename, sizeof(filename), "%s/backend/%s", cups_serverbin, scheme);
552
553 /*
38e73f87 554 * Overwrite the device URI and run the new backend...
7a14d768
MS
555 */
556
557 setenv("DEVICE_URI", resolved_uri, 1);
558
559 argv[0] = (char *)resolved_uri;
560
561 fprintf(stderr, "DEBUG: Executing backend \"%s\"...\n", filename);
562
563 execv(filename, argv);
564
565 fprintf(stderr, "ERROR: Unable to execute backend \"%s\": %s\n", filename,
566 strerror(errno));
567 exit(CUPS_BACKEND_STOP);
568}
569
570
571/*
572 * 'get_device()' - Create or update a device.
573 */
574
575static cups_device_t * /* O - Device */
576get_device(cups_array_t *devices, /* I - Device array */
577 const char *serviceName, /* I - Name of service/device */
578 const char *regtype, /* I - Type of service */
579 const char *replyDomain) /* I - Service domain */
580{
581 cups_device_t key, /* Search key */
582 *device; /* Device */
58dc1933
MS
583 char fullName[kDNSServiceMaxDomainName];
584 /* Full name for query */
7a14d768
MS
585
586
587 /*
588 * See if this is a new device...
589 */
590
38e73f87 591 key.name = (char *)serviceName;
7a14d768 592
d4d03398 593 if (!strcmp(regtype, "_ipp._tcp."))
7a14d768 594 key.type = CUPS_DEVICE_IPP;
d4d03398
MS
595 else if (!strcmp(regtype, "_ipps._tcp.") ||
596 !strcmp(regtype, "_ipp-tls._tcp."))
597 key.type = CUPS_DEVICE_IPPS;
7a14d768
MS
598 else if (!strcmp(regtype, "_fax-ipp._tcp."))
599 key.type = CUPS_DEVICE_FAX_IPP;
600 else if (!strcmp(regtype, "_printer._tcp."))
601 key.type = CUPS_DEVICE_PRINTER;
602 else if (!strcmp(regtype, "_pdl-datastream._tcp."))
603 key.type = CUPS_DEVICE_PDL_DATASTREAM;
604 else
605 key.type = CUPS_DEVICE_RIOUSBPRINT;
606
1f0275e3
MS
607 for (device = cupsArrayFind(devices, &key);
608 device;
609 device = cupsArrayNext(devices))
88f9aafc 610 if (_cups_strcasecmp(device->name, key.name))
1f0275e3
MS
611 break;
612 else if (device->type == key.type)
38e73f87 613 {
88f9aafc
MS
614 if (!_cups_strcasecmp(device->domain, "local.") &&
615 _cups_strcasecmp(device->domain, replyDomain))
38e73f87
MS
616 {
617 /*
618 * Update the .local listing to use the "global" domain name instead.
619 * The backend will try local lookups first, then the global domain name.
620 */
621
622 free(device->domain);
623 device->domain = strdup(replyDomain);
624
625 DNSServiceConstructFullName(fullName, device->name, regtype,
626 replyDomain);
627 free(device->fullName);
628 device->fullName = strdup(fullName);
629 }
630
1f0275e3 631 return (device);
38e73f87 632 }
7a14d768 633
1f0275e3
MS
634 /*
635 * Yes, add the device...
636 */
7a14d768 637
1f0275e3
MS
638 fprintf(stderr, "DEBUG: Found \"%s.%s%s\"...\n", serviceName, regtype,
639 replyDomain);
7a14d768 640
1f0275e3
MS
641 device = calloc(sizeof(cups_device_t), 1);
642 device->name = strdup(serviceName);
643 device->domain = strdup(replyDomain);
644 device->type = key.type;
645 device->priority = 50;
7a14d768 646
1f0275e3 647 cupsArrayAdd(devices, device);
7a14d768
MS
648
649 /*
1f0275e3 650 * Set the "full name" of this service, which is used for queries...
7a14d768
MS
651 */
652
58dc1933 653 DNSServiceConstructFullName(fullName, serviceName, regtype, replyDomain);
7a14d768
MS
654 device->fullName = strdup(fullName);
655
656 return (device);
657}
658
659
660/*
661 * 'query_callback()' - Process query data.
662 */
663
664static void
665query_callback(
666 DNSServiceRef sdRef, /* I - Service reference */
667 DNSServiceFlags flags, /* I - Data flags */
668 uint32_t interfaceIndex, /* I - Interface */
669 DNSServiceErrorType errorCode, /* I - Error, if any */
670 const char *fullName, /* I - Full service name */
671 uint16_t rrtype, /* I - Record type */
672 uint16_t rrclass, /* I - Record class */
673 uint16_t rdlen, /* I - Length of record data */
674 const void *rdata, /* I - Record data */
675 uint32_t ttl, /* I - Time-to-live */
676 void *context) /* I - Devices array */
677{
678 cups_array_t *devices; /* Device array */
679 char name[1024], /* Service name */
238c3832
MS
680 *ptr; /* Pointer into string */
681 cups_device_t dkey, /* Search key */
7a14d768
MS
682 *device; /* Device */
683
684
685 fprintf(stderr, "DEBUG2: query_callback(sdRef=%p, flags=%x, "
686 "interfaceIndex=%d, errorCode=%d, fullName=\"%s\", "
687 "rrtype=%u, rrclass=%u, rdlen=%u, rdata=%p, ttl=%u, "
688 "context=%p)\n",
689 sdRef, flags, interfaceIndex, errorCode,
690 fullName ? fullName : "(null)", rrtype, rrclass, rdlen, rdata, ttl,
691 context);
692
693 /*
694 * Only process "add" data...
695 */
696
697 if (errorCode != kDNSServiceErr_NoError || !(flags & kDNSServiceFlagsAdd))
698 return;
699
700 /*
701 * Lookup the service in the devices array.
702 */
703
238c3832
MS
704 devices = (cups_array_t *)context;
705 dkey.name = name;
7a14d768
MS
706
707 unquote(name, fullName, sizeof(name));
708
238c3832
MS
709 if ((dkey.domain = strstr(name, "._tcp.")) != NULL)
710 dkey.domain += 6;
7a14d768 711 else
238c3832 712 dkey.domain = (char *)"local.";
7a14d768
MS
713
714 if ((ptr = strstr(name, "._")) != NULL)
715 *ptr = '\0';
716
d4d03398 717 if (strstr(fullName, "_ipp._tcp."))
238c3832 718 dkey.type = CUPS_DEVICE_IPP;
d4d03398
MS
719 else if (strstr(fullName, "_ipps._tcp.") ||
720 strstr(fullName, "_ipp-tls._tcp."))
721 dkey.type = CUPS_DEVICE_IPPS;
1f0275e3 722 else if (strstr(fullName, "_fax-ipp._tcp."))
238c3832 723 dkey.type = CUPS_DEVICE_FAX_IPP;
1f0275e3 724 else if (strstr(fullName, "_printer._tcp."))
238c3832 725 dkey.type = CUPS_DEVICE_PRINTER;
1f0275e3 726 else if (strstr(fullName, "_pdl-datastream._tcp."))
238c3832 727 dkey.type = CUPS_DEVICE_PDL_DATASTREAM;
1f0275e3 728 else
238c3832 729 dkey.type = CUPS_DEVICE_RIOUSBPRINT;
1f0275e3 730
238c3832 731 for (device = cupsArrayFind(devices, &dkey);
1f0275e3
MS
732 device;
733 device = cupsArrayNext(devices))
7a14d768 734 {
88f9aafc
MS
735 if (_cups_strcasecmp(device->name, dkey.name) ||
736 _cups_strcasecmp(device->domain, dkey.domain))
1f0275e3
MS
737 {
738 device = NULL;
739 break;
740 }
238c3832 741 else if (device->type == dkey.type)
1f0275e3
MS
742 {
743 /*
744 * Found it, pull out the priority and make and model from the TXT
745 * record and save it...
746 */
7a14d768 747
238c3832
MS
748 const uint8_t *data, /* Pointer into data */
749 *datanext, /* Next key/value pair */
750 *dataend; /* End of entire TXT record */
751 uint8_t datalen; /* Length of current key/value pair */
752 char key[256], /* Key string */
753 value[256], /* Value string */
754 make_and_model[512],
755 /* Manufacturer and model */
756 model[256], /* Model */
757 device_id[2048];/* 1284 device ID */
7a14d768
MS
758
759
238c3832
MS
760 device_id[0] = '\0';
761 make_and_model[0] = '\0';
7a14d768 762
238c3832
MS
763 strcpy(model, "Unknown");
764
765 for (data = rdata, dataend = data + rdlen;
766 data < dataend;
767 data = datanext)
1f0275e3 768 {
238c3832
MS
769 /*
770 * Read a key/value pair starting with an 8-bit length. Since the
771 * length is 8 bits and the size of the key/value buffers is 256, we
772 * don't need to check for overflow...
773 */
7a14d768 774
238c3832 775 datalen = *data++;
7a14d768 776
238c3832
MS
777 if (!datalen || (data + datalen) >= dataend)
778 break;
1f0275e3 779
238c3832 780 datanext = data + datalen;
1f0275e3 781
238c3832
MS
782 for (ptr = key; data < datanext && *data != '='; data ++)
783 *ptr++ = *data;
784 *ptr = '\0';
785
786 if (data < datanext && *data == '=')
1f0275e3 787 {
238c3832 788 data ++;
1f0275e3 789
238c3832
MS
790 if (data < datanext)
791 memcpy(value, data, datanext - data);
792 value[datanext - data] = '\0';
d4d03398
MS
793
794 fprintf(stderr, "DEBUG2: query_callback: \"%s=%s\".\n",
795 key, value);
1f0275e3
MS
796 }
797 else
d4d03398
MS
798 {
799 fprintf(stderr, "DEBUG2: query_callback: \"%s\" with no value.\n",
800 key);
238c3832 801 continue;
d4d03398 802 }
238c3832 803
88f9aafc 804 if (!_cups_strncasecmp(key, "usb_", 4))
1f0275e3 805 {
238c3832
MS
806 /*
807 * Add USB device ID information...
808 */
7a14d768 809
238c3832
MS
810 ptr = device_id + strlen(device_id);
811 snprintf(ptr, sizeof(device_id) - (ptr - device_id), "%s:%s;",
812 key + 4, value);
813 }
814
88f9aafc
MS
815 if (!_cups_strcasecmp(key, "usb_MFG") || !_cups_strcasecmp(key, "usb_MANU") ||
816 !_cups_strcasecmp(key, "usb_MANUFACTURER"))
238c3832 817 strcpy(make_and_model, value);
88f9aafc 818 else if (!_cups_strcasecmp(key, "usb_MDL") || !_cups_strcasecmp(key, "usb_MODEL"))
238c3832 819 strcpy(model, value);
88f9aafc 820 else if (!_cups_strcasecmp(key, "product") && !strstr(value, "Ghostscript"))
1f0275e3 821 {
238c3832 822 if (value[0] == '(')
1f0275e3 823 {
238c3832
MS
824 /*
825 * Strip parenthesis...
826 */
1f0275e3 827
238c3832 828 if ((ptr = value + strlen(value) - 1) > value && *ptr == ')')
1f0275e3 829 *ptr = '\0';
238c3832
MS
830
831 strcpy(model, value + 1);
1f0275e3
MS
832 }
833 else
238c3832
MS
834 strcpy(model, value);
835 }
88f9aafc 836 else if (!_cups_strcasecmp(key, "ty"))
238c3832
MS
837 {
838 strcpy(model, value);
839
840 if ((ptr = strchr(model, ',')) != NULL)
841 *ptr = '\0';
7a14d768 842 }
88f9aafc 843 else if (!_cups_strcasecmp(key, "priority"))
238c3832
MS
844 device->priority = atoi(value);
845 else if ((device->type == CUPS_DEVICE_IPP ||
d4d03398 846 device->type == CUPS_DEVICE_IPPS ||
238c3832 847 device->type == CUPS_DEVICE_PRINTER) &&
88f9aafc 848 !_cups_strcasecmp(key, "printer-type"))
238c3832
MS
849 {
850 /*
851 * This is a CUPS printer!
852 */
853
854 device->cups_shared = 1;
855
856 if (device->type == CUPS_DEVICE_PRINTER)
857 device->sent = 1;
858 }
859 }
860
861 if (device->device_id)
862 free(device->device_id);
863
864 if (!device_id[0] && strcmp(model, "Unknown"))
865 {
866 if (make_and_model[0])
867 snprintf(device_id, sizeof(device_id), "MFG:%s;MDL:%s;",
868 make_and_model, model);
88f9aafc 869 else if (!_cups_strncasecmp(model, "designjet ", 10))
238c3832 870 snprintf(device_id, sizeof(device_id), "MFG:HP;MDL:%s", model + 10);
88f9aafc 871 else if (!_cups_strncasecmp(model, "stylus ", 7))
238c3832
MS
872 snprintf(device_id, sizeof(device_id), "MFG:EPSON;MDL:%s", model + 7);
873 else if ((ptr = strchr(model, ' ')) != NULL)
874 {
875 /*
876 * Assume the first word is the make...
877 */
878
879 memcpy(make_and_model, model, ptr - model);
880 make_and_model[ptr - model] = '\0';
881
882 snprintf(device_id, sizeof(device_id), "MFG:%s;MDL:%s",
883 make_and_model, ptr + 1);
884 }
7a14d768 885 }
238c3832
MS
886
887 if (device_id[0])
888 device->device_id = strdup(device_id);
1f0275e3 889 else
238c3832 890 device->device_id = NULL;
7a14d768 891
1f0275e3
MS
892 if (device->make_and_model)
893 free(device->make_and_model);
7a14d768 894
1f0275e3
MS
895 if (make_and_model[0])
896 {
897 strlcat(make_and_model, " ", sizeof(make_and_model));
898 strlcat(make_and_model, model, sizeof(make_and_model));
238c3832 899
1f0275e3
MS
900 device->make_and_model = strdup(make_and_model);
901 }
902 else
903 device->make_and_model = strdup(model);
1f0275e3 904 break;
7a14d768
MS
905 }
906 }
1f0275e3
MS
907
908 if (!device)
7a14d768
MS
909 fprintf(stderr, "DEBUG: Ignoring TXT record for \"%s\"...\n", fullName);
910}
911
912
38e73f87
MS
913/*
914 * 'sigterm_handler()' - Handle termination signals...
915 */
916
917static void
918sigterm_handler(int sig) /* I - Signal number (unused) */
919{
321d8d57
MS
920 (void)sig;
921
e07d4801
MS
922 if (job_canceled)
923 exit(CUPS_BACKEND_OK);
924 else
925 job_canceled = 1;
38e73f87
MS
926}
927
928
7a14d768
MS
929/*
930 * 'unquote()' - Unquote a name string.
931 */
932
933static void
934unquote(char *dst, /* I - Destination buffer */
935 const char *src, /* I - Source string */
936 size_t dstsize) /* I - Size of destination buffer */
937{
938 char *dstend = dst + dstsize - 1; /* End of destination buffer */
939
940
941 while (*src && dst < dstend)
942 {
943 if (*src == '\\')
944 {
945 src ++;
946 if (isdigit(src[0] & 255) && isdigit(src[1] & 255) &&
947 isdigit(src[2] & 255))
948 {
949 *dst++ = ((((src[0] - '0') * 10) + src[1] - '0') * 10) + src[2] - '0';
950 src += 3;
951 }
952 else
953 *dst++ = *src++;
954 }
955 else
956 *dst++ = *src ++;
957 }
958
959 *dst = '\0';
960}
961
962
963/*
964 * End of "$Id$".
965 */