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