]> git.ipfire.org Git - thirdparty/cups.git/blame - backend/dnssd.c
Import CUPS v1.7.4
[thirdparty/cups.git] / backend / dnssd.c
CommitLineData
7a14d768 1/*
215ef638 2 * "$Id: dnssd.c 11971 2014-07-01 14:38:29Z msweet $"
7a14d768 3 *
215ef638 4 * DNS-SD discovery backend for CUPS.
7a14d768 5 *
215ef638 6 * Copyright 2008-2014 by Apple Inc.
7a14d768 7 *
215ef638
MS
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/".
7a14d768 13 *
215ef638 14 * This file is subject to the Apple OS-Developed Software exception.
7a14d768
MS
15 */
16
17/*
18 * Include necessary headers.
19 */
20
21#include "backend-private.h"
22#include <cups/array.h>
f3c17241
MS
23#ifdef HAVE_DNSSD
24# include <dns_sd.h>
25#endif /* HAVE_DNSSD */
26#ifdef HAVE_AVAHI
27# include <avahi-client/client.h>
28# include <avahi-client/lookup.h>
29# include <avahi-common/simple-watch.h>
30# include <avahi-common/domain.h>
31# include <avahi-common/error.h>
32# include <avahi-common/malloc.h>
33#define kDNSServiceMaxDomainName AVAHI_DOMAIN_NAME_MAX
34#endif /* HAVE_AVAHI */
7a14d768
MS
35
36
37/*
38 * Device structure...
39 */
40
41typedef enum
42{
43 CUPS_DEVICE_PRINTER = 0, /* lpd://... */
d4d03398 44 CUPS_DEVICE_IPPS, /* ipps://... */
3e7fe0ca 45 CUPS_DEVICE_IPP, /* ipp://... */
7a14d768
MS
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{
f3c17241
MS
54#ifdef HAVE_DNSSD
55 DNSServiceRef ref; /* Service reference for query */
56#endif /* HAVE_DNSSD */
57#ifdef HAVE_AVAHI
58 AvahiRecordBrowser *ref; /* Browser for query */
59#endif /* HAVE_AVAHI */
7a14d768
MS
60 char *name, /* Service name */
61 *domain, /* Domain name */
62 *fullName, /* Full name */
238c3832
MS
63 *make_and_model, /* Make and model from TXT record */
64 *device_id; /* 1284 device ID from TXT record */
7a14d768 65 cups_devtype_t type; /* Device registration type */
1f0275e3
MS
66 int priority, /* Priority associated with type */
67 cups_shared, /* CUPS shared printer? */
7a14d768
MS
68 sent; /* Did we list the device? */
69} cups_device_t;
70
71
38e73f87
MS
72/*
73 * Local globals...
74 */
75
76static int job_canceled = 0;
77 /* Set to 1 on SIGTERM */
f3c17241
MS
78#ifdef HAVE_AVAHI
79static AvahiSimplePoll *simple_poll = NULL;
80 /* Poll information */
81static int got_data = 0; /* Got data from poll? */
71f63681 82static int browsers = 0; /* Number of running browsers */
f3c17241 83#endif /* HAVE_AVAHI */
38e73f87
MS
84
85
7a14d768
MS
86/*
87 * Local functions...
88 */
89
f3c17241 90#ifdef HAVE_DNSSD
7a14d768
MS
91static void browse_callback(DNSServiceRef sdRef,
92 DNSServiceFlags flags,
93 uint32_t interfaceIndex,
94 DNSServiceErrorType errorCode,
95 const char *serviceName,
96 const char *regtype,
82cc1f9a
MS
97 const char *replyDomain, void *context)
98 __attribute__((nonnull(1,5,6,7,8)));
7a14d768
MS
99static void browse_local_callback(DNSServiceRef sdRef,
100 DNSServiceFlags flags,
101 uint32_t interfaceIndex,
102 DNSServiceErrorType errorCode,
103 const char *serviceName,
104 const char *regtype,
105 const char *replyDomain,
82cc1f9a
MS
106 void *context)
107 __attribute__((nonnull(1,5,6,7,8)));
f3c17241
MS
108#endif /* HAVE_DNSSD */
109#ifdef HAVE_AVAHI
110static void browse_callback(AvahiServiceBrowser *browser,
111 AvahiIfIndex interface,
112 AvahiProtocol protocol,
113 AvahiBrowserEvent event,
114 const char *serviceName,
115 const char *regtype,
116 const char *replyDomain,
117 AvahiLookupResultFlags flags,
118 void *context);
119static void client_callback(AvahiClient *client,
120 AvahiClientState state,
121 void *context);
122#endif /* HAVE_AVAHI */
123
7a14d768
MS
124static int compare_devices(cups_device_t *a, cups_device_t *b);
125static void exec_backend(char **argv);
126static cups_device_t *get_device(cups_array_t *devices,
127 const char *serviceName,
128 const char *regtype,
82cc1f9a
MS
129 const char *replyDomain)
130 __attribute__((nonnull(1,2,3,4)));
f3c17241 131#ifdef HAVE_DNSSD
7a14d768
MS
132static void query_callback(DNSServiceRef sdRef,
133 DNSServiceFlags flags,
134 uint32_t interfaceIndex,
135 DNSServiceErrorType errorCode,
136 const char *fullName, uint16_t rrtype,
137 uint16_t rrclass, uint16_t rdlen,
138 const void *rdata, uint32_t ttl,
82cc1f9a
MS
139 void *context)
140 __attribute__((nonnull(1,5,9,11)));
f3c17241
MS
141#elif defined(HAVE_AVAHI)
142static int poll_callback(struct pollfd *pollfds,
143 unsigned int num_pollfds, int timeout,
144 void *context);
145static void query_callback(AvahiRecordBrowser *browser,
146 AvahiIfIndex interface,
147 AvahiProtocol protocol,
148 AvahiBrowserEvent event,
149 const char *name, uint16_t rrclass,
150 uint16_t rrtype, const void *rdata,
151 size_t rdlen,
152 AvahiLookupResultFlags flags,
153 void *context);
154#endif /* HAVE_DNSSD */
38e73f87 155static void sigterm_handler(int sig);
82cc1f9a
MS
156static void unquote(char *dst, const char *src, size_t dstsize)
157 __attribute__((nonnull(1,2)));
7a14d768
MS
158
159
160/*
161 * 'main()' - Browse for printers.
162 */
163
164int /* O - Exit status */
165main(int argc, /* I - Number of command-line args */
166 char *argv[]) /* I - Command-line arguments */
167{
c5571a1d 168 const char *name; /* Backend name */
f3c17241
MS
169 cups_array_t *devices; /* Device array */
170 cups_device_t *device; /* Current device */
171 char uriName[1024]; /* Unquoted fullName for URI */
172#ifdef HAVE_DNSSD
173 int fd; /* Main file descriptor */
174 fd_set input; /* Input set for select() */
175 struct timeval timeout; /* Timeout for select() */
7a14d768
MS
176 DNSServiceRef main_ref, /* Main service reference */
177 fax_ipp_ref, /* IPP fax service reference */
178 ipp_ref, /* IPP service reference */
179 ipp_tls_ref, /* IPP w/TLS service reference */
d4d03398 180 ipps_ref, /* IPP service reference */
7a14d768
MS
181 local_fax_ipp_ref, /* Local IPP fax service reference */
182 local_ipp_ref, /* Local IPP service reference */
183 local_ipp_tls_ref, /* Local IPP w/TLS service reference */
d4d03398 184 local_ipps_ref, /* Local IPP service reference */
7a14d768
MS
185 local_printer_ref, /* Local LPD service reference */
186 pdl_datastream_ref, /* AppSocket service reference */
187 printer_ref, /* LPD service reference */
188 riousbprint_ref; /* Remote IO service reference */
f3c17241
MS
189#endif /* HAVE_DNSSD */
190#ifdef HAVE_AVAHI
191 AvahiClient *client; /* Client information */
192 int error; /* Error code, if any */
193#endif /* HAVE_AVAHI */
38e73f87
MS
194#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
195 struct sigaction action; /* Actions for POSIX signals */
196#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
7a14d768
MS
197
198
199 /*
38e73f87 200 * Don't buffer stderr, and catch SIGTERM...
7a14d768
MS
201 */
202
203 setbuf(stderr, NULL);
204
38e73f87
MS
205#ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
206 sigset(SIGTERM, sigterm_handler);
207#elif defined(HAVE_SIGACTION)
208 memset(&action, 0, sizeof(action));
209
210 sigemptyset(&action.sa_mask);
ee6ddad2
MS
211 action.sa_handler = sigterm_handler;
212 sigaction(SIGTERM, &action, NULL);
38e73f87
MS
213#else
214 signal(SIGTERM, sigterm_handler);
215#endif /* HAVE_SIGSET */
216
217 /*
218 * Check command-line...
219 */
220
7a14d768
MS
221 if (argc >= 6)
222 exec_backend(argv);
223 else if (argc != 1)
224 {
0837b7e8
MS
225 _cupsLangPrintf(stderr,
226 _("Usage: %s job-id user title copies options [file]"),
227 argv[0]);
7a14d768
MS
228 return (1);
229 }
230
c5571a1d
MS
231 /*
232 * Only do discovery when run as "dnssd"...
233 */
234
235 if ((name = strrchr(argv[0], '/')) != NULL)
236 name ++;
237 else
238 name = argv[0];
239
240 if (strcmp(name, "dnssd"))
241 return (0);
242
7a14d768
MS
243 /*
244 * Create an array to track devices...
245 */
246
247 devices = cupsArrayNew((cups_array_func_t)compare_devices, NULL);
248
249 /*
250 * Browse for different kinds of printers...
251 */
252
f3c17241 253#ifdef HAVE_DNSSD
7a14d768
MS
254 if (DNSServiceCreateConnection(&main_ref) != kDNSServiceErr_NoError)
255 {
256 perror("ERROR: Unable to create service connection");
257 return (1);
258 }
259
260 fd = DNSServiceRefSockFD(main_ref);
261
262 fax_ipp_ref = main_ref;
263 DNSServiceBrowse(&fax_ipp_ref, kDNSServiceFlagsShareConnection, 0,
264 "_fax-ipp._tcp", NULL, browse_callback, devices);
265
266 ipp_ref = main_ref;
267 DNSServiceBrowse(&ipp_ref, kDNSServiceFlagsShareConnection, 0,
268 "_ipp._tcp", NULL, browse_callback, devices);
269
270 ipp_tls_ref = main_ref;
271 DNSServiceBrowse(&ipp_tls_ref, kDNSServiceFlagsShareConnection, 0,
272 "_ipp-tls._tcp", NULL, browse_callback, devices);
273
d4d03398 274 ipps_ref = main_ref;
85dda01c 275 DNSServiceBrowse(&ipps_ref, kDNSServiceFlagsShareConnection, 0,
d4d03398
MS
276 "_ipps._tcp", NULL, browse_callback, devices);
277
7a14d768
MS
278 local_fax_ipp_ref = main_ref;
279 DNSServiceBrowse(&local_fax_ipp_ref, kDNSServiceFlagsShareConnection,
280 kDNSServiceInterfaceIndexLocalOnly,
281 "_fax-ipp._tcp", NULL, browse_local_callback, devices);
282
283 local_ipp_ref = main_ref;
284 DNSServiceBrowse(&local_ipp_ref, kDNSServiceFlagsShareConnection,
285 kDNSServiceInterfaceIndexLocalOnly,
286 "_ipp._tcp", NULL, browse_local_callback, devices);
287
288 local_ipp_tls_ref = main_ref;
289 DNSServiceBrowse(&local_ipp_tls_ref, kDNSServiceFlagsShareConnection,
290 kDNSServiceInterfaceIndexLocalOnly,
291 "_ipp-tls._tcp", NULL, browse_local_callback, devices);
292
d4d03398 293 local_ipps_ref = main_ref;
85dda01c 294 DNSServiceBrowse(&local_ipps_ref, kDNSServiceFlagsShareConnection,
d4d03398
MS
295 kDNSServiceInterfaceIndexLocalOnly,
296 "_ipps._tcp", NULL, browse_local_callback, devices);
297
7a14d768
MS
298 local_printer_ref = main_ref;
299 DNSServiceBrowse(&local_printer_ref, kDNSServiceFlagsShareConnection,
300 kDNSServiceInterfaceIndexLocalOnly,
301 "_printer._tcp", NULL, browse_local_callback, devices);
302
88f9aafc 303 pdl_datastream_ref = main_ref;
7a14d768
MS
304 DNSServiceBrowse(&pdl_datastream_ref, kDNSServiceFlagsShareConnection, 0,
305 "_pdl-datastream._tcp", NULL, browse_callback, devices);
306
307 printer_ref = main_ref;
308 DNSServiceBrowse(&printer_ref, kDNSServiceFlagsShareConnection, 0,
309 "_printer._tcp", NULL, browse_callback, devices);
310
311 riousbprint_ref = main_ref;
312 DNSServiceBrowse(&riousbprint_ref, kDNSServiceFlagsShareConnection, 0,
313 "_riousbprint._tcp", NULL, browse_callback, devices);
f3c17241
MS
314#endif /* HAVE_DNSSD */
315
316#ifdef HAVE_AVAHI
317 if ((simple_poll = avahi_simple_poll_new()) == NULL)
318 {
a29fd7dd 319 fputs("DEBUG: Unable to create Avahi simple poll object.\n", stderr);
f3c17241
MS
320 return (1);
321 }
322
323 avahi_simple_poll_set_func(simple_poll, poll_callback, NULL);
324
325 client = avahi_client_new(avahi_simple_poll_get(simple_poll),
326 0, client_callback, simple_poll, &error);
327 if (!client)
328 {
a29fd7dd 329 fputs("DEBUG: Unable to create Avahi client.\n", stderr);
f3c17241
MS
330 return (1);
331 }
332
71f63681 333 browsers = 6;
f3c17241
MS
334 avahi_service_browser_new(client, AVAHI_IF_UNSPEC,
335 AVAHI_PROTO_UNSPEC,
336 "_fax-ipp._tcp", NULL, 0,
337 browse_callback, devices);
338 avahi_service_browser_new(client, AVAHI_IF_UNSPEC,
339 AVAHI_PROTO_UNSPEC,
340 "_ipp._tcp", NULL, 0,
341 browse_callback, devices);
342 avahi_service_browser_new(client, AVAHI_IF_UNSPEC,
343 AVAHI_PROTO_UNSPEC,
344 "_ipp-tls._tcp", NULL, 0,
345 browse_callback, devices);
346 avahi_service_browser_new(client, AVAHI_IF_UNSPEC,
347 AVAHI_PROTO_UNSPEC,
348 "_ipps._tcp", NULL, 0,
349 browse_callback, devices);
350 avahi_service_browser_new(client, AVAHI_IF_UNSPEC,
351 AVAHI_PROTO_UNSPEC,
352 "_pdl-datastream._tcp",
353 NULL, 0,
354 browse_callback,
355 devices);
356 avahi_service_browser_new(client, AVAHI_IF_UNSPEC,
357 AVAHI_PROTO_UNSPEC,
358 "_printer._tcp", NULL, 0,
359 browse_callback, devices);
360#endif /* HAVE_AVAHI */
7a14d768
MS
361
362 /*
363 * Loop until we are killed...
364 */
365
38e73f87 366 while (!job_canceled)
7a14d768 367 {
f3c17241
MS
368 int announce = 0; /* Announce printers? */
369
370#ifdef HAVE_DNSSD
7a14d768
MS
371 FD_ZERO(&input);
372 FD_SET(fd, &input);
373
d4d03398 374 timeout.tv_sec = 0;
f3c17241 375 timeout.tv_usec = 500000;
7a14d768
MS
376
377 if (select(fd + 1, &input, NULL, NULL, &timeout) < 0)
378 continue;
379
380 if (FD_ISSET(fd, &input))
381 {
382 /*
383 * Process results of our browsing...
384 */
385
386 DNSServiceProcessResult(main_ref);
387 }
388 else
f3c17241
MS
389 announce = 1;
390
391#elif defined(HAVE_AVAHI)
392 got_data = 0;
393
394 if ((error = avahi_simple_poll_iterate(simple_poll, 500)) > 0)
395 {
396 /*
397 * We've been told to exit the loop. Perhaps the connection to
398 * Avahi failed.
399 */
400
401 break;
402 }
403
404 if (!got_data)
405 announce = 1;
406#endif /* HAVE_DNSSD */
407
408/* fprintf(stderr, "DEBUG: announce=%d\n", announce);*/
409
410 if (announce)
7a14d768
MS
411 {
412 /*
413 * Announce any devices we've found...
414 */
415
f3c17241 416#ifdef HAVE_DNSSD
58dc1933 417 DNSServiceErrorType status; /* DNS query status */
f3c17241 418#endif /* HAVE_DNSSD */
1f0275e3 419 cups_device_t *best; /* Best matching device */
7a14d768 420 char device_uri[1024]; /* Device URI */
5eb9da71 421 int count; /* Number of queries */
d4d03398 422 int sent; /* Number of sent */
7a14d768 423
1f0275e3 424 for (device = (cups_device_t *)cupsArrayFirst(devices),
d4d03398 425 best = NULL, count = 0, sent = 0;
7a14d768
MS
426 device;
427 device = (cups_device_t *)cupsArrayNext(devices))
d4d03398
MS
428 {
429 if (device->sent)
430 sent ++;
431
432 if (device->ref)
433 count ++;
434
7a14d768
MS
435 if (!device->ref && !device->sent)
436 {
437 /*
438 * Found the device, now get the TXT record(s) for it...
439 */
440
f3c17241 441 if (count < 50)
5eb9da71 442 {
5eb9da71
MS
443 fprintf(stderr, "DEBUG: Querying \"%s\"...\n", device->fullName);
444
f3c17241
MS
445#ifdef HAVE_DNSSD
446 device->ref = main_ref;
447
58dc1933
MS
448 status = DNSServiceQueryRecord(&(device->ref),
449 kDNSServiceFlagsShareConnection,
450 0, device->fullName,
451 kDNSServiceType_TXT,
452 kDNSServiceClass_IN, query_callback,
f3c17241 453 device);
58dc1933 454 if (status != kDNSServiceErr_NoError)
f3c17241
MS
455 fprintf(stderr,
456 "ERROR: Unable to query \"%s\" for TXT records: %d\n",
457 device->fullName, status);
458 /* Users never see this */
58dc1933 459 else
5eb9da71 460 count ++;
f3c17241
MS
461
462#else
463 if ((device->ref = avahi_record_browser_new(client, AVAHI_IF_UNSPEC,
464 AVAHI_PROTO_UNSPEC,
465 device->fullName,
466 AVAHI_DNS_CLASS_IN,
467 AVAHI_DNS_TYPE_TXT,
468 0,
469 query_callback,
470 device)) == NULL)
471 fprintf(stderr,
472 "ERROR: Unable to query \"%s\" for TXT records: %s\n",
473 device->fullName,
474 avahi_strerror(avahi_client_errno(client)));
475 /* Users never see this */
476 else
477 count ++;
478#endif /* HAVE_AVAHI */
5eb9da71 479 }
7a14d768
MS
480 }
481 else if (!device->sent)
482 {
f3c17241 483#ifdef HAVE_DNSSD
7a14d768
MS
484 /*
485 * Got the TXT records, now report the device...
486 */
487
488 DNSServiceRefDeallocate(device->ref);
f3c17241
MS
489#else
490 avahi_record_browser_free(device->ref);
491#endif /* HAVE_DNSSD */
492
493 device->ref = NULL;
7a14d768 494
1f0275e3
MS
495 if (!best)
496 best = device;
88f9aafc
MS
497 else if (_cups_strcasecmp(best->name, device->name) ||
498 _cups_strcasecmp(best->domain, device->domain))
1f0275e3 499 {
bf3816c7
MS
500 unquote(uriName, best->fullName, sizeof(uriName));
501
1f0275e3 502 httpAssembleURI(HTTP_URI_CODING_ALL, device_uri, sizeof(device_uri),
38e73f87 503 "dnssd", NULL, uriName, 0,
1f0275e3
MS
504 best->cups_shared ? "/cups" : "/");
505
749b1e90 506 cupsBackendReport("network", device_uri, best->make_and_model,
238c3832 507 best->name, best->device_id, NULL);
1f0275e3
MS
508 best->sent = 1;
509 best = device;
d4d03398
MS
510
511 sent ++;
1f0275e3
MS
512 }
513 else if (best->priority > device->priority ||
514 (best->priority == device->priority &&
515 best->type < device->type))
516 {
517 best->sent = 1;
518 best = device;
d4d03398
MS
519
520 sent ++;
1f0275e3
MS
521 }
522 else
d4d03398 523 {
1f0275e3 524 device->sent = 1;
d4d03398
MS
525
526 sent ++;
527 }
1f0275e3 528 }
d4d03398 529 }
7a14d768 530
1f0275e3
MS
531 if (best)
532 {
bf3816c7
MS
533 unquote(uriName, best->fullName, sizeof(uriName));
534
1f0275e3 535 httpAssembleURI(HTTP_URI_CODING_ALL, device_uri, sizeof(device_uri),
38e73f87 536 "dnssd", NULL, uriName, 0,
1f0275e3 537 best->cups_shared ? "/cups" : "/");
7a14d768 538
749b1e90 539 cupsBackendReport("network", device_uri, best->make_and_model,
238c3832 540 best->name, best->device_id, NULL);
1f0275e3 541 best->sent = 1;
d4d03398 542 sent ++;
1f0275e3 543 }
d4d03398 544
f3c17241
MS
545 fprintf(stderr, "DEBUG: sent=%d, count=%d\n", sent, count);
546
71f63681
MS
547#ifdef HAVE_AVAHI
548 if (sent == cupsArrayCount(devices) && browsers == 0)
549#else
d4d03398 550 if (sent == cupsArrayCount(devices))
71f63681 551#endif /* HAVE_AVAHI */
d4d03398 552 break;
7a14d768
MS
553 }
554 }
38e73f87
MS
555
556 return (CUPS_BACKEND_OK);
7a14d768
MS
557}
558
559
f3c17241 560#ifdef HAVE_DNSSD
7a14d768
MS
561/*
562 * 'browse_callback()' - Browse devices.
563 */
564
565static void
566browse_callback(
567 DNSServiceRef sdRef, /* I - Service reference */
568 DNSServiceFlags flags, /* I - Option flags */
569 uint32_t interfaceIndex, /* I - Interface number */
570 DNSServiceErrorType errorCode, /* I - Error, if any */
571 const char *serviceName, /* I - Name of service/device */
572 const char *regtype, /* I - Type of service */
573 const char *replyDomain, /* I - Service domain */
574 void *context) /* I - Devices array */
575{
576 fprintf(stderr, "DEBUG2: browse_callback(sdRef=%p, flags=%x, "
577 "interfaceIndex=%d, errorCode=%d, serviceName=\"%s\", "
578 "regtype=\"%s\", replyDomain=\"%s\", context=%p)\n",
579 sdRef, flags, interfaceIndex, errorCode,
a29fd7dd 580 serviceName, regtype, replyDomain, context);
7a14d768
MS
581
582 /*
583 * Only process "add" data...
584 */
585
586 if (errorCode != kDNSServiceErr_NoError || !(flags & kDNSServiceFlagsAdd))
587 return;
588
589 /*
590 * Get the device...
591 */
592
593 get_device((cups_array_t *)context, serviceName, regtype, replyDomain);
594}
595
596
597/*
598 * 'browse_local_callback()' - Browse local devices.
599 */
600
601static void
602browse_local_callback(
603 DNSServiceRef sdRef, /* I - Service reference */
604 DNSServiceFlags flags, /* I - Option flags */
605 uint32_t interfaceIndex, /* I - Interface number */
606 DNSServiceErrorType errorCode, /* I - Error, if any */
607 const char *serviceName, /* I - Name of service/device */
608 const char *regtype, /* I - Type of service */
609 const char *replyDomain, /* I - Service domain */
610 void *context) /* I - Devices array */
611{
612 cups_device_t *device; /* Device */
613
614
615 fprintf(stderr, "DEBUG2: browse_local_callback(sdRef=%p, flags=%x, "
616 "interfaceIndex=%d, errorCode=%d, serviceName=\"%s\", "
617 "regtype=\"%s\", replyDomain=\"%s\", context=%p)\n",
618 sdRef, flags, interfaceIndex, errorCode,
a29fd7dd 619 serviceName, regtype, replyDomain, context);
7a14d768
MS
620
621 /*
622 * Only process "add" data...
623 */
624
625 if (errorCode != kDNSServiceErr_NoError || !(flags & kDNSServiceFlagsAdd))
626 return;
627
628 /*
629 * Get the device...
630 */
631
632 device = get_device((cups_array_t *)context, serviceName, regtype,
633 replyDomain);
634
635 /*
636 * Hide locally-registered devices...
637 */
638
639 fprintf(stderr, "DEBUG: Hiding local printer \"%s\"...\n",
640 device->fullName);
641 device->sent = 1;
642}
f3c17241
MS
643#endif /* HAVE_DNSSD */
644
645
646#ifdef HAVE_AVAHI
647/*
648 * 'browse_callback()' - Browse devices.
649 */
650
651static void
652browse_callback(
653 AvahiServiceBrowser *browser, /* I - Browser */
654 AvahiIfIndex interface, /* I - Interface index (unused) */
655 AvahiProtocol protocol, /* I - Network protocol (unused) */
656 AvahiBrowserEvent event, /* I - What happened */
657 const char *name, /* I - Service name */
658 const char *type, /* I - Registration type */
659 const char *domain, /* I - Domain */
660 AvahiLookupResultFlags flags, /* I - Flags */
661 void *context) /* I - Devices array */
662{
663 AvahiClient *client = avahi_service_browser_get_client(browser);
664 /* Client information */
665
666
667 (void)interface;
668 (void)protocol;
669 (void)context;
670
671 switch (event)
672 {
673 case AVAHI_BROWSER_FAILURE:
674 fprintf(stderr, "DEBUG: browse_callback: %s\n",
675 avahi_strerror(avahi_client_errno(client)));
676 avahi_simple_poll_quit(simple_poll);
677 break;
678
679 case AVAHI_BROWSER_NEW:
680 /*
681 * This object is new on the network.
682 */
683
684 if (flags & AVAHI_LOOKUP_RESULT_LOCAL)
685 {
686 /*
687 * This comes from the local machine so ignore it.
688 */
689
690 fprintf(stderr, "DEBUG: Ignoring local service %s.\n", name);
691 }
692 else
693 {
694 /*
695 * Create a device entry for it if it doesn't yet exist.
696 */
697
698 get_device((cups_array_t *)context, name, type, domain);
699 }
700 break;
701
702 case AVAHI_BROWSER_REMOVE:
f3c17241
MS
703 case AVAHI_BROWSER_CACHE_EXHAUSTED:
704 break;
71f63681
MS
705
706 case AVAHI_BROWSER_ALL_FOR_NOW:
707 browsers--;
708 break;
f3c17241
MS
709 }
710}
711
712
713/*
714 * 'client_callback()' - Avahi client callback function.
715 */
716
717static void
718client_callback(
719 AvahiClient *client, /* I - Client information (unused) */
720 AvahiClientState state, /* I - Current state */
721 void *context) /* I - User data (unused) */
722{
723 (void)client;
724 (void)context;
725
726 /*
727 * If the connection drops, quit.
728 */
729
730 if (state == AVAHI_CLIENT_FAILURE)
731 {
732 fputs("DEBUG: Avahi connection failed.\n", stderr);
733 avahi_simple_poll_quit(simple_poll);
734 }
735}
736#endif /* HAVE_AVAHI */
7a14d768
MS
737
738
739/*
740 * 'compare_devices()' - Compare two devices.
741 */
742
743static int /* O - Result of comparison */
744compare_devices(cups_device_t *a, /* I - First device */
745 cups_device_t *b) /* I - Second device */
746{
38e73f87 747 return (strcmp(a->name, b->name));
7a14d768
MS
748}
749
750
751/*
752 * 'exec_backend()' - Execute the backend that corresponds to the
753 * resolved service name.
754 */
755
756static void
757exec_backend(char **argv) /* I - Command-line arguments */
758{
759 const char *resolved_uri, /* Resolved device URI */
760 *cups_serverbin; /* Location of programs */
761 char scheme[1024], /* Scheme from URI */
762 *ptr, /* Pointer into scheme */
763 filename[1024]; /* Backend filename */
764
765
766 /*
767 * Resolve the device URI...
768 */
769
e07d4801
MS
770 job_canceled = -1;
771
0268488e
MS
772 while ((resolved_uri = cupsBackendDeviceURI(argv)) == NULL)
773 {
774 _cupsLangPrintFilter(stderr, "INFO", _("Unable to locate printer."));
775 sleep(10);
776
777 if (getenv("CLASS") != NULL)
778 exit(CUPS_BACKEND_FAILED);
779 }
7a14d768
MS
780
781 /*
782 * Extract the scheme from the URI...
783 */
784
785 strlcpy(scheme, resolved_uri, sizeof(scheme));
786 if ((ptr = strchr(scheme, ':')) != NULL)
787 *ptr = '\0';
788
789 /*
790 * Get the filename of the backend...
791 */
792
793 if ((cups_serverbin = getenv("CUPS_SERVERBIN")) == NULL)
794 cups_serverbin = CUPS_SERVERBIN;
795
796 snprintf(filename, sizeof(filename), "%s/backend/%s", cups_serverbin, scheme);
797
798 /*
38e73f87 799 * Overwrite the device URI and run the new backend...
7a14d768
MS
800 */
801
802 setenv("DEVICE_URI", resolved_uri, 1);
803
804 argv[0] = (char *)resolved_uri;
805
806 fprintf(stderr, "DEBUG: Executing backend \"%s\"...\n", filename);
807
808 execv(filename, argv);
809
810 fprintf(stderr, "ERROR: Unable to execute backend \"%s\": %s\n", filename,
811 strerror(errno));
812 exit(CUPS_BACKEND_STOP);
813}
814
815
f3c17241
MS
816/*
817 * 'device_type()' - Get DNS-SD type enumeration from string.
818 */
819
820static int
821device_type(const char *regtype)
822{
823#ifdef HAVE_AVAHI
824 if (!strcmp(regtype, "_ipp._tcp"))
825 return (CUPS_DEVICE_IPP);
826 else if (!strcmp(regtype, "_ipps._tcp") ||
827 !strcmp(regtype, "_ipp-tls._tcp"))
828 return (CUPS_DEVICE_IPPS);
829 else if (!strcmp(regtype, "_fax-ipp._tcp"))
830 return (CUPS_DEVICE_FAX_IPP);
831 else if (!strcmp(regtype, "_printer._tcp"))
832 return (CUPS_DEVICE_PDL_DATASTREAM);
833#else
834 if (!strcmp(regtype, "_ipp._tcp."))
835 return (CUPS_DEVICE_IPP);
836 else if (!strcmp(regtype, "_ipps._tcp.") ||
837 !strcmp(regtype, "_ipp-tls._tcp."))
838 return (CUPS_DEVICE_IPPS);
839 else if (!strcmp(regtype, "_fax-ipp._tcp."))
840 return (CUPS_DEVICE_FAX_IPP);
841 else if (!strcmp(regtype, "_printer._tcp."))
842 return (CUPS_DEVICE_PRINTER);
843 else if (!strcmp(regtype, "_pdl-datastream._tcp."))
844 return (CUPS_DEVICE_PDL_DATASTREAM);
845#endif /* HAVE_AVAHI */
846
847 return (CUPS_DEVICE_RIOUSBPRINT);
848}
849
850
7a14d768
MS
851/*
852 * 'get_device()' - Create or update a device.
853 */
854
855static cups_device_t * /* O - Device */
856get_device(cups_array_t *devices, /* I - Device array */
857 const char *serviceName, /* I - Name of service/device */
858 const char *regtype, /* I - Type of service */
859 const char *replyDomain) /* I - Service domain */
860{
861 cups_device_t key, /* Search key */
862 *device; /* Device */
58dc1933
MS
863 char fullName[kDNSServiceMaxDomainName];
864 /* Full name for query */
7a14d768
MS
865
866
867 /*
868 * See if this is a new device...
869 */
870
38e73f87 871 key.name = (char *)serviceName;
f3c17241 872 key.type = device_type(regtype);
7a14d768 873
1f0275e3
MS
874 for (device = cupsArrayFind(devices, &key);
875 device;
876 device = cupsArrayNext(devices))
88f9aafc 877 if (_cups_strcasecmp(device->name, key.name))
1f0275e3
MS
878 break;
879 else if (device->type == key.type)
38e73f87 880 {
88f9aafc
MS
881 if (!_cups_strcasecmp(device->domain, "local.") &&
882 _cups_strcasecmp(device->domain, replyDomain))
38e73f87
MS
883 {
884 /*
885 * Update the .local listing to use the "global" domain name instead.
886 * The backend will try local lookups first, then the global domain name.
887 */
888
889 free(device->domain);
890 device->domain = strdup(replyDomain);
891
f3c17241 892#ifdef HAVE_DNSSD
38e73f87
MS
893 DNSServiceConstructFullName(fullName, device->name, regtype,
894 replyDomain);
f3c17241
MS
895#else /* HAVE_AVAHI */
896 avahi_service_name_join(fullName, kDNSServiceMaxDomainName,
a29fd7dd 897 serviceName, regtype, replyDomain);
f3c17241
MS
898#endif /* HAVE_DNSSD */
899
38e73f87
MS
900 free(device->fullName);
901 device->fullName = strdup(fullName);
902 }
903
1f0275e3 904 return (device);
38e73f87 905 }
7a14d768 906
1f0275e3
MS
907 /*
908 * Yes, add the device...
909 */
7a14d768 910
215ef638
MS
911#ifdef HAVE_DNSSD
912 fprintf(stderr, "DEBUG: Found \"%s.%s%s\"...\n", serviceName, regtype, replyDomain);
913#else /* HAVE_AVAHI */
914 fprintf(stderr, "DEBUG: Found \"%s.%s.%s\"...\n", serviceName, regtype, replyDomain);
915#endif /* HAVE_DNSSD */
7a14d768 916
1f0275e3
MS
917 device = calloc(sizeof(cups_device_t), 1);
918 device->name = strdup(serviceName);
919 device->domain = strdup(replyDomain);
920 device->type = key.type;
921 device->priority = 50;
7a14d768 922
1f0275e3 923 cupsArrayAdd(devices, device);
7a14d768
MS
924
925 /*
1f0275e3 926 * Set the "full name" of this service, which is used for queries...
7a14d768
MS
927 */
928
f3c17241 929#ifdef HAVE_DNSSD
58dc1933 930 DNSServiceConstructFullName(fullName, serviceName, regtype, replyDomain);
f3c17241
MS
931#else /* HAVE_AVAHI */
932 avahi_service_name_join(fullName, kDNSServiceMaxDomainName,
933 serviceName, regtype, replyDomain);
934#endif /* HAVE_DNSSD */
935
7a14d768
MS
936 device->fullName = strdup(fullName);
937
938 return (device);
939}
940
941
f3c17241
MS
942#ifdef HAVE_AVAHI
943/*
944 * 'poll_callback()' - Wait for input on the specified file descriptors.
945 *
946 * Note: This function is needed because avahi_simple_poll_iterate is broken
947 * and always uses a timeout of 0 (!) milliseconds.
948 * (Avahi Ticket #364)
949 */
950
951static int /* O - Number of file descriptors matching */
952poll_callback(
953 struct pollfd *pollfds, /* I - File descriptors */
954 unsigned int num_pollfds, /* I - Number of file descriptors */
955 int timeout, /* I - Timeout in milliseconds (unused) */
956 void *context) /* I - User data (unused) */
957{
958 int val; /* Return value */
959
960
961 (void)timeout;
962 (void)context;
963
964 val = poll(pollfds, num_pollfds, 500);
965
966 if (val < 0)
967 fprintf(stderr, "DEBUG: poll_callback: %s\n", strerror(errno));
968 else if (val > 0)
969 got_data = 1;
970
971 return (val);
972}
973#endif /* HAVE_AVAHI */
974
975
976#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
977# ifdef HAVE_DNSSD
7a14d768
MS
978/*
979 * 'query_callback()' - Process query data.
980 */
981
982static void
983query_callback(
984 DNSServiceRef sdRef, /* I - Service reference */
985 DNSServiceFlags flags, /* I - Data flags */
986 uint32_t interfaceIndex, /* I - Interface */
987 DNSServiceErrorType errorCode, /* I - Error, if any */
988 const char *fullName, /* I - Full service name */
989 uint16_t rrtype, /* I - Record type */
990 uint16_t rrclass, /* I - Record class */
991 uint16_t rdlen, /* I - Length of record data */
992 const void *rdata, /* I - Record data */
993 uint32_t ttl, /* I - Time-to-live */
f3c17241 994 void *context) /* I - Device */
7a14d768 995{
f3c17241
MS
996# else
997/*
998 * 'query_callback()' - Process query data.
999 */
7a14d768 1000
f3c17241
MS
1001static void
1002query_callback(
1003 AvahiRecordBrowser *browser, /* I - Record browser */
1004 AvahiIfIndex interfaceIndex,
1005 /* I - Interface index (unused) */
1006 AvahiProtocol protocol, /* I - Network protocol (unused) */
1007 AvahiBrowserEvent event, /* I - What happened? */
1008 const char *fullName, /* I - Service name */
1009 uint16_t rrclass, /* I - Record class */
1010 uint16_t rrtype, /* I - Record type */
1011 const void *rdata, /* I - TXT record */
1012 size_t rdlen, /* I - Length of TXT record */
1013 AvahiLookupResultFlags flags, /* I - Flags */
1014 void *context) /* I - Device */
1015{
1016 AvahiClient *client = avahi_record_browser_get_client(browser);
1017 /* Client information */
1018# endif /* HAVE_DNSSD */
1019 char *ptr; /* Pointer into string */
1020 cups_device_t *device = (cups_device_t *)context;
1021 /* Device */
1022 const uint8_t *data, /* Pointer into data */
1023 *datanext, /* Next key/value pair */
1024 *dataend; /* End of entire TXT record */
1025 uint8_t datalen; /* Length of current key/value pair */
1026 char key[256], /* Key string */
1027 value[256], /* Value string */
1028 make_and_model[512], /* Manufacturer and model */
1029 model[256], /* Model */
c1420c87 1030 pdl[256], /* PDL */
f3c17241
MS
1031 device_id[2048]; /* 1284 device ID */
1032
1033
1034# ifdef HAVE_DNSSD
7a14d768
MS
1035 fprintf(stderr, "DEBUG2: query_callback(sdRef=%p, flags=%x, "
1036 "interfaceIndex=%d, errorCode=%d, fullName=\"%s\", "
1037 "rrtype=%u, rrclass=%u, rdlen=%u, rdata=%p, ttl=%u, "
1038 "context=%p)\n",
1039 sdRef, flags, interfaceIndex, errorCode,
1040 fullName ? fullName : "(null)", rrtype, rrclass, rdlen, rdata, ttl,
1041 context);
1042
1043 /*
1044 * Only process "add" data...
1045 */
1046
1047 if (errorCode != kDNSServiceErr_NoError || !(flags & kDNSServiceFlagsAdd))
1048 return;
1049
f3c17241
MS
1050# else
1051 fprintf(stderr, "DEBUG2: query_callback(browser=%p, interfaceIndex=%d, "
1052 "protocol=%d, event=%d, fullName=\"%s\", rrclass=%u, "
1053 "rrtype=%u, rdata=%p, rdlen=%u, flags=%x, context=%p)\n",
1054 browser, interfaceIndex, protocol, event,
1055 fullName ? fullName : "(null)", rrclass, rrtype, rdata,
1056 (unsigned)rdlen, flags, context);
1057
7a14d768 1058 /*
f3c17241 1059 * Only process "add" data...
7a14d768
MS
1060 */
1061
f3c17241
MS
1062 if (event != AVAHI_BROWSER_NEW)
1063 {
1064 if (event == AVAHI_BROWSER_FAILURE)
1065 fprintf(stderr, "ERROR: %s\n",
1066 avahi_strerror(avahi_client_errno(client)));
7a14d768 1067
f3c17241
MS
1068 return;
1069 }
1070# endif /* HAVE_DNSSD */
7a14d768 1071
f3c17241
MS
1072 /*
1073 * Pull out the priority and make and model from the TXT
1074 * record and save it...
1075 */
7a14d768 1076
f3c17241
MS
1077 device_id[0] = '\0';
1078 make_and_model[0] = '\0';
c1420c87 1079 pdl[0] = '\0';
7a14d768 1080
5a9febac 1081 strlcpy(model, "Unknown", sizeof(model));
1f0275e3 1082
f3c17241
MS
1083 for (data = rdata, dataend = data + rdlen;
1084 data < dataend;
1085 data = datanext)
7a14d768 1086 {
f3c17241
MS
1087 /*
1088 * Read a key/value pair starting with an 8-bit length. Since the
1089 * length is 8 bits and the size of the key/value buffers is 256, we
1090 * don't need to check for overflow...
1091 */
1092
1093 datalen = *data++;
1094
1095 if (!datalen || (data + datalen) > dataend)
1f0275e3 1096 break;
f3c17241
MS
1097
1098 datanext = data + datalen;
1099
1100 for (ptr = key; data < datanext && *data != '='; data ++)
1101 *ptr++ = *data;
1102 *ptr = '\0';
1103
1104 if (data < datanext && *data == '=')
1f0275e3 1105 {
f3c17241 1106 data ++;
7a14d768 1107
f3c17241
MS
1108 if (data < datanext)
1109 memcpy(value, data, datanext - data);
1110 value[datanext - data] = '\0';
7a14d768 1111
f3c17241
MS
1112 fprintf(stderr, "DEBUG2: query_callback: \"%s=%s\".\n",
1113 key, value);
1114 }
1115 else
1116 {
1117 fprintf(stderr, "DEBUG2: query_callback: \"%s\" with no value.\n",
1118 key);
1119 continue;
1120 }
7a14d768 1121
f3c17241
MS
1122 if (!_cups_strncasecmp(key, "usb_", 4))
1123 {
1124 /*
1125 * Add USB device ID information...
1126 */
7a14d768 1127
f3c17241
MS
1128 ptr = device_id + strlen(device_id);
1129 snprintf(ptr, sizeof(device_id) - (ptr - device_id), "%s:%s;",
1130 key + 4, value);
1131 }
238c3832 1132
f3c17241
MS
1133 if (!_cups_strcasecmp(key, "usb_MFG") || !_cups_strcasecmp(key, "usb_MANU") ||
1134 !_cups_strcasecmp(key, "usb_MANUFACTURER"))
5a9febac 1135 strlcpy(make_and_model, value, sizeof(make_and_model));
f3c17241 1136 else if (!_cups_strcasecmp(key, "usb_MDL") || !_cups_strcasecmp(key, "usb_MODEL"))
5a9febac 1137 strlcpy(model, value, sizeof(model));
f3c17241
MS
1138 else if (!_cups_strcasecmp(key, "product") && !strstr(value, "Ghostscript"))
1139 {
1140 if (value[0] == '(')
1f0275e3 1141 {
238c3832 1142 /*
f3c17241 1143 * Strip parenthesis...
238c3832 1144 */
7a14d768 1145
f3c17241
MS
1146 if ((ptr = value + strlen(value) - 1) > value && *ptr == ')')
1147 *ptr = '\0';
7a14d768 1148
5a9febac 1149 strlcpy(model, value + 1, sizeof(model));
f3c17241
MS
1150 }
1151 else
5a9febac 1152 strlcpy(model, value, sizeof(model));
f3c17241
MS
1153 }
1154 else if (!_cups_strcasecmp(key, "ty"))
1155 {
5a9febac 1156 strlcpy(model, value, sizeof(model));
1f0275e3 1157
f3c17241 1158 if ((ptr = strchr(model, ',')) != NULL)
238c3832 1159 *ptr = '\0';
f3c17241 1160 }
c1420c87
MS
1161 else if (!_cups_strcasecmp(key, "pdl"))
1162 strlcpy(pdl, value, sizeof(pdl));
f3c17241
MS
1163 else if (!_cups_strcasecmp(key, "priority"))
1164 device->priority = atoi(value);
1165 else if ((device->type == CUPS_DEVICE_IPP ||
1166 device->type == CUPS_DEVICE_IPPS ||
1167 device->type == CUPS_DEVICE_PRINTER) &&
1168 !_cups_strcasecmp(key, "printer-type"))
1169 {
1170 /*
1171 * This is a CUPS printer!
1172 */
238c3832 1173
f3c17241 1174 device->cups_shared = 1;
238c3832 1175
f3c17241
MS
1176 if (device->type == CUPS_DEVICE_PRINTER)
1177 device->sent = 1;
1178 }
1179 }
238c3832 1180
f3c17241
MS
1181 if (device->device_id)
1182 free(device->device_id);
238c3832 1183
f3c17241
MS
1184 if (!device_id[0] && strcmp(model, "Unknown"))
1185 {
1186 if (make_and_model[0])
1187 snprintf(device_id, sizeof(device_id), "MFG:%s;MDL:%s;",
1188 make_and_model, model);
1189 else if (!_cups_strncasecmp(model, "designjet ", 10))
1190 snprintf(device_id, sizeof(device_id), "MFG:HP;MDL:%s", model + 10);
1191 else if (!_cups_strncasecmp(model, "stylus ", 7))
1192 snprintf(device_id, sizeof(device_id), "MFG:EPSON;MDL:%s", model + 7);
1193 else if ((ptr = strchr(model, ' ')) != NULL)
1194 {
1195 /*
1196 * Assume the first word is the make...
1197 */
238c3832 1198
f3c17241
MS
1199 memcpy(make_and_model, model, ptr - model);
1200 make_and_model[ptr - model] = '\0';
238c3832 1201
f3c17241
MS
1202 snprintf(device_id, sizeof(device_id), "MFG:%s;MDL:%s",
1203 make_and_model, ptr + 1);
1204 }
1205 }
238c3832 1206
c1420c87
MS
1207 if (device_id[0] &&
1208 !strstr(device_id, "CMD:") &&
1209 !strstr(device_id, "COMMAND SET:") &&
1210 (strstr(pdl, "application/pdf") ||
1211 strstr(pdl, "application/postscript") ||
1212 strstr(pdl, "application/vnd.hp-PCL") ||
1213 strstr(pdl, "image/")))
1214 {
1215 value[0] = '\0';
1216 if (strstr(pdl, "application/pdf"))
1217 strlcat(value, ",PDF", sizeof(value));
1218 if (strstr(pdl, "application/postscript"))
1219 strlcat(value, ",PS", sizeof(value));
1220 if (strstr(pdl, "application/vnd.hp-PCL"))
1221 strlcat(value, ",PCL", sizeof(value));
1222 for (ptr = strstr(pdl, "image/"); ptr; ptr = strstr(ptr, "image/"))
1223 {
1224 char *valptr = value + strlen(value);
1225 /* Pointer into value */
1226
1227 if (valptr < (value + sizeof(value) - 1))
1228 *valptr++ = ',';
1229
1230 ptr += 6;
1231 while (isalnum(*ptr & 255) || *ptr == '-' || *ptr == '.')
1232 {
1233 if (isalnum(*ptr & 255) && valptr < (value + sizeof(value) - 1))
1234 *valptr++ = toupper(*ptr++ & 255);
1235 else
1236 break;
1237 }
1238
1239 *valptr = '\0';
1240 }
1241
1242 ptr = device_id + strlen(device_id);
1243 snprintf(ptr, sizeof(device_id) - (ptr - device_id), "CMD:%s;",
1244 value + 1);
1245 }
1246
f3c17241
MS
1247 if (device_id[0])
1248 device->device_id = strdup(device_id);
1249 else
1250 device->device_id = NULL;
7a14d768 1251
f3c17241
MS
1252 if (device->make_and_model)
1253 free(device->make_and_model);
7a14d768 1254
f3c17241
MS
1255 if (make_and_model[0])
1256 {
1257 strlcat(make_and_model, " ", sizeof(make_and_model));
1258 strlcat(make_and_model, model, sizeof(make_and_model));
238c3832 1259
f3c17241 1260 device->make_and_model = strdup(make_and_model);
7a14d768 1261 }
f3c17241
MS
1262 else
1263 device->make_and_model = strdup(model);
7a14d768 1264}
f3c17241 1265#endif /* HAVE_DNSSD || HAVE_AVAHI */
7a14d768
MS
1266
1267
38e73f87 1268/*
f3c17241 1269 * 'sigterm_handler()' - Handle termination signals.
38e73f87
MS
1270 */
1271
1272static void
1273sigterm_handler(int sig) /* I - Signal number (unused) */
1274{
321d8d57
MS
1275 (void)sig;
1276
e07d4801
MS
1277 if (job_canceled)
1278 exit(CUPS_BACKEND_OK);
1279 else
1280 job_canceled = 1;
38e73f87
MS
1281}
1282
1283
7a14d768
MS
1284/*
1285 * 'unquote()' - Unquote a name string.
1286 */
1287
1288static void
1289unquote(char *dst, /* I - Destination buffer */
1290 const char *src, /* I - Source string */
1291 size_t dstsize) /* I - Size of destination buffer */
1292{
1293 char *dstend = dst + dstsize - 1; /* End of destination buffer */
1294
1295
1296 while (*src && dst < dstend)
1297 {
1298 if (*src == '\\')
1299 {
1300 src ++;
1301 if (isdigit(src[0] & 255) && isdigit(src[1] & 255) &&
1302 isdigit(src[2] & 255))
1303 {
1304 *dst++ = ((((src[0] - '0') * 10) + src[1] - '0') * 10) + src[2] - '0';
1305 src += 3;
1306 }
1307 else
1308 *dst++ = *src++;
1309 }
1310 else
1311 *dst++ = *src ++;
1312 }
1313
1314 *dst = '\0';
1315}
1316
1317
1318/*
215ef638 1319 * End of "$Id: dnssd.c 11971 2014-07-01 14:38:29Z msweet $".
7a14d768 1320 */