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