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