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