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