]> git.ipfire.org Git - thirdparty/cups.git/blame - backend/dnssd.c
Fix build errors on Fedora.
[thirdparty/cups.git] / backend / dnssd.c
CommitLineData
7a14d768
MS
1/*
2 * "$Id$"
3 *
7e86f2f6 4 * DNS-SD discovery backend for CUPS.
7a14d768 5 *
7e86f2f6 6 * Copyright 2008-2014 by Apple Inc.
7a14d768 7 *
7e86f2f6
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 *
7e86f2f6 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 63 *make_and_model, /* Make and model from TXT record */
2c3f5865
MS
64 *device_id, /* 1284 device ID from TXT record */
65 *uuid; /* UUID from TXT record */
7a14d768 66 cups_devtype_t type; /* Device registration type */
1f0275e3
MS
67 int priority, /* Priority associated with type */
68 cups_shared, /* CUPS shared printer? */
7a14d768
MS
69 sent; /* Did we list the device? */
70} cups_device_t;
71
72
38e73f87
MS
73/*
74 * Local globals...
75 */
76
77static int job_canceled = 0;
78 /* Set to 1 on SIGTERM */
f3c17241
MS
79#ifdef HAVE_AVAHI
80static AvahiSimplePoll *simple_poll = NULL;
81 /* Poll information */
82static int got_data = 0; /* Got data from poll? */
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 124static int compare_devices(cups_device_t *a, cups_device_t *b);
7e86f2f6 125static void exec_backend(char **argv) __attribute__((noreturn));
7a14d768
MS
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
333 avahi_service_browser_new(client, AVAHI_IF_UNSPEC,
334 AVAHI_PROTO_UNSPEC,
335 "_fax-ipp._tcp", NULL, 0,
336 browse_callback, devices);
337 avahi_service_browser_new(client, AVAHI_IF_UNSPEC,
338 AVAHI_PROTO_UNSPEC,
339 "_ipp._tcp", NULL, 0,
340 browse_callback, devices);
341 avahi_service_browser_new(client, AVAHI_IF_UNSPEC,
342 AVAHI_PROTO_UNSPEC,
343 "_ipp-tls._tcp", NULL, 0,
344 browse_callback, devices);
345 avahi_service_browser_new(client, AVAHI_IF_UNSPEC,
346 AVAHI_PROTO_UNSPEC,
347 "_ipps._tcp", NULL, 0,
348 browse_callback, devices);
349 avahi_service_browser_new(client, AVAHI_IF_UNSPEC,
350 AVAHI_PROTO_UNSPEC,
351 "_pdl-datastream._tcp",
352 NULL, 0,
353 browse_callback,
354 devices);
355 avahi_service_browser_new(client, AVAHI_IF_UNSPEC,
356 AVAHI_PROTO_UNSPEC,
357 "_printer._tcp", NULL, 0,
358 browse_callback, devices);
359#endif /* HAVE_AVAHI */
7a14d768
MS
360
361 /*
362 * Loop until we are killed...
363 */
364
38e73f87 365 while (!job_canceled)
7a14d768 366 {
f3c17241
MS
367 int announce = 0; /* Announce printers? */
368
369#ifdef HAVE_DNSSD
7a14d768
MS
370 FD_ZERO(&input);
371 FD_SET(fd, &input);
372
d4d03398 373 timeout.tv_sec = 0;
f3c17241 374 timeout.tv_usec = 500000;
7a14d768
MS
375
376 if (select(fd + 1, &input, NULL, NULL, &timeout) < 0)
377 continue;
378
379 if (FD_ISSET(fd, &input))
380 {
381 /*
382 * Process results of our browsing...
383 */
384
385 DNSServiceProcessResult(main_ref);
386 }
387 else
f3c17241
MS
388 announce = 1;
389
390#elif defined(HAVE_AVAHI)
391 got_data = 0;
392
393 if ((error = avahi_simple_poll_iterate(simple_poll, 500)) > 0)
394 {
395 /*
396 * We've been told to exit the loop. Perhaps the connection to
397 * Avahi failed.
398 */
399
400 break;
401 }
402
403 if (!got_data)
404 announce = 1;
405#endif /* HAVE_DNSSD */
406
407/* fprintf(stderr, "DEBUG: announce=%d\n", announce);*/
408
409 if (announce)
7a14d768
MS
410 {
411 /*
412 * Announce any devices we've found...
413 */
414
f3c17241 415#ifdef HAVE_DNSSD
58dc1933 416 DNSServiceErrorType status; /* DNS query status */
f3c17241 417#endif /* HAVE_DNSSD */
1f0275e3 418 cups_device_t *best; /* Best matching device */
7a14d768 419 char device_uri[1024]; /* Device URI */
5eb9da71 420 int count; /* Number of queries */
d4d03398 421 int sent; /* Number of sent */
7a14d768 422
1f0275e3 423 for (device = (cups_device_t *)cupsArrayFirst(devices),
d4d03398 424 best = NULL, count = 0, sent = 0;
7a14d768
MS
425 device;
426 device = (cups_device_t *)cupsArrayNext(devices))
d4d03398
MS
427 {
428 if (device->sent)
429 sent ++;
430
431 if (device->ref)
432 count ++;
433
7a14d768
MS
434 if (!device->ref && !device->sent)
435 {
436 /*
437 * Found the device, now get the TXT record(s) for it...
438 */
439
f3c17241 440 if (count < 50)
5eb9da71 441 {
5eb9da71
MS
442 fprintf(stderr, "DEBUG: Querying \"%s\"...\n", device->fullName);
443
f3c17241
MS
444#ifdef HAVE_DNSSD
445 device->ref = main_ref;
446
58dc1933
MS
447 status = DNSServiceQueryRecord(&(device->ref),
448 kDNSServiceFlagsShareConnection,
449 0, device->fullName,
450 kDNSServiceType_TXT,
451 kDNSServiceClass_IN, query_callback,
f3c17241 452 device);
58dc1933 453 if (status != kDNSServiceErr_NoError)
f3c17241
MS
454 fprintf(stderr,
455 "ERROR: Unable to query \"%s\" for TXT records: %d\n",
456 device->fullName, status);
457 /* Users never see this */
58dc1933 458 else
5eb9da71 459 count ++;
f3c17241
MS
460
461#else
462 if ((device->ref = avahi_record_browser_new(client, AVAHI_IF_UNSPEC,
463 AVAHI_PROTO_UNSPEC,
464 device->fullName,
465 AVAHI_DNS_CLASS_IN,
466 AVAHI_DNS_TYPE_TXT,
467 0,
468 query_callback,
469 device)) == NULL)
470 fprintf(stderr,
471 "ERROR: Unable to query \"%s\" for TXT records: %s\n",
472 device->fullName,
473 avahi_strerror(avahi_client_errno(client)));
474 /* Users never see this */
475 else
476 count ++;
477#endif /* HAVE_AVAHI */
5eb9da71 478 }
7a14d768
MS
479 }
480 else if (!device->sent)
481 {
f3c17241 482#ifdef HAVE_DNSSD
7a14d768
MS
483 /*
484 * Got the TXT records, now report the device...
485 */
486
487 DNSServiceRefDeallocate(device->ref);
f3c17241
MS
488#else
489 avahi_record_browser_free(device->ref);
490#endif /* HAVE_DNSSD */
491
492 device->ref = NULL;
7a14d768 493
1f0275e3
MS
494 if (!best)
495 best = device;
88f9aafc
MS
496 else if (_cups_strcasecmp(best->name, device->name) ||
497 _cups_strcasecmp(best->domain, device->domain))
1f0275e3 498 {
bf3816c7
MS
499 unquote(uriName, best->fullName, sizeof(uriName));
500
2c3f5865
MS
501 if (best->uuid)
502 httpAssembleURIf(HTTP_URI_CODING_ALL, device_uri,
503 sizeof(device_uri), "dnssd", NULL, uriName, 0,
504 best->cups_shared ? "/cups?uuid=%s" : "/?uuid=%s",
505 best->uuid);
506 else
507 httpAssembleURI(HTTP_URI_CODING_ALL, device_uri,
508 sizeof(device_uri), "dnssd", NULL, uriName, 0,
509 best->cups_shared ? "/cups" : "/");
1f0275e3 510
749b1e90 511 cupsBackendReport("network", device_uri, best->make_and_model,
238c3832 512 best->name, best->device_id, NULL);
1f0275e3
MS
513 best->sent = 1;
514 best = device;
d4d03398
MS
515
516 sent ++;
1f0275e3
MS
517 }
518 else if (best->priority > device->priority ||
519 (best->priority == device->priority &&
520 best->type < device->type))
521 {
522 best->sent = 1;
523 best = device;
d4d03398
MS
524
525 sent ++;
1f0275e3
MS
526 }
527 else
d4d03398 528 {
1f0275e3 529 device->sent = 1;
d4d03398
MS
530
531 sent ++;
532 }
1f0275e3 533 }
d4d03398 534 }
7a14d768 535
1f0275e3
MS
536 if (best)
537 {
bf3816c7
MS
538 unquote(uriName, best->fullName, sizeof(uriName));
539
2c3f5865
MS
540 if (best->uuid)
541 httpAssembleURIf(HTTP_URI_CODING_ALL, device_uri,
542 sizeof(device_uri), "dnssd", NULL, uriName, 0,
543 best->cups_shared ? "/cups?uuid=%s" : "/?uuid=%s",
544 best->uuid);
545 else
546 httpAssembleURI(HTTP_URI_CODING_ALL, device_uri,
547 sizeof(device_uri), "dnssd", NULL, uriName, 0,
548 best->cups_shared ? "/cups" : "/");
7a14d768 549
749b1e90 550 cupsBackendReport("network", device_uri, best->make_and_model,
238c3832 551 best->name, best->device_id, NULL);
1f0275e3 552 best->sent = 1;
d4d03398 553 sent ++;
1f0275e3 554 }
d4d03398 555
f3c17241
MS
556 fprintf(stderr, "DEBUG: sent=%d, count=%d\n", sent, count);
557
d4d03398
MS
558 if (sent == cupsArrayCount(devices))
559 break;
7a14d768
MS
560 }
561 }
38e73f87
MS
562
563 return (CUPS_BACKEND_OK);
7a14d768
MS
564}
565
566
f3c17241 567#ifdef HAVE_DNSSD
7a14d768
MS
568/*
569 * 'browse_callback()' - Browse devices.
570 */
571
572static void
573browse_callback(
574 DNSServiceRef sdRef, /* I - Service reference */
575 DNSServiceFlags flags, /* I - Option flags */
576 uint32_t interfaceIndex, /* I - Interface number */
577 DNSServiceErrorType errorCode, /* I - Error, if any */
578 const char *serviceName, /* I - Name of service/device */
579 const char *regtype, /* I - Type of service */
580 const char *replyDomain, /* I - Service domain */
581 void *context) /* I - Devices array */
582{
583 fprintf(stderr, "DEBUG2: browse_callback(sdRef=%p, flags=%x, "
584 "interfaceIndex=%d, errorCode=%d, serviceName=\"%s\", "
585 "regtype=\"%s\", replyDomain=\"%s\", context=%p)\n",
586 sdRef, flags, interfaceIndex, errorCode,
a29fd7dd 587 serviceName, regtype, replyDomain, context);
7a14d768
MS
588
589 /*
590 * Only process "add" data...
591 */
592
593 if (errorCode != kDNSServiceErr_NoError || !(flags & kDNSServiceFlagsAdd))
594 return;
595
596 /*
597 * Get the device...
598 */
599
600 get_device((cups_array_t *)context, serviceName, regtype, replyDomain);
601}
602
603
604/*
605 * 'browse_local_callback()' - Browse local devices.
606 */
607
608static void
609browse_local_callback(
610 DNSServiceRef sdRef, /* I - Service reference */
611 DNSServiceFlags flags, /* I - Option flags */
612 uint32_t interfaceIndex, /* I - Interface number */
613 DNSServiceErrorType errorCode, /* I - Error, if any */
614 const char *serviceName, /* I - Name of service/device */
615 const char *regtype, /* I - Type of service */
616 const char *replyDomain, /* I - Service domain */
617 void *context) /* I - Devices array */
618{
619 cups_device_t *device; /* Device */
620
621
622 fprintf(stderr, "DEBUG2: browse_local_callback(sdRef=%p, flags=%x, "
623 "interfaceIndex=%d, errorCode=%d, serviceName=\"%s\", "
624 "regtype=\"%s\", replyDomain=\"%s\", context=%p)\n",
625 sdRef, flags, interfaceIndex, errorCode,
a29fd7dd 626 serviceName, regtype, replyDomain, context);
7a14d768
MS
627
628 /*
629 * Only process "add" data...
630 */
631
632 if (errorCode != kDNSServiceErr_NoError || !(flags & kDNSServiceFlagsAdd))
633 return;
634
635 /*
636 * Get the device...
637 */
638
639 device = get_device((cups_array_t *)context, serviceName, regtype,
640 replyDomain);
641
642 /*
643 * Hide locally-registered devices...
644 */
645
646 fprintf(stderr, "DEBUG: Hiding local printer \"%s\"...\n",
647 device->fullName);
648 device->sent = 1;
649}
f3c17241
MS
650#endif /* HAVE_DNSSD */
651
652
653#ifdef HAVE_AVAHI
654/*
655 * 'browse_callback()' - Browse devices.
656 */
657
658static void
659browse_callback(
660 AvahiServiceBrowser *browser, /* I - Browser */
661 AvahiIfIndex interface, /* I - Interface index (unused) */
662 AvahiProtocol protocol, /* I - Network protocol (unused) */
663 AvahiBrowserEvent event, /* I - What happened */
664 const char *name, /* I - Service name */
665 const char *type, /* I - Registration type */
666 const char *domain, /* I - Domain */
667 AvahiLookupResultFlags flags, /* I - Flags */
668 void *context) /* I - Devices array */
669{
670 AvahiClient *client = avahi_service_browser_get_client(browser);
671 /* Client information */
672
673
674 (void)interface;
675 (void)protocol;
676 (void)context;
677
678 switch (event)
679 {
680 case AVAHI_BROWSER_FAILURE:
681 fprintf(stderr, "DEBUG: browse_callback: %s\n",
682 avahi_strerror(avahi_client_errno(client)));
683 avahi_simple_poll_quit(simple_poll);
684 break;
685
686 case AVAHI_BROWSER_NEW:
687 /*
688 * This object is new on the network.
689 */
690
691 if (flags & AVAHI_LOOKUP_RESULT_LOCAL)
692 {
693 /*
694 * This comes from the local machine so ignore it.
695 */
696
697 fprintf(stderr, "DEBUG: Ignoring local service %s.\n", name);
698 }
699 else
700 {
701 /*
702 * Create a device entry for it if it doesn't yet exist.
703 */
704
705 get_device((cups_array_t *)context, name, type, domain);
706 }
707 break;
708
709 case AVAHI_BROWSER_REMOVE:
710 case AVAHI_BROWSER_ALL_FOR_NOW:
711 case AVAHI_BROWSER_CACHE_EXHAUSTED:
712 break;
713 }
714}
715
716
717/*
718 * 'client_callback()' - Avahi client callback function.
719 */
720
721static void
722client_callback(
723 AvahiClient *client, /* I - Client information (unused) */
724 AvahiClientState state, /* I - Current state */
725 void *context) /* I - User data (unused) */
726{
727 (void)client;
728 (void)context;
729
730 /*
731 * If the connection drops, quit.
732 */
733
734 if (state == AVAHI_CLIENT_FAILURE)
735 {
736 fputs("DEBUG: Avahi connection failed.\n", stderr);
737 avahi_simple_poll_quit(simple_poll);
738 }
739}
740#endif /* HAVE_AVAHI */
7a14d768
MS
741
742
743/*
744 * 'compare_devices()' - Compare two devices.
745 */
746
747static int /* O - Result of comparison */
748compare_devices(cups_device_t *a, /* I - First device */
749 cups_device_t *b) /* I - Second device */
750{
38e73f87 751 return (strcmp(a->name, b->name));
7a14d768
MS
752}
753
754
755/*
756 * 'exec_backend()' - Execute the backend that corresponds to the
757 * resolved service name.
758 */
759
760static void
761exec_backend(char **argv) /* I - Command-line arguments */
762{
763 const char *resolved_uri, /* Resolved device URI */
764 *cups_serverbin; /* Location of programs */
765 char scheme[1024], /* Scheme from URI */
766 *ptr, /* Pointer into scheme */
767 filename[1024]; /* Backend filename */
768
769
770 /*
771 * Resolve the device URI...
772 */
773
e07d4801
MS
774 job_canceled = -1;
775
0268488e
MS
776 while ((resolved_uri = cupsBackendDeviceURI(argv)) == NULL)
777 {
778 _cupsLangPrintFilter(stderr, "INFO", _("Unable to locate printer."));
779 sleep(10);
780
781 if (getenv("CLASS") != NULL)
782 exit(CUPS_BACKEND_FAILED);
783 }
7a14d768
MS
784
785 /*
786 * Extract the scheme from the URI...
787 */
788
789 strlcpy(scheme, resolved_uri, sizeof(scheme));
790 if ((ptr = strchr(scheme, ':')) != NULL)
791 *ptr = '\0';
792
793 /*
794 * Get the filename of the backend...
795 */
796
797 if ((cups_serverbin = getenv("CUPS_SERVERBIN")) == NULL)
798 cups_serverbin = CUPS_SERVERBIN;
799
800 snprintf(filename, sizeof(filename), "%s/backend/%s", cups_serverbin, scheme);
801
802 /*
38e73f87 803 * Overwrite the device URI and run the new backend...
7a14d768
MS
804 */
805
806 setenv("DEVICE_URI", resolved_uri, 1);
807
808 argv[0] = (char *)resolved_uri;
809
810 fprintf(stderr, "DEBUG: Executing backend \"%s\"...\n", filename);
811
812 execv(filename, argv);
813
814 fprintf(stderr, "ERROR: Unable to execute backend \"%s\": %s\n", filename,
815 strerror(errno));
816 exit(CUPS_BACKEND_STOP);
817}
818
819
f3c17241
MS
820/*
821 * 'device_type()' - Get DNS-SD type enumeration from string.
822 */
823
7e86f2f6
MS
824static cups_devtype_t /* O - Device type */
825device_type(const char *regtype) /* I - Service registration type */
f3c17241
MS
826{
827#ifdef HAVE_AVAHI
828 if (!strcmp(regtype, "_ipp._tcp"))
829 return (CUPS_DEVICE_IPP);
830 else if (!strcmp(regtype, "_ipps._tcp") ||
831 !strcmp(regtype, "_ipp-tls._tcp"))
832 return (CUPS_DEVICE_IPPS);
833 else if (!strcmp(regtype, "_fax-ipp._tcp"))
834 return (CUPS_DEVICE_FAX_IPP);
835 else if (!strcmp(regtype, "_printer._tcp"))
836 return (CUPS_DEVICE_PDL_DATASTREAM);
837#else
838 if (!strcmp(regtype, "_ipp._tcp."))
839 return (CUPS_DEVICE_IPP);
840 else if (!strcmp(regtype, "_ipps._tcp.") ||
841 !strcmp(regtype, "_ipp-tls._tcp."))
842 return (CUPS_DEVICE_IPPS);
843 else if (!strcmp(regtype, "_fax-ipp._tcp."))
844 return (CUPS_DEVICE_FAX_IPP);
845 else if (!strcmp(regtype, "_printer._tcp."))
846 return (CUPS_DEVICE_PRINTER);
847 else if (!strcmp(regtype, "_pdl-datastream._tcp."))
848 return (CUPS_DEVICE_PDL_DATASTREAM);
849#endif /* HAVE_AVAHI */
850
851 return (CUPS_DEVICE_RIOUSBPRINT);
852}
853
854
7a14d768
MS
855/*
856 * 'get_device()' - Create or update a device.
857 */
858
859static cups_device_t * /* O - Device */
860get_device(cups_array_t *devices, /* I - Device array */
861 const char *serviceName, /* I - Name of service/device */
862 const char *regtype, /* I - Type of service */
863 const char *replyDomain) /* I - Service domain */
864{
865 cups_device_t key, /* Search key */
866 *device; /* Device */
58dc1933
MS
867 char fullName[kDNSServiceMaxDomainName];
868 /* Full name for query */
7a14d768
MS
869
870
871 /*
872 * See if this is a new device...
873 */
874
38e73f87 875 key.name = (char *)serviceName;
f3c17241 876 key.type = device_type(regtype);
7a14d768 877
1f0275e3
MS
878 for (device = cupsArrayFind(devices, &key);
879 device;
880 device = cupsArrayNext(devices))
88f9aafc 881 if (_cups_strcasecmp(device->name, key.name))
1f0275e3
MS
882 break;
883 else if (device->type == key.type)
38e73f87 884 {
88f9aafc
MS
885 if (!_cups_strcasecmp(device->domain, "local.") &&
886 _cups_strcasecmp(device->domain, replyDomain))
38e73f87
MS
887 {
888 /*
889 * Update the .local listing to use the "global" domain name instead.
890 * The backend will try local lookups first, then the global domain name.
891 */
892
893 free(device->domain);
894 device->domain = strdup(replyDomain);
895
f3c17241 896#ifdef HAVE_DNSSD
38e73f87
MS
897 DNSServiceConstructFullName(fullName, device->name, regtype,
898 replyDomain);
f3c17241
MS
899#else /* HAVE_AVAHI */
900 avahi_service_name_join(fullName, kDNSServiceMaxDomainName,
a29fd7dd 901 serviceName, regtype, replyDomain);
f3c17241
MS
902#endif /* HAVE_DNSSD */
903
38e73f87
MS
904 free(device->fullName);
905 device->fullName = strdup(fullName);
906 }
907
1f0275e3 908 return (device);
38e73f87 909 }
7a14d768 910
1f0275e3
MS
911 /*
912 * Yes, add the device...
913 */
7a14d768 914
1f0275e3
MS
915 fprintf(stderr, "DEBUG: Found \"%s.%s%s\"...\n", serviceName, regtype,
916 replyDomain);
7a14d768 917
1f0275e3
MS
918 device = calloc(sizeof(cups_device_t), 1);
919 device->name = strdup(serviceName);
920 device->domain = strdup(replyDomain);
921 device->type = key.type;
922 device->priority = 50;
7a14d768 923
1f0275e3 924 cupsArrayAdd(devices, device);
7a14d768
MS
925
926 /*
1f0275e3 927 * Set the "full name" of this service, which is used for queries...
7a14d768
MS
928 */
929
f3c17241 930#ifdef HAVE_DNSSD
58dc1933 931 DNSServiceConstructFullName(fullName, serviceName, regtype, replyDomain);
f3c17241
MS
932#else /* HAVE_AVAHI */
933 avahi_service_name_join(fullName, kDNSServiceMaxDomainName,
934 serviceName, regtype, replyDomain);
935#endif /* HAVE_DNSSD */
936
7a14d768
MS
937 device->fullName = strdup(fullName);
938
939 return (device);
940}
941
942
f3c17241
MS
943#ifdef HAVE_AVAHI
944/*
945 * 'poll_callback()' - Wait for input on the specified file descriptors.
946 *
947 * Note: This function is needed because avahi_simple_poll_iterate is broken
948 * and always uses a timeout of 0 (!) milliseconds.
949 * (Avahi Ticket #364)
950 */
951
952static int /* O - Number of file descriptors matching */
953poll_callback(
954 struct pollfd *pollfds, /* I - File descriptors */
955 unsigned int num_pollfds, /* I - Number of file descriptors */
956 int timeout, /* I - Timeout in milliseconds (unused) */
957 void *context) /* I - User data (unused) */
958{
959 int val; /* Return value */
960
961
962 (void)timeout;
963 (void)context;
964
965 val = poll(pollfds, num_pollfds, 500);
966
967 if (val < 0)
968 fprintf(stderr, "DEBUG: poll_callback: %s\n", strerror(errno));
969 else if (val > 0)
970 got_data = 1;
971
972 return (val);
973}
974#endif /* HAVE_AVAHI */
975
976
977#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
978# ifdef HAVE_DNSSD
7a14d768
MS
979/*
980 * 'query_callback()' - Process query data.
981 */
982
983static void
984query_callback(
985 DNSServiceRef sdRef, /* I - Service reference */
986 DNSServiceFlags flags, /* I - Data flags */
987 uint32_t interfaceIndex, /* I - Interface */
988 DNSServiceErrorType errorCode, /* I - Error, if any */
989 const char *fullName, /* I - Full service name */
990 uint16_t rrtype, /* I - Record type */
991 uint16_t rrclass, /* I - Record class */
992 uint16_t rdlen, /* I - Length of record data */
993 const void *rdata, /* I - Record data */
994 uint32_t ttl, /* I - Time-to-live */
f3c17241 995 void *context) /* I - Device */
7a14d768 996{
f3c17241
MS
997# else
998/*
999 * 'query_callback()' - Process query data.
1000 */
7a14d768 1001
f3c17241
MS
1002static void
1003query_callback(
1004 AvahiRecordBrowser *browser, /* I - Record browser */
1005 AvahiIfIndex interfaceIndex,
1006 /* I - Interface index (unused) */
1007 AvahiProtocol protocol, /* I - Network protocol (unused) */
1008 AvahiBrowserEvent event, /* I - What happened? */
1009 const char *fullName, /* I - Service name */
1010 uint16_t rrclass, /* I - Record class */
1011 uint16_t rrtype, /* I - Record type */
1012 const void *rdata, /* I - TXT record */
1013 size_t rdlen, /* I - Length of TXT record */
1014 AvahiLookupResultFlags flags, /* I - Flags */
1015 void *context) /* I - Device */
1016{
1017 AvahiClient *client = avahi_record_browser_get_client(browser);
1018 /* Client information */
1019# endif /* HAVE_DNSSD */
1020 char *ptr; /* Pointer into string */
1021 cups_device_t *device = (cups_device_t *)context;
1022 /* Device */
1023 const uint8_t *data, /* Pointer into data */
1024 *datanext, /* Next key/value pair */
1025 *dataend; /* End of entire TXT record */
1026 uint8_t datalen; /* Length of current key/value pair */
1027 char key[256], /* Key string */
1028 value[256], /* Value string */
1029 make_and_model[512], /* Manufacturer and model */
1030 model[256], /* Model */
c1420c87 1031 pdl[256], /* PDL */
f3c17241
MS
1032 device_id[2048]; /* 1284 device ID */
1033
1034
1035# ifdef HAVE_DNSSD
7a14d768
MS
1036 fprintf(stderr, "DEBUG2: query_callback(sdRef=%p, flags=%x, "
1037 "interfaceIndex=%d, errorCode=%d, fullName=\"%s\", "
1038 "rrtype=%u, rrclass=%u, rdlen=%u, rdata=%p, ttl=%u, "
1039 "context=%p)\n",
1040 sdRef, flags, interfaceIndex, errorCode,
1041 fullName ? fullName : "(null)", rrtype, rrclass, rdlen, rdata, ttl,
1042 context);
1043
1044 /*
1045 * Only process "add" data...
1046 */
1047
1048 if (errorCode != kDNSServiceErr_NoError || !(flags & kDNSServiceFlagsAdd))
1049 return;
1050
f3c17241
MS
1051# else
1052 fprintf(stderr, "DEBUG2: query_callback(browser=%p, interfaceIndex=%d, "
1053 "protocol=%d, event=%d, fullName=\"%s\", rrclass=%u, "
1054 "rrtype=%u, rdata=%p, rdlen=%u, flags=%x, context=%p)\n",
1055 browser, interfaceIndex, protocol, event,
1056 fullName ? fullName : "(null)", rrclass, rrtype, rdata,
1057 (unsigned)rdlen, flags, context);
1058
7a14d768 1059 /*
f3c17241 1060 * Only process "add" data...
7a14d768
MS
1061 */
1062
f3c17241
MS
1063 if (event != AVAHI_BROWSER_NEW)
1064 {
1065 if (event == AVAHI_BROWSER_FAILURE)
1066 fprintf(stderr, "ERROR: %s\n",
1067 avahi_strerror(avahi_client_errno(client)));
7a14d768 1068
f3c17241
MS
1069 return;
1070 }
1071# endif /* HAVE_DNSSD */
7a14d768 1072
f3c17241
MS
1073 /*
1074 * Pull out the priority and make and model from the TXT
1075 * record and save it...
1076 */
7a14d768 1077
f3c17241
MS
1078 device_id[0] = '\0';
1079 make_and_model[0] = '\0';
c1420c87 1080 pdl[0] = '\0';
7a14d768 1081
5a9febac 1082 strlcpy(model, "Unknown", sizeof(model));
1f0275e3 1083
f3c17241
MS
1084 for (data = rdata, dataend = data + rdlen;
1085 data < dataend;
1086 data = datanext)
7a14d768 1087 {
f3c17241
MS
1088 /*
1089 * Read a key/value pair starting with an 8-bit length. Since the
1090 * length is 8 bits and the size of the key/value buffers is 256, we
1091 * don't need to check for overflow...
1092 */
1093
1094 datalen = *data++;
1095
1096 if (!datalen || (data + datalen) > dataend)
1f0275e3 1097 break;
f3c17241
MS
1098
1099 datanext = data + datalen;
1100
1101 for (ptr = key; data < datanext && *data != '='; data ++)
7e86f2f6 1102 *ptr++ = (char)*data;
f3c17241
MS
1103 *ptr = '\0';
1104
1105 if (data < datanext && *data == '=')
1f0275e3 1106 {
f3c17241 1107 data ++;
7a14d768 1108
f3c17241 1109 if (data < datanext)
07623986 1110 memcpy(value, data, (size_t)(datanext - data));
f3c17241 1111 value[datanext - data] = '\0';
7a14d768 1112
f3c17241
MS
1113 fprintf(stderr, "DEBUG2: query_callback: \"%s=%s\".\n",
1114 key, value);
1115 }
1116 else
1117 {
1118 fprintf(stderr, "DEBUG2: query_callback: \"%s\" with no value.\n",
1119 key);
1120 continue;
1121 }
7a14d768 1122
f3c17241
MS
1123 if (!_cups_strncasecmp(key, "usb_", 4))
1124 {
1125 /*
1126 * Add USB device ID information...
1127 */
7a14d768 1128
f3c17241 1129 ptr = device_id + strlen(device_id);
7e86f2f6 1130 snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%s:%s;", key + 4, value);
f3c17241 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 }
2c3f5865
MS
1179 else if (!_cups_strcasecmp(key, "UUID"))
1180 device->uuid = strdup(value);
f3c17241 1181 }
238c3832 1182
f3c17241
MS
1183 if (device->device_id)
1184 free(device->device_id);
238c3832 1185
f3c17241
MS
1186 if (!device_id[0] && strcmp(model, "Unknown"))
1187 {
1188 if (make_and_model[0])
1189 snprintf(device_id, sizeof(device_id), "MFG:%s;MDL:%s;",
1190 make_and_model, model);
1191 else if (!_cups_strncasecmp(model, "designjet ", 10))
1192 snprintf(device_id, sizeof(device_id), "MFG:HP;MDL:%s", model + 10);
1193 else if (!_cups_strncasecmp(model, "stylus ", 7))
1194 snprintf(device_id, sizeof(device_id), "MFG:EPSON;MDL:%s", model + 7);
1195 else if ((ptr = strchr(model, ' ')) != NULL)
1196 {
1197 /*
1198 * Assume the first word is the make...
1199 */
238c3832 1200
07623986 1201 memcpy(make_and_model, model, (size_t)(ptr - model));
f3c17241 1202 make_and_model[ptr - model] = '\0';
238c3832 1203
f3c17241
MS
1204 snprintf(device_id, sizeof(device_id), "MFG:%s;MDL:%s",
1205 make_and_model, ptr + 1);
1206 }
1207 }
238c3832 1208
c1420c87
MS
1209 if (device_id[0] &&
1210 !strstr(device_id, "CMD:") &&
1211 !strstr(device_id, "COMMAND SET:") &&
1212 (strstr(pdl, "application/pdf") ||
1213 strstr(pdl, "application/postscript") ||
1214 strstr(pdl, "application/vnd.hp-PCL") ||
1215 strstr(pdl, "image/")))
1216 {
1217 value[0] = '\0';
1218 if (strstr(pdl, "application/pdf"))
1219 strlcat(value, ",PDF", sizeof(value));
1220 if (strstr(pdl, "application/postscript"))
1221 strlcat(value, ",PS", sizeof(value));
1222 if (strstr(pdl, "application/vnd.hp-PCL"))
1223 strlcat(value, ",PCL", sizeof(value));
1224 for (ptr = strstr(pdl, "image/"); ptr; ptr = strstr(ptr, "image/"))
1225 {
1226 char *valptr = value + strlen(value);
1227 /* Pointer into value */
1228
1229 if (valptr < (value + sizeof(value) - 1))
1230 *valptr++ = ',';
1231
1232 ptr += 6;
1233 while (isalnum(*ptr & 255) || *ptr == '-' || *ptr == '.')
1234 {
1235 if (isalnum(*ptr & 255) && valptr < (value + sizeof(value) - 1))
7e86f2f6 1236 *valptr++ = (char)toupper(*ptr++ & 255);
c1420c87
MS
1237 else
1238 break;
1239 }
1240
1241 *valptr = '\0';
1242 }
1243
1244 ptr = device_id + strlen(device_id);
7e86f2f6 1245 snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "CMD:%s;", value + 1);
c1420c87
MS
1246 }
1247
f3c17241
MS
1248 if (device_id[0])
1249 device->device_id = strdup(device_id);
1250 else
1251 device->device_id = NULL;
7a14d768 1252
f3c17241
MS
1253 if (device->make_and_model)
1254 free(device->make_and_model);
7a14d768 1255
f3c17241
MS
1256 if (make_and_model[0])
1257 {
1258 strlcat(make_and_model, " ", sizeof(make_and_model));
1259 strlcat(make_and_model, model, sizeof(make_and_model));
238c3832 1260
f3c17241 1261 device->make_and_model = strdup(make_and_model);
7a14d768 1262 }
f3c17241
MS
1263 else
1264 device->make_and_model = strdup(model);
7a14d768 1265}
f3c17241 1266#endif /* HAVE_DNSSD || HAVE_AVAHI */
7a14d768
MS
1267
1268
38e73f87 1269/*
f3c17241 1270 * 'sigterm_handler()' - Handle termination signals.
38e73f87
MS
1271 */
1272
1273static void
1274sigterm_handler(int sig) /* I - Signal number (unused) */
1275{
321d8d57
MS
1276 (void)sig;
1277
e07d4801
MS
1278 if (job_canceled)
1279 exit(CUPS_BACKEND_OK);
1280 else
1281 job_canceled = 1;
38e73f87
MS
1282}
1283
1284
7a14d768
MS
1285/*
1286 * 'unquote()' - Unquote a name string.
1287 */
1288
1289static void
1290unquote(char *dst, /* I - Destination buffer */
1291 const char *src, /* I - Source string */
1292 size_t dstsize) /* I - Size of destination buffer */
1293{
1294 char *dstend = dst + dstsize - 1; /* End of destination buffer */
1295
1296
1297 while (*src && dst < dstend)
1298 {
1299 if (*src == '\\')
1300 {
1301 src ++;
1302 if (isdigit(src[0] & 255) && isdigit(src[1] & 255) &&
1303 isdigit(src[2] & 255))
1304 {
1305 *dst++ = ((((src[0] - '0') * 10) + src[1] - '0') * 10) + src[2] - '0';
1306 src += 3;
1307 }
1308 else
1309 *dst++ = *src++;
1310 }
1311 else
1312 *dst++ = *src ++;
1313 }
1314
1315 *dst = '\0';
1316}
1317
1318
1319/*
1320 * End of "$Id$".
1321 */