]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/dest.c
Update HTTP implementation to support field values larger than HTTP_MAX_VALUE
[thirdparty/cups.git] / cups / dest.c
CommitLineData
ef416fc2 1/*
7e86f2f6 2 * User-defined destination (and option) support for CUPS.
ef416fc2 3 *
625bb9de 4 * Copyright 2007-2018 by Apple Inc.
7e86f2f6 5 * Copyright 1997-2007 by Easy Software Products.
ef416fc2 6 *
e3101897 7 * Licensed under Apache License v2.0. See the file "LICENSE" for more information.
ef416fc2 8 */
9
10/*
11 * Include necessary headers...
12 */
13
71e16022 14#include "cups-private.h"
b423cd4c 15#include <sys/stat.h>
ef416fc2 16
fa73b229 17#ifdef HAVE_NOTIFY_H
18# include <notify.h>
19#endif /* HAVE_NOTIFY_H */
20
dcb445bc
MS
21#ifdef HAVE_POLL
22# include <poll.h>
23#endif /* HAVE_POLL */
24
25#ifdef HAVE_DNSSD
26# include <dns_sd.h>
27#endif /* HAVE_DNSSD */
28
a29fd7dd
MS
29#ifdef HAVE_AVAHI
30# include <avahi-client/client.h>
31# include <avahi-client/lookup.h>
32# include <avahi-common/simple-watch.h>
33# include <avahi-common/domain.h>
34# include <avahi-common/error.h>
35# include <avahi-common/malloc.h>
36#define kDNSServiceMaxDomainName AVAHI_DOMAIN_NAME_MAX
37#endif /* HAVE_AVAHI */
38
dcb445bc
MS
39
40/*
41 * Constants...
42 */
43
080811b1 44#ifdef __APPLE__
9688061e
MS
45# if !TARGET_OS_IOS
46# include <SystemConfiguration/SystemConfiguration.h>
47# define _CUPS_LOCATION_DEFAULTS 1
48# endif /* !TARGET_OS_IOS */
f14324a7
MS
49# define kCUPSPrintingPrefs CFSTR("org.cups.PrintingPrefs")
50# define kDefaultPaperIDKey CFSTR("DefaultPaperID")
51# define kLastUsedPrintersKey CFSTR("LastUsedPrinters")
52# define kLocationNetworkKey CFSTR("Network")
53# define kLocationPrinterIDKey CFSTR("PrinterID")
54# define kUseLastPrinter CFSTR("UseLastPrinter")
080811b1
MS
55#endif /* __APPLE__ */
56
3fae3b33 57#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
ced9dda8 58# define _CUPS_DNSSD_GET_DESTS 250 /* Milliseconds for cupsGetDests */
9f573d7b 59# define _CUPS_DNSSD_MAXTIME 50 /* Milliseconds for maximum quantum of time */
b39dd420
MS
60#else
61# define _CUPS_DNSSD_GET_DESTS 0 /* Milliseconds for cupsGetDests */
3fae3b33
MS
62#endif /* HAVE_DNSSD || HAVE_AVAHI */
63
ef416fc2 64
dcb445bc
MS
65/*
66 * Types...
67 */
68
a29fd7dd 69#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
dcb445bc
MS
70typedef enum _cups_dnssd_state_e /* Enumerated device state */
71{
72 _CUPS_DNSSD_NEW,
73 _CUPS_DNSSD_QUERY,
74 _CUPS_DNSSD_PENDING,
75 _CUPS_DNSSD_ACTIVE,
a29fd7dd 76 _CUPS_DNSSD_INCOMPATIBLE,
dcb445bc
MS
77 _CUPS_DNSSD_ERROR
78} _cups_dnssd_state_t;
79
80typedef struct _cups_dnssd_data_s /* Enumeration data */
81{
a29fd7dd 82# ifdef HAVE_DNSSD
dcb445bc 83 DNSServiceRef main_ref; /* Main service reference */
a29fd7dd
MS
84# else /* HAVE_AVAHI */
85 AvahiSimplePoll *simple_poll; /* Polling interface */
86 AvahiClient *client; /* Client information */
87 int got_data; /* Did we get data? */
657c5b5f 88 int browsers; /* How many browsers are running? */
a29fd7dd 89# endif /* HAVE_DNSSD */
dcb445bc
MS
90 cups_dest_cb_t cb; /* Callback */
91 void *user_data; /* User data pointer */
92 cups_ptype_t type, /* Printer type filter */
93 mask; /* Printer type mask */
94 cups_array_t *devices; /* Devices found so far */
c5f5c5a7
MS
95 int num_dests; /* Number of lpoptions destinations */
96 cups_dest_t *dests; /* lpoptions destinations */
97 char def_name[1024], /* Default printer name, if any */
98 *def_instance; /* Default printer instance, if any */
dcb445bc
MS
99} _cups_dnssd_data_t;
100
101typedef struct _cups_dnssd_device_s /* Enumerated device */
102{
103 _cups_dnssd_state_t state; /* State of device listing */
a29fd7dd 104# ifdef HAVE_DNSSD
dcb445bc 105 DNSServiceRef ref; /* Service reference for query */
a29fd7dd
MS
106# else /* HAVE_AVAHI */
107 AvahiRecordBrowser *ref; /* Browser for query */
108# endif /* HAVE_DNSSD */
a2187a63 109 char *fullName, /* Full name */
a2187a63
MS
110 *regtype, /* Registration type */
111 *domain; /* Domain name */
dcb445bc
MS
112 cups_ptype_t type; /* Device registration type */
113 cups_dest_t dest; /* Destination record */
114} _cups_dnssd_device_t;
115
116typedef struct _cups_dnssd_resolve_s /* Data for resolving URI */
117{
118 int *cancel; /* Pointer to "cancel" variable */
119 struct timeval end_time; /* Ending time */
120} _cups_dnssd_resolve_t;
121#endif /* HAVE_DNSSD */
122
7536de1a
MS
123typedef struct _cups_getdata_s
124{
c5f5c5a7
MS
125 int num_dests; /* Number of destinations */
126 cups_dest_t *dests; /* Destinations */
127 char def_name[1024], /* Default printer name, if any */
128 *def_instance; /* Default printer instance, if any */
7536de1a
MS
129} _cups_getdata_t;
130
4a366251
MS
131typedef struct _cups_namedata_s
132{
133 const char *name; /* Named destination */
134 cups_dest_t *dest; /* Destination */
135} _cups_namedata_t;
136
7536de1a 137
ef416fc2 138/*
139 * Local functions...
140 */
141
9688061e 142#if _CUPS_LOCATION_DEFAULTS
9c80ffa2
MS
143static CFArrayRef appleCopyLocations(void);
144static CFStringRef appleCopyNetwork(void);
9688061e
MS
145#endif /* _CUPS_LOCATION_DEFAULTS */
146#ifdef __APPLE__
d649be59 147static char *appleGetPaperSize(char *name, size_t namesize);
9688061e
MS
148#endif /* __APPLE__ */
149#if _CUPS_LOCATION_DEFAULTS
a29fd7dd
MS
150static CFStringRef appleGetPrinter(CFArrayRef locations,
151 CFStringRef network, CFIndex *locindex);
9688061e 152#endif /* _CUPS_LOCATION_DEFAULTS */
9c80ffa2
MS
153static cups_dest_t *cups_add_dest(const char *name, const char *instance,
154 int *num_dests, cups_dest_t **dests);
155#ifdef __BLOCKS__
dcb445bc
MS
156static int cups_block_cb(cups_dest_block_t block, unsigned flags,
157 cups_dest_t *dest);
9c80ffa2
MS
158#endif /* __BLOCKS__ */
159static int cups_compare_dests(cups_dest_t *a, cups_dest_t *b);
a29fd7dd
MS
160#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
161# ifdef HAVE_DNSSD
dcb445bc
MS
162static void cups_dnssd_browse_cb(DNSServiceRef sdRef,
163 DNSServiceFlags flags,
164 uint32_t interfaceIndex,
165 DNSServiceErrorType errorCode,
166 const char *serviceName,
167 const char *regtype,
168 const char *replyDomain,
169 void *context);
a29fd7dd
MS
170# else /* HAVE_AVAHI */
171static void cups_dnssd_browse_cb(AvahiServiceBrowser *browser,
172 AvahiIfIndex interface,
173 AvahiProtocol protocol,
174 AvahiBrowserEvent event,
175 const char *serviceName,
176 const char *regtype,
177 const char *replyDomain,
178 AvahiLookupResultFlags flags,
179 void *context);
180static void cups_dnssd_client_cb(AvahiClient *client,
181 AvahiClientState state,
182 void *context);
183# endif /* HAVE_DNSSD */
dcb445bc
MS
184static int cups_dnssd_compare_devices(_cups_dnssd_device_t *a,
185 _cups_dnssd_device_t *b);
186static void cups_dnssd_free_device(_cups_dnssd_device_t *device,
187 _cups_dnssd_data_t *data);
188static _cups_dnssd_device_t *
189 cups_dnssd_get_device(_cups_dnssd_data_t *data,
190 const char *serviceName,
191 const char *regtype,
192 const char *replyDomain);
a29fd7dd 193# ifdef HAVE_DNSSD
dcb445bc
MS
194static void cups_dnssd_query_cb(DNSServiceRef sdRef,
195 DNSServiceFlags flags,
196 uint32_t interfaceIndex,
197 DNSServiceErrorType errorCode,
198 const char *fullName,
199 uint16_t rrtype, uint16_t rrclass,
200 uint16_t rdlen, const void *rdata,
201 uint32_t ttl, void *context);
a29fd7dd
MS
202# else /* HAVE_AVAHI */
203static int cups_dnssd_poll_cb(struct pollfd *pollfds,
204 unsigned int num_pollfds,
205 int timeout, void *context);
206static void cups_dnssd_query_cb(AvahiRecordBrowser *browser,
207 AvahiIfIndex interface,
208 AvahiProtocol protocol,
209 AvahiBrowserEvent event,
210 const char *name, uint16_t rrclass,
211 uint16_t rrtype, const void *rdata,
212 size_t rdlen,
213 AvahiLookupResultFlags flags,
214 void *context);
215# endif /* HAVE_DNSSD */
dcb445bc
MS
216static const char *cups_dnssd_resolve(cups_dest_t *dest, const char *uri,
217 int msec, int *cancel,
218 cups_dest_cb_t cb, void *user_data);
219static int cups_dnssd_resolve_cb(void *context);
220static void cups_dnssd_unquote(char *dst, const char *src,
221 size_t dstsize);
3fae3b33 222static int cups_elapsed(struct timeval *t);
f34c1c99 223#endif /* HAVE_DNSSD || HAVE_AVAHI */
9554d4e7 224static int cups_enum_dests(http_t *http, unsigned flags, int msec, int *cancel, cups_ptype_t type, cups_ptype_t mask, cups_dest_cb_t cb, void *user_data);
9c80ffa2
MS
225static int cups_find_dest(const char *name, const char *instance,
226 int num_dests, cups_dest_t *dests, int prev,
227 int *rdiff);
7536de1a 228static int cups_get_cb(_cups_getdata_t *data, unsigned flags, cups_dest_t *dest);
9c80ffa2
MS
229static char *cups_get_default(const char *filename, char *namebuf,
230 size_t namesize, const char **instance);
c5f5c5a7 231static int cups_get_dests(const char *filename, const char *match_name, const char *match_inst, int load_all, int user_default_set, int num_dests, cups_dest_t **dests);
9c80ffa2
MS
232static char *cups_make_string(ipp_attribute_t *attr, char *buffer,
233 size_t bufsize);
4a366251 234static int cups_name_cb(_cups_namedata_t *data, unsigned flags, cups_dest_t *dest);
efa72f61 235static void cups_queue_name(char *name, const char *serviceName, size_t namesize);
ef416fc2 236
237
238/*
239 * 'cupsAddDest()' - Add a destination to the list of destinations.
240 *
2abf387c 241 * This function cannot be used to add a new class or printer queue,
242 * it only adds a new container of saved options for the named
243 * destination or instance.
244 *
245 * If the named destination already exists, the destination list is
246 * returned unchanged. Adding a new instance of a destination creates
247 * a copy of that destination's options.
248 *
5a738aea 249 * Use the @link cupsSaveDests@ function to save the updated list of
2abf387c 250 * destinations to the user's lpoptions file.
ef416fc2 251 */
252
253int /* O - New number of destinations */
2abf387c 254cupsAddDest(const char *name, /* I - Destination name */
5a738aea 255 const char *instance, /* I - Instance name or @code NULL@ for none/primary */
ef416fc2 256 int num_dests, /* I - Number of destinations */
257 cups_dest_t **dests) /* IO - Destinations */
258{
259 int i; /* Looping var */
260 cups_dest_t *dest; /* Destination pointer */
bf3816c7 261 cups_dest_t *parent = NULL; /* Parent destination */
426c6a59
MS
262 cups_option_t *doption, /* Current destination option */
263 *poption; /* Current parent option */
ef416fc2 264
265
2abf387c 266 if (!name || !dests)
ef416fc2 267 return (0);
268
426c6a59
MS
269 if (!cupsGetDest(name, instance, num_dests, *dests))
270 {
41681883 271 if (instance && !cupsGetDest(name, NULL, num_dests, *dests))
426c6a59 272 return (num_dests);
ef416fc2 273
5a9febac
MS
274 if ((dest = cups_add_dest(name, instance, &num_dests, dests)) == NULL)
275 return (num_dests);
2abf387c 276
5a662dc0
MS
277 /*
278 * Find the base dest again now the array has been realloc'd.
279 */
280
281 parent = cupsGetDest(name, NULL, num_dests, *dests);
282
426c6a59
MS
283 if (instance && parent && parent->num_options > 0)
284 {
285 /*
286 * Copy options from parent...
287 */
ef416fc2 288
7e86f2f6 289 dest->options = calloc(sizeof(cups_option_t), (size_t)parent->num_options);
2abf387c 290
426c6a59
MS
291 if (dest->options)
292 {
293 dest->num_options = parent->num_options;
ef416fc2 294
426c6a59
MS
295 for (i = dest->num_options, doption = dest->options,
296 poption = parent->options;
297 i > 0;
298 i --, doption ++, poption ++)
299 {
300 doption->name = _cupsStrRetain(poption->name);
301 doption->value = _cupsStrRetain(poption->value);
302 }
303 }
2abf387c 304 }
305 }
306
426c6a59 307 return (num_dests);
ef416fc2 308}
309
310
c8fef167
MS
311#ifdef __APPLE__
312/*
313 * '_cupsAppleCopyDefaultPaperID()' - Get the default paper ID.
314 */
315
316CFStringRef /* O - Default paper ID */
317_cupsAppleCopyDefaultPaperID(void)
318{
f14324a7
MS
319 return (CFPreferencesCopyAppValue(kDefaultPaperIDKey,
320 kCUPSPrintingPrefs));
321}
322
323
324/*
325 * '_cupsAppleCopyDefaultPrinter()' - Get the default printer at this location.
326 */
327
328CFStringRef /* O - Default printer name */
329_cupsAppleCopyDefaultPrinter(void)
330{
9688061e 331# if _CUPS_LOCATION_DEFAULTS
f14324a7
MS
332 CFStringRef network; /* Network location */
333 CFArrayRef locations; /* Location array */
334 CFStringRef locprinter; /* Current printer */
c8fef167
MS
335
336
f14324a7
MS
337 /*
338 * Use location-based defaults only if "use last printer" is selected in the
339 * system preferences...
340 */
c8fef167 341
f14324a7
MS
342 if (!_cupsAppleGetUseLastPrinter())
343 {
344 DEBUG_puts("1_cupsAppleCopyDefaultPrinter: Not using last printer as "
345 "default.");
346 return (NULL);
347 }
c8fef167 348
f14324a7
MS
349 /*
350 * Get the current location...
351 */
352
353 if ((network = appleCopyNetwork()) == NULL)
354 {
355 DEBUG_puts("1_cupsAppleCopyDefaultPrinter: Unable to get current "
356 "network.");
357 return (NULL);
358 }
359
f14324a7
MS
360 /*
361 * Lookup the network in the preferences...
362 */
363
364 if ((locations = appleCopyLocations()) == NULL)
365 {
366 /*
367 * Missing or bad location array, so no location-based default...
368 */
369
370 DEBUG_puts("1_cupsAppleCopyDefaultPrinter: Missing or bad last used "
371 "printer array.");
372
373 CFRelease(network);
374
375 return (NULL);
376 }
88f9aafc 377
f14324a7
MS
378 DEBUG_printf(("1_cupsAppleCopyDefaultPrinter: Got locations, %d entries.",
379 (int)CFArrayGetCount(locations)));
380
381 if ((locprinter = appleGetPrinter(locations, network, NULL)) != NULL)
382 CFRetain(locprinter);
383
384 CFRelease(network);
385 CFRelease(locations);
386
387 return (locprinter);
9688061e
MS
388
389# else
390 return (NULL);
391# endif /* _CUPS_LOCATION_DEFAULTS */
c8fef167
MS
392}
393
394
395/*
396 * '_cupsAppleGetUseLastPrinter()' - Get whether to use the last used printer.
397 */
398
399int /* O - 1 to use last printer, 0 otherwise */
400_cupsAppleGetUseLastPrinter(void)
401{
402 Boolean uselast, /* Use last printer preference value */
403 uselast_set; /* Valid is set? */
404
405
406 if (getenv("CUPS_DISABLE_APPLE_DEFAULT"))
407 return (0);
408
f14324a7
MS
409 uselast = CFPreferencesGetAppBooleanValue(kUseLastPrinter,
410 kCUPSPrintingPrefs,
c8fef167 411 &uselast_set);
c8fef167
MS
412 if (!uselast_set)
413 return (1);
414 else
415 return (uselast);
416}
417
418
419/*
420 * '_cupsAppleSetDefaultPaperID()' - Set the default paper id.
421 */
422
423void
424_cupsAppleSetDefaultPaperID(
425 CFStringRef name) /* I - New paper ID */
426{
f14324a7
MS
427 CFPreferencesSetAppValue(kDefaultPaperIDKey, name, kCUPSPrintingPrefs);
428 CFPreferencesAppSynchronize(kCUPSPrintingPrefs);
684d1464
MS
429
430# ifdef HAVE_NOTIFY_POST
f14324a7 431 notify_post("com.apple.printerPrefsChange");
684d1464 432# endif /* HAVE_NOTIFY_POST */
f14324a7
MS
433}
434
435
436/*
437 * '_cupsAppleSetDefaultPrinter()' - Set the default printer for this location.
438 */
439
440void
441_cupsAppleSetDefaultPrinter(
442 CFStringRef name) /* I - Default printer/class name */
443{
9688061e 444# if _CUPS_LOCATION_DEFAULTS
f14324a7
MS
445 CFStringRef network; /* Current network */
446 CFArrayRef locations; /* Old locations array */
447 CFIndex locindex; /* Index in locations array */
448 CFStringRef locprinter; /* Current printer */
449 CFMutableArrayRef newlocations; /* New locations array */
450 CFMutableDictionaryRef newlocation; /* New location */
451
452
453 /*
454 * Get the current location...
455 */
456
457 if ((network = appleCopyNetwork()) == NULL)
458 {
459 DEBUG_puts("1_cupsAppleSetDefaultPrinter: Unable to get current network...");
460 return;
461 }
462
463 /*
464 * Lookup the network in the preferences...
465 */
466
467 if ((locations = appleCopyLocations()) != NULL)
468 locprinter = appleGetPrinter(locations, network, &locindex);
469 else
470 {
471 locprinter = NULL;
472 locindex = -1;
473 }
474
475 if (!locprinter || CFStringCompare(locprinter, name, 0) != kCFCompareEqualTo)
476 {
477 /*
478 * Need to change the locations array...
479 */
480
481 if (locations)
482 {
483 newlocations = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0,
484 locations);
485
486 if (locprinter)
487 CFArrayRemoveValueAtIndex(newlocations, locindex);
488 }
489 else
490 newlocations = CFArrayCreateMutable(kCFAllocatorDefault, 0,
491 &kCFTypeArrayCallBacks);
492
493 newlocation = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
494 &kCFTypeDictionaryKeyCallBacks,
495 &kCFTypeDictionaryValueCallBacks);
496
497 if (newlocation && newlocations)
498 {
499 /*
500 * Put the new location at the front of the array...
501 */
502
503 CFDictionaryAddValue(newlocation, kLocationNetworkKey, network);
504 CFDictionaryAddValue(newlocation, kLocationPrinterIDKey, name);
505 CFArrayInsertValueAtIndex(newlocations, 0, newlocation);
506
507 /*
508 * Limit the number of locations to 10...
509 */
510
511 while (CFArrayGetCount(newlocations) > 10)
512 CFArrayRemoveValueAtIndex(newlocations, 10);
513
514 /*
515 * Push the changes out...
516 */
517
518 CFPreferencesSetAppValue(kLastUsedPrintersKey, newlocations,
519 kCUPSPrintingPrefs);
520 CFPreferencesAppSynchronize(kCUPSPrintingPrefs);
684d1464
MS
521
522# ifdef HAVE_NOTIFY_POST
f14324a7 523 notify_post("com.apple.printerPrefsChange");
684d1464 524# endif /* HAVE_NOTIFY_POST */
f14324a7
MS
525 }
526
527 if (newlocations)
528 CFRelease(newlocations);
529
530 if (newlocation)
531 CFRelease(newlocation);
532 }
533
534 if (locations)
535 CFRelease(locations);
536
537 CFRelease(network);
9688061e
MS
538
539# else
540 (void)name;
541# endif /* _CUPS_LOCATION_DEFAULTS */
c8fef167
MS
542}
543
544
545/*
546 * '_cupsAppleSetUseLastPrinter()' - Set whether to use the last used printer.
547 */
88f9aafc
MS
548
549void
c8fef167
MS
550_cupsAppleSetUseLastPrinter(
551 int uselast) /* O - 1 to use last printer, 0 otherwise */
552{
88f9aafc 553 CFPreferencesSetAppValue(kUseLastPrinter,
c8fef167 554 uselast ? kCFBooleanTrue : kCFBooleanFalse,
f14324a7
MS
555 kCUPSPrintingPrefs);
556 CFPreferencesAppSynchronize(kCUPSPrintingPrefs);
684d1464
MS
557
558# ifdef HAVE_NOTIFY_POST
f14324a7 559 notify_post("com.apple.printerPrefsChange");
684d1464 560# endif /* HAVE_NOTIFY_POST */
c8fef167
MS
561}
562#endif /* __APPLE__ */
563
564
9c80ffa2 565/*
98d88c8d 566 * 'cupsConnectDest()' - Open a conection to the destination.
9c80ffa2 567 *
98d88c8d
MS
568 * Connect to the destination, returning a new @code http_t@ connection object
569 * and optionally the resource path to use for the destination. These calls
570 * will block until a connection is made, the timeout expires, the integer
571 * pointed to by "cancel" is non-zero, or the callback function (or block)
572 * returns 0. The caller is responsible for calling @link httpClose@ on the
573 * returned connection.
dcb445bc 574 *
90c67342
MS
575 * Starting with CUPS 2.2.4, the caller can pass @code CUPS_DEST_FLAGS_DEVICE@
576 * for the "flags" argument to connect directly to the device associated with
577 * the destination. Otherwise, the connection is made to the CUPS scheduler
578 * associated with the destination.
579 *
8072030b 580 * @since CUPS 1.6/macOS 10.8@
9c80ffa2
MS
581 */
582
98d88c8d 583http_t * /* O - Connection to destination or @code NULL@ */
dcb445bc
MS
584cupsConnectDest(
585 cups_dest_t *dest, /* I - Destination */
586 unsigned flags, /* I - Connection flags */
587 int msec, /* I - Timeout in milliseconds */
588 int *cancel, /* I - Pointer to "cancel" variable */
589 char *resource, /* I - Resource buffer */
590 size_t resourcesize, /* I - Size of resource buffer */
591 cups_dest_cb_t cb, /* I - Callback function */
592 void *user_data) /* I - User data pointer */
9c80ffa2 593{
dcb445bc
MS
594 const char *uri; /* Printer URI */
595 char scheme[32], /* URI scheme */
596 userpass[256], /* Username and password (unused) */
597 hostname[256], /* Hostname */
598 tempresource[1024]; /* Temporary resource buffer */
599 int port; /* Port number */
600 char portstr[16]; /* Port number string */
12f89d24 601 http_encryption_t encryption; /* Encryption to use */
dcb445bc
MS
602 http_addrlist_t *addrlist; /* Address list for server */
603 http_t *http; /* Connection to server */
604
605
807315e6 606 DEBUG_printf(("cupsConnectDest(dest=%p, flags=0x%x, msec=%d, cancel=%p(%d), resource=\"%s\", resourcesize=" CUPS_LLFMT ", cb=%p, user_data=%p)", (void *)dest, flags, msec, (void *)cancel, cancel ? *cancel : -1, resource, CUPS_LLCAST resourcesize, (void *)cb, user_data));
4db7fcee 607
dcb445bc
MS
608 /*
609 * Range check input...
610 */
611
612 if (!dest)
613 {
614 if (resource)
615 *resource = '\0';
616
cb7f98ee 617 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
dcb445bc
MS
618 return (NULL);
619 }
620
621 if (!resource || resourcesize < 1)
622 {
623 resource = tempresource;
624 resourcesize = sizeof(tempresource);
625 }
626
627 /*
628 * Grab the printer URI...
629 */
630
90c67342
MS
631 if (flags & CUPS_DEST_FLAGS_DEVICE)
632 {
c9a0ff62 633 if ((uri = cupsGetOption("device-uri", dest->num_options, dest->options)) != NULL)
90c67342 634 {
90c67342 635#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
c9a0ff62
MS
636 if (strstr(uri, "._tcp"))
637 uri = cups_dnssd_resolve(dest, uri, msec, cancel, cb, user_data);
90c67342 638#endif /* HAVE_DNSSD || HAVE_AVAHI */
90c67342
MS
639 }
640 }
641 else if ((uri = cupsGetOption("printer-uri-supported", dest->num_options, dest->options)) == NULL)
dcb445bc 642 {
c9a0ff62 643 if ((uri = cupsGetOption("device-uri", dest->num_options, dest->options)) != NULL)
46385a1a 644 {
46385a1a 645#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
c9a0ff62
MS
646 if (strstr(uri, "._tcp"))
647 uri = cups_dnssd_resolve(dest, uri, msec, cancel, cb, user_data);
46385a1a
MS
648#endif /* HAVE_DNSSD || HAVE_AVAHI */
649 }
dcb445bc 650
46385a1a
MS
651 if (uri)
652 uri = _cupsCreateDest(dest->name, cupsGetOption("printer-info", dest->num_options, dest->options), NULL, uri, tempresource, sizeof(tempresource));
dcb445bc 653
46385a1a
MS
654 if (uri)
655 {
656 dest->num_options = cupsAddOption("printer-uri-supported", uri, dest->num_options, &dest->options);
657
658 uri = cupsGetOption("printer-uri-supported", dest->num_options, dest->options);
659 }
dcb445bc
MS
660 }
661
46385a1a 662 if (!uri)
dcb445bc 663 {
46385a1a
MS
664 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(ENOENT), 0);
665
666 if (cb)
667 (*cb)(user_data, CUPS_DEST_FLAGS_UNCONNECTED | CUPS_DEST_FLAGS_ERROR, dest);
668
669 return (NULL);
dcb445bc 670 }
dcb445bc
MS
671
672 if (httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, sizeof(scheme),
673 userpass, sizeof(userpass), hostname, sizeof(hostname),
7e86f2f6 674 &port, resource, (int)resourcesize) < HTTP_URI_STATUS_OK)
dcb445bc 675 {
f5f2e19e 676 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad printer-uri."), 1);
dcb445bc
MS
677
678 if (cb)
679 (*cb)(user_data, CUPS_DEST_FLAGS_UNCONNECTED | CUPS_DEST_FLAGS_ERROR,
680 dest);
681
682 return (NULL);
683 }
684
685 /*
686 * Lookup the address for the server...
687 */
688
689 if (cb)
46385a1a 690 (*cb)(user_data, CUPS_DEST_FLAGS_UNCONNECTED | CUPS_DEST_FLAGS_RESOLVING, dest);
dcb445bc
MS
691
692 snprintf(portstr, sizeof(portstr), "%d", port);
693
694 if ((addrlist = httpAddrGetList(hostname, AF_UNSPEC, portstr)) == NULL)
695 {
696 if (cb)
46385a1a 697 (*cb)(user_data, CUPS_DEST_FLAGS_UNCONNECTED | CUPS_DEST_FLAGS_ERROR, dest);
dcb445bc
MS
698
699 return (NULL);
700 }
701
702 if (cancel && *cancel)
703 {
704 httpAddrFreeList(addrlist);
705
706 if (cb)
46385a1a 707 (*cb)(user_data, CUPS_DEST_FLAGS_UNCONNECTED | CUPS_DEST_FLAGS_CANCELED, dest);
dcb445bc
MS
708
709 return (NULL);
710 }
711
712 /*
713 * Create the HTTP object pointing to the server referenced by the URI...
714 */
715
716 if (!strcmp(scheme, "ipps") || port == 443)
cb7f98ee 717 encryption = HTTP_ENCRYPTION_ALWAYS;
dcb445bc 718 else
cb7f98ee 719 encryption = HTTP_ENCRYPTION_IF_REQUESTED;
dcb445bc 720
46385a1a 721 http = httpConnect2(hostname, port, addrlist, AF_UNSPEC, encryption, 1, 0, NULL);
520fe1de 722 httpAddrFreeList(addrlist);
dcb445bc
MS
723
724 /*
725 * Connect if requested...
726 */
727
728 if (flags & CUPS_DEST_FLAGS_UNCONNECTED)
729 {
730 if (cb)
731 (*cb)(user_data, CUPS_DEST_FLAGS_UNCONNECTED, dest);
732 }
733 else
734 {
735 if (cb)
46385a1a 736 (*cb)(user_data, CUPS_DEST_FLAGS_UNCONNECTED | CUPS_DEST_FLAGS_CONNECTING, dest);
dcb445bc
MS
737
738 if (!httpReconnect2(http, msec, cancel) && cb)
739 {
740 if (cancel && *cancel)
46385a1a 741 (*cb)(user_data, CUPS_DEST_FLAGS_UNCONNECTED | CUPS_DEST_FLAGS_CONNECTING, dest);
dcb445bc 742 else
46385a1a 743 (*cb)(user_data, CUPS_DEST_FLAGS_UNCONNECTED | CUPS_DEST_FLAGS_ERROR, dest);
dcb445bc
MS
744 }
745 else if (cb)
746 (*cb)(user_data, CUPS_DEST_FLAGS_NONE, dest);
747 }
748
749 return (http);
750}
751
752
753#ifdef __BLOCKS__
754/*
98d88c8d 755 * 'cupsConnectDestBlock()' - Open a connection to the destination.
dcb445bc 756 *
98d88c8d
MS
757 * Connect to the destination, returning a new @code http_t@ connection object
758 * and optionally the resource path to use for the destination. These calls
759 * will block until a connection is made, the timeout expires, the integer
760 * pointed to by "cancel" is non-zero, or the block returns 0. The caller is
761 * responsible for calling @link httpClose@ on the returned connection.
dcb445bc 762 *
90c67342
MS
763 * Starting with CUPS 2.2.4, the caller can pass @code CUPS_DEST_FLAGS_DEVICE@
764 * for the "flags" argument to connect directly to the device associated with
765 * the destination. Otherwise, the connection is made to the CUPS scheduler
766 * associated with the destination.
767 *
53af7f21 768 * @since CUPS 1.6/macOS 10.8@ @exclude all@
dcb445bc
MS
769 */
770
98d88c8d 771http_t * /* O - Connection to destination or @code NULL@ */
dcb445bc
MS
772cupsConnectDestBlock(
773 cups_dest_t *dest, /* I - Destination */
774 unsigned flags, /* I - Connection flags */
775 int msec, /* I - Timeout in milliseconds */
776 int *cancel, /* I - Pointer to "cancel" variable */
777 char *resource, /* I - Resource buffer */
778 size_t resourcesize, /* I - Size of resource buffer */
779 cups_dest_block_t block) /* I - Callback block */
780{
781 return (cupsConnectDest(dest, flags, msec, cancel, resource, resourcesize,
782 (cups_dest_cb_t)cups_block_cb, (void *)block));
783}
784#endif /* __BLOCKS__ */
785
786
787/*
788 * 'cupsCopyDest()' - Copy a destination.
789 *
790 * Make a copy of the destination to an array of destinations (or just a single
791 * copy) - for use with the cupsEnumDests* functions. The caller is responsible
792 * for calling cupsFreeDests() on the returned object(s).
793 *
8072030b 794 * @since CUPS 1.6/macOS 10.8@
dcb445bc
MS
795 */
796
798d6e29
MS
797int /* O - New number of destinations */
798cupsCopyDest(cups_dest_t *dest, /* I - Destination to copy */
799 int num_dests, /* I - Number of destinations */
800 cups_dest_t **dests) /* IO - Destination array */
dcb445bc
MS
801{
802 int i; /* Looping var */
803 cups_dest_t *new_dest; /* New destination pointer */
804 cups_option_t *new_option, /* Current destination option */
805 *option; /* Current parent option */
806
807
808 /*
809 * Range check input...
810 */
811
812 if (!dest || num_dests < 0 || !dests)
813 return (num_dests);
814
815 /*
816 * See if the destination already exists...
817 */
818
819 if ((new_dest = cupsGetDest(dest->name, dest->instance, num_dests,
820 *dests)) != NULL)
821 {
822 /*
823 * Protect against copying destination to itself...
824 */
825
826 if (new_dest == dest)
827 return (num_dests);
828
829 /*
830 * Otherwise, free the options...
831 */
832
833 cupsFreeOptions(new_dest->num_options, new_dest->options);
834
835 new_dest->num_options = 0;
836 new_dest->options = NULL;
837 }
838 else
839 new_dest = cups_add_dest(dest->name, dest->instance, &num_dests, dests);
840
841 if (new_dest)
842 {
7e86f2f6 843 if ((new_dest->options = calloc(sizeof(cups_option_t), (size_t)dest->num_options)) == NULL)
dcb445bc
MS
844 return (cupsRemoveDest(dest->name, dest->instance, num_dests, dests));
845
846 new_dest->num_options = dest->num_options;
847
848 for (i = dest->num_options, option = dest->options,
849 new_option = new_dest->options;
850 i > 0;
851 i --, option ++, new_option ++)
852 {
853 new_option->name = _cupsStrRetain(option->name);
854 new_option->value = _cupsStrRetain(option->value);
855 }
856 }
857
858 return (num_dests);
9c80ffa2
MS
859}
860
861
7ae00c35
MS
862/*
863 * '_cupsCreateDest()' - Create a local (temporary) queue.
864 */
865
866char * /* O - Printer URI or @code NULL@ on error */
867_cupsCreateDest(const char *name, /* I - Printer name */
868 const char *info, /* I - Printer description of @code NULL@ */
869 const char *device_id, /* I - 1284 Device ID or @code NULL@ */
870 const char *device_uri, /* I - Device URI */
871 char *uri, /* I - Printer URI buffer */
872 size_t urisize) /* I - Size of URI buffer */
873{
874 http_t *http; /* Connection to server */
875 ipp_t *request, /* CUPS-Create-Local-Printer request */
876 *response; /* CUPS-Create-Local-Printer response */
877 ipp_attribute_t *attr; /* printer-uri-supported attribute */
c585706d
MS
878 ipp_pstate_t state = IPP_PSTATE_STOPPED;
879 /* printer-state value */
7ae00c35
MS
880
881
7ae00c35
MS
882 if (!name || !device_uri || !uri || urisize < 32)
883 return (NULL);
884
885 if ((http = httpConnect2(cupsServer(), ippPort(), NULL, AF_UNSPEC, HTTP_ENCRYPTION_IF_REQUESTED, 1, 30000, NULL)) == NULL)
886 return (NULL);
887
888 request = ippNewRequest(IPP_OP_CUPS_CREATE_LOCAL_PRINTER);
889
4db7fcee 890 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, "ipp://localhost/");
7ae00c35
MS
891 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser());
892
893 ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_URI, "device-uri", NULL, device_uri);
894 ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME, "printer-name", NULL, name);
895 if (info)
896 ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-info", NULL, info);
897 if (device_id)
898 ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-device-id", NULL, device_id);
899
900 response = cupsDoRequest(http, request, "/");
901
7ae00c35
MS
902 if ((attr = ippFindAttribute(response, "printer-uri-supported", IPP_TAG_URI)) != NULL)
903 strlcpy(uri, ippGetString(attr, 0, NULL), urisize);
c585706d
MS
904 else
905 {
906 ippDelete(response);
907 httpClose(http);
908 return (NULL);
909 }
910
911 if ((attr = ippFindAttribute(response, "printer-state", IPP_TAG_ENUM)) != NULL)
912 state = (ipp_pstate_t)ippGetInteger(attr, 0);
913
914 while (state == IPP_PSTATE_STOPPED && cupsLastError() == IPP_STATUS_OK)
915 {
916 sleep(1);
917 ippDelete(response);
918
919 request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
920
921 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
922 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser());
923 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", NULL, "printer-state");
924
925 response = cupsDoRequest(http, request, "/");
926
927 if ((attr = ippFindAttribute(response, "printer-state", IPP_TAG_ENUM)) != NULL)
928 state = (ipp_pstate_t)ippGetInteger(attr, 0);
929 }
7ae00c35
MS
930
931 ippDelete(response);
932
c585706d
MS
933 httpClose(http);
934
935 return (uri);
7ae00c35
MS
936}
937
938
9c80ffa2
MS
939/*
940 * 'cupsEnumDests()' - Enumerate available destinations with a callback function.
941 *
f50db552
MS
942 * Destinations are enumerated from one or more sources. The callback function
943 * receives the @code user_data@ pointer and the destination pointer which can
944 * be used as input to the @link cupsCopyDest@ function. The function must
945 * return 1 to continue enumeration or 0 to stop.
946 *
947 * The @code type@ and @code mask@ arguments allow the caller to filter the
948 * destinations that are enumerated. Passing 0 for both will enumerate all
949 * printers. The constant @code CUPS_PRINTER_DISCOVERED@ is used to filter on
950 * destinations that are available but have not yet been added locally.
9c80ffa2 951 *
dcb445bc
MS
952 * Enumeration happens on the current thread and does not return until all
953 * destinations have been enumerated or the callback function returns 0.
9c80ffa2 954 *
500fca27
MS
955 * Note: The callback function will likely receive multiple updates for the same
956 * destinations - it is up to the caller to suppress any duplicate destinations.
957 *
8072030b 958 * @since CUPS 1.6/macOS 10.8@
9c80ffa2
MS
959 */
960
961int /* O - 1 on success, 0 on failure */
dcb445bc 962cupsEnumDests(
a2187a63
MS
963 unsigned flags, /* I - Enumeration flags */
964 int msec, /* I - Timeout in milliseconds, -1 for indefinite */
965 int *cancel, /* I - Pointer to "cancel" variable */
966 cups_ptype_t type, /* I - Printer type bits */
967 cups_ptype_t mask, /* I - Mask for printer type bits */
968 cups_dest_cb_t cb, /* I - Callback function */
969 void *user_data) /* I - User data */
9c80ffa2 970{
9554d4e7
MS
971 return (cups_enum_dests(CUPS_HTTP_DEFAULT, flags, msec, cancel, type, mask, cb, user_data));
972}
dcb445bc 973
7f500d89 974
9554d4e7
MS
975# ifdef __BLOCKS__
976/*
977 * 'cupsEnumDestsBlock()' - Enumerate available destinations with a block.
978 *
979 * Destinations are enumerated from one or more sources. The block receives the
980 * @code user_data@ pointer and the destination pointer which can be used as
981 * input to the @link cupsCopyDest@ function. The block must return 1 to
982 * continue enumeration or 0 to stop.
983 *
984 * The @code type@ and @code mask@ arguments allow the caller to filter the
985 * destinations that are enumerated. Passing 0 for both will enumerate all
986 * printers. The constant @code CUPS_PRINTER_DISCOVERED@ is used to filter on
987 * destinations that are available but have not yet been added locally.
988 *
989 * Enumeration happens on the current thread and does not return until all
990 * destinations have been enumerated or the block returns 0.
991 *
992 * Note: The block will likely receive multiple updates for the same
993 * destinations - it is up to the caller to suppress any duplicate destinations.
994 *
995 * @since CUPS 1.6/macOS 10.8@ @exclude all@
996 */
fb963b8d 997
9554d4e7
MS
998int /* O - 1 on success, 0 on failure */
999cupsEnumDestsBlock(
1000 unsigned flags, /* I - Enumeration flags */
1001 int timeout, /* I - Timeout in milliseconds, 0 for indefinite */
1002 int *cancel, /* I - Pointer to "cancel" variable */
1003 cups_ptype_t type, /* I - Printer type bits */
1004 cups_ptype_t mask, /* I - Mask for printer type bits */
1005 cups_dest_block_t block) /* I - Block */
1006{
1007 return (cupsEnumDests(flags, timeout, cancel, type, mask,
1008 (cups_dest_cb_t)cups_block_cb, (void *)block));
1009}
1010# endif /* __BLOCKS__ */
dcb445bc 1011
f3c17241 1012
9554d4e7
MS
1013/*
1014 * 'cupsFreeDests()' - Free the memory used by the list of destinations.
1015 */
dcb445bc 1016
9554d4e7
MS
1017void
1018cupsFreeDests(int num_dests, /* I - Number of destinations */
1019 cups_dest_t *dests) /* I - Destinations */
1020{
1021 int i; /* Looping var */
1022 cups_dest_t *dest; /* Current destination */
dcb445bc 1023
7f500d89 1024
9554d4e7
MS
1025 if (num_dests == 0 || dests == NULL)
1026 return;
a2187a63 1027
9554d4e7 1028 for (i = num_dests, dest = dests; i > 0; i --, dest ++)
a2187a63 1029 {
9554d4e7
MS
1030 _cupsStrFree(dest->name);
1031 _cupsStrFree(dest->instance);
a2187a63 1032
9554d4e7
MS
1033 cupsFreeOptions(dest->num_options, dest->options);
1034 }
dcb445bc 1035
9554d4e7
MS
1036 free(dests);
1037}
f50db552 1038
a2187a63 1039
9554d4e7
MS
1040/*
1041 * 'cupsGetDest()' - Get the named destination from the list.
1042 *
1043 * Use the @link cupsEnumDests@ or @link cupsGetDests2@ functions to get a
1044 * list of supported destinations for the current user.
1045 */
a2187a63 1046
9554d4e7
MS
1047cups_dest_t * /* O - Destination pointer or @code NULL@ */
1048cupsGetDest(const char *name, /* I - Destination name or @code NULL@ for the default destination */
1049 const char *instance, /* I - Instance name or @code NULL@ */
1050 int num_dests, /* I - Number of destinations */
1051 cups_dest_t *dests) /* I - Destinations */
1052{
1053 int diff, /* Result of comparison */
1054 match; /* Matching index */
a2187a63 1055
f50db552 1056
9554d4e7
MS
1057 if (num_dests <= 0 || !dests)
1058 return (NULL);
1059
1060 if (!name)
1061 {
1062 /*
1063 * NULL name for default printer.
1064 */
1065
1066 while (num_dests > 0)
f50db552 1067 {
9554d4e7
MS
1068 if (dests->is_default)
1069 return (dests);
a2187a63 1070
9554d4e7
MS
1071 num_dests --;
1072 dests ++;
1073 }
1074 }
1075 else
1076 {
1077 /*
1078 * Lookup name and optionally the instance...
1079 */
dcb445bc 1080
9554d4e7 1081 match = cups_find_dest(name, instance, num_dests, dests, -1, &diff);
f50db552 1082
9554d4e7
MS
1083 if (!diff)
1084 return (dests + match);
1085 }
f50db552 1086
9554d4e7
MS
1087 return (NULL);
1088}
f50db552 1089
f50db552 1090
9554d4e7
MS
1091/*
1092 * '_cupsGetDestResource()' - Get the resource path and URI for a destination.
1093 */
657c5b5f 1094
9554d4e7
MS
1095const char * /* O - Printer URI */
1096_cupsGetDestResource(
1097 cups_dest_t *dest, /* I - Destination */
1098 char *resource, /* I - Resource buffer */
1099 size_t resourcesize) /* I - Size of resource buffer */
1100{
1101 const char *uri; /* Printer URI */
1102 char scheme[32], /* URI scheme */
1103 userpass[256], /* Username and password (unused) */
1104 hostname[256]; /* Hostname */
1105 int port; /* Port number */
f50db552 1106
9554d4e7
MS
1107
1108 DEBUG_printf(("_cupsGetDestResource(dest=%p(%s), resource=%p, resourcesize=%d)", (void *)dest, dest->name, (void *)resource, (int)resourcesize));
dcb445bc 1109
f50db552 1110 /*
9554d4e7 1111 * Range check input...
f50db552
MS
1112 */
1113
9554d4e7
MS
1114 if (!dest || !resource || resourcesize < 1)
1115 {
1116 if (resource)
1117 *resource = '\0';
1118
1119 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
1120 return (NULL);
1121 }
f50db552 1122
dcb445bc 1123 /*
9554d4e7 1124 * Grab the printer URI...
dcb445bc
MS
1125 */
1126
9554d4e7 1127 if ((uri = cupsGetOption("printer-uri-supported", dest->num_options, dest->options)) == NULL)
fb963b8d 1128 {
9554d4e7
MS
1129 if ((uri = cupsGetOption("device-uri", dest->num_options, dest->options)) != NULL)
1130 {
1131#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
1132 if (strstr(uri, "._tcp"))
1133 uri = cups_dnssd_resolve(dest, uri, 5000, NULL, NULL, NULL);
1134#endif /* HAVE_DNSSD || HAVE_AVAHI */
1135 }
dcb445bc 1136
9554d4e7
MS
1137 if (uri)
1138 {
1139 DEBUG_printf(("1_cupsGetDestResource: Resolved printer-uri-supported=\"%s\"", uri));
dcb445bc 1140
9554d4e7
MS
1141 uri = _cupsCreateDest(dest->name, cupsGetOption("printer-info", dest->num_options, dest->options), NULL, uri, resource, resourcesize);
1142 }
dcb445bc 1143
9554d4e7
MS
1144 if (uri)
1145 {
1146 DEBUG_printf(("1_cupsGetDestResource: Local printer-uri-supported=\"%s\"", uri));
dcb445bc 1147
9554d4e7 1148 dest->num_options = cupsAddOption("printer-uri-supported", uri, dest->num_options, &dest->options);
dcb445bc 1149
9554d4e7
MS
1150 uri = cupsGetOption("printer-uri-supported", dest->num_options, dest->options);
1151 }
1152 else
1153 {
1154 DEBUG_puts("1_cupsGetDestResource: No printer-uri-supported found.");
a29fd7dd 1155
9554d4e7
MS
1156 if (resource)
1157 *resource = '\0';
a29fd7dd 1158
9554d4e7 1159 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(ENOENT), 0);
a29fd7dd 1160
9554d4e7
MS
1161 return (NULL);
1162 }
a29fd7dd 1163 }
9554d4e7 1164 else
ced9dda8 1165 {
9554d4e7 1166 DEBUG_printf(("1_cupsGetDestResource: printer-uri-supported=\"%s\"", uri));
3fae3b33 1167
9554d4e7
MS
1168 if (httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, sizeof(scheme),
1169 userpass, sizeof(userpass), hostname, sizeof(hostname),
1170 &port, resource, (int)resourcesize) < HTTP_URI_STATUS_OK)
1171 {
1172 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad printer-uri."), 1);
ced9dda8 1173
9554d4e7
MS
1174 return (NULL);
1175 }
ced9dda8 1176 }
dcb445bc 1177
9554d4e7 1178 DEBUG_printf(("1_cupsGetDestResource: resource=\"%s\"", resource));
dcb445bc 1179
9554d4e7
MS
1180 return (uri);
1181}
dcb445bc 1182
3fae3b33 1183
9554d4e7
MS
1184/*
1185 * 'cupsGetDestWithURI()' - Get a destination associated with a URI.
1186 *
1187 * "name" is the desired name for the printer. If @code NULL@, a name will be
1188 * created using the URI.
1189 *
1190 * "uri" is the "ipp" or "ipps" URI for the printer.
1191 *
1192 * @since CUPS 2.0/macOS 10.10@
1193 */
3fae3b33 1194
9554d4e7
MS
1195cups_dest_t * /* O - Destination or @code NULL@ */
1196cupsGetDestWithURI(const char *name, /* I - Desired printer name or @code NULL@ */
1197 const char *uri) /* I - URI for the printer */
1198{
1199 cups_dest_t *dest; /* New destination */
1200 char temp[1024], /* Temporary string */
1201 scheme[256], /* Scheme from URI */
1202 userpass[256], /* Username:password from URI */
1203 hostname[256], /* Hostname from URI */
1204 resource[1024], /* Resource path from URI */
1205 *ptr; /* Pointer into string */
1206 const char *info; /* printer-info string */
1207 int port; /* Port number from URI */
dcb445bc 1208
dcb445bc 1209
9554d4e7
MS
1210 /*
1211 * Range check input...
1212 */
dcb445bc 1213
9554d4e7
MS
1214 if (!uri)
1215 {
1216 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
1217 return (NULL);
1218 }
dcb445bc 1219
9554d4e7
MS
1220 if (httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, sizeof(scheme), userpass, sizeof(userpass), hostname, sizeof(hostname), &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK ||
1221 (strncmp(uri, "ipp://", 6) && strncmp(uri, "ipps://", 7)))
1222 {
1223 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad printer-uri."), 1);
dcb445bc 1224
9554d4e7
MS
1225 return (NULL);
1226 }
dcb445bc 1227
9554d4e7
MS
1228 if (name)
1229 {
1230 info = name;
1231 }
1232 else
1233 {
1234 /*
1235 * Create the name from the URI...
1236 */
a29fd7dd 1237
9554d4e7 1238 if (strstr(hostname, "._tcp"))
a29fd7dd
MS
1239 {
1240 /*
9554d4e7 1241 * Use the service instance name...
a29fd7dd
MS
1242 */
1243
9554d4e7
MS
1244 if ((ptr = strstr(hostname, "._")) != NULL)
1245 *ptr = '\0';
a2187a63 1246
9554d4e7
MS
1247 cups_queue_name(temp, hostname, sizeof(temp));
1248 name = temp;
1249 info = hostname;
1250 }
1251 else if (!strncmp(resource, "/classes/", 9))
dcb445bc 1252 {
9554d4e7
MS
1253 snprintf(temp, sizeof(temp), "%s @ %s", resource + 9, hostname);
1254 name = resource + 9;
1255 info = temp;
1256 }
1257 else if (!strncmp(resource, "/printers/", 10))
1258 {
1259 snprintf(temp, sizeof(temp), "%s @ %s", resource + 10, hostname);
1260 name = resource + 10;
1261 info = temp;
1262 }
1263 else
1264 {
1265 name = hostname;
1266 info = hostname;
dcb445bc
MS
1267 }
1268 }
7f500d89
MS
1269
1270 /*
9554d4e7 1271 * Create the destination...
7f500d89
MS
1272 */
1273
9554d4e7
MS
1274 if ((dest = calloc(1, sizeof(cups_dest_t))) == NULL)
1275 {
1276 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
1277 return (NULL);
1278 }
de4966cb 1279
9554d4e7
MS
1280 dest->name = _cupsStrAlloc(name);
1281 dest->num_options = cupsAddOption("device-uri", uri, dest->num_options, &(dest->options));
1282 dest->num_options = cupsAddOption("printer-info", info, dest->num_options, &(dest->options));
dcb445bc 1283
9554d4e7
MS
1284 return (dest);
1285}
dcb445bc 1286
a29fd7dd 1287
9c80ffa2 1288/*
9554d4e7 1289 * '_cupsGetDests()' - Get destinations from a server.
9c80ffa2 1290 *
9554d4e7
MS
1291 * "op" is IPP_OP_CUPS_GET_PRINTERS to get a full list, IPP_OP_CUPS_GET_DEFAULT
1292 * to get the system-wide default printer, or IPP_OP_GET_PRINTER_ATTRIBUTES for
1293 * a known printer.
9c80ffa2 1294 *
9554d4e7
MS
1295 * "name" is the name of an existing printer and is only used when "op" is
1296 * IPP_OP_GET_PRINTER_ATTRIBUTES.
f50db552 1297 *
9554d4e7 1298 * "dest" is initialized to point to the array of destinations.
9c80ffa2 1299 *
9554d4e7
MS
1300 * 0 is returned if there are no printers, no default printer, or the named
1301 * printer does not exist, respectively.
500fca27 1302 *
9554d4e7
MS
1303 * Free the memory used by the destination array using the @link cupsFreeDests@
1304 * function.
1305 *
1306 * Note: On macOS this function also gets the default paper from the system
1307 * preferences (~/L/P/org.cups.PrintingPrefs.plist) and includes it in the
1308 * options array for each destination that supports it.
ef416fc2 1309 */
1310
9554d4e7
MS
1311int /* O - Number of destinations */
1312_cupsGetDests(http_t *http, /* I - Connection to server or
1313 * @code CUPS_HTTP_DEFAULT@ */
1314 ipp_op_t op, /* I - IPP operation */
1315 const char *name, /* I - Name of destination */
1316 cups_dest_t **dests, /* IO - Destinations */
1317 cups_ptype_t type, /* I - Printer type bits */
1318 cups_ptype_t mask) /* I - Printer type mask */
ef416fc2 1319{
9554d4e7 1320 int num_dests = 0; /* Number of destinations */
ef416fc2 1321 cups_dest_t *dest; /* Current destination */
9554d4e7
MS
1322 ipp_t *request, /* IPP Request */
1323 *response; /* IPP Response */
1324 ipp_attribute_t *attr; /* Current attribute */
1325 const char *printer_name; /* printer-name attribute */
1326 char uri[1024]; /* printer-uri value */
1327 int num_options; /* Number of options */
1328 cups_option_t *options; /* Options */
1329#ifdef __APPLE__
1330 char media_default[41]; /* Default paper size */
1331#endif /* __APPLE__ */
1332 char optname[1024], /* Option name */
1333 value[2048], /* Option value */
1334 *ptr; /* Pointer into name/value */
1335 static const char * const pattrs[] = /* Attributes we're interested in */
1336 {
1337 "auth-info-required",
1338 "device-uri",
1339 "job-sheets-default",
1340 "marker-change-time",
1341 "marker-colors",
1342 "marker-high-levels",
1343 "marker-levels",
1344 "marker-low-levels",
1345 "marker-message",
1346 "marker-names",
1347 "marker-types",
1348#ifdef __APPLE__
1349 "media-supported",
1350#endif /* __APPLE__ */
1351 "printer-commands",
1352 "printer-defaults",
1353 "printer-info",
1354 "printer-is-accepting-jobs",
1355 "printer-is-shared",
1356 "printer-is-temporary",
1357 "printer-location",
1358 "printer-make-and-model",
1359 "printer-mandatory-job-attributes",
1360 "printer-name",
1361 "printer-state",
1362 "printer-state-change-time",
1363 "printer-state-reasons",
1364 "printer-type",
1365 "printer-uri-supported"
1366 };
ef416fc2 1367
1368
9554d4e7 1369 DEBUG_printf(("_cupsGetDests(http=%p, op=%x(%s), name=\"%s\", dests=%p, type=%x, mask=%x)", (void *)http, op, ippOpString(op), name, (void *)dests, type, mask));
ef416fc2 1370
9554d4e7
MS
1371#ifdef __APPLE__
1372 /*
1373 * Get the default paper size...
1374 */
ef416fc2 1375
9554d4e7
MS
1376 appleGetPaperSize(media_default, sizeof(media_default));
1377 DEBUG_printf(("1_cupsGetDests: Default media is '%s'.", media_default));
1378#endif /* __APPLE__ */
ef416fc2 1379
9554d4e7
MS
1380 /*
1381 * Build a IPP_OP_CUPS_GET_PRINTERS or IPP_OP_GET_PRINTER_ATTRIBUTES request, which
1382 * require the following attributes:
1383 *
1384 * attributes-charset
1385 * attributes-natural-language
1386 * requesting-user-name
1387 * printer-uri [for IPP_OP_GET_PRINTER_ATTRIBUTES]
1388 */
ef416fc2 1389
9554d4e7 1390 request = ippNewRequest(op);
ef416fc2 1391
9554d4e7
MS
1392 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1393 "requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]),
1394 NULL, pattrs);
ef416fc2 1395
9554d4e7
MS
1396 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1397 "requesting-user-name", NULL, cupsUser());
ef416fc2 1398
9554d4e7 1399 if (name && op != IPP_OP_CUPS_GET_DEFAULT)
ef416fc2 1400 {
9554d4e7
MS
1401 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
1402 "localhost", ippPort(), "/printers/%s", name);
1403 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
1404 uri);
ef416fc2 1405 }
9554d4e7 1406 else if (mask)
ef416fc2 1407 {
9554d4e7
MS
1408 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM, "printer-type", (int)type);
1409 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM, "printer-type-mask", (int)mask);
ef416fc2 1410 }
1411
dcb445bc 1412 /*
9554d4e7 1413 * Do the request and get back a response...
dcb445bc
MS
1414 */
1415
9554d4e7 1416 if ((response = cupsDoRequest(http, request, "/")) != NULL)
dcb445bc 1417 {
9554d4e7
MS
1418 for (attr = response->attrs; attr != NULL; attr = attr->next)
1419 {
1420 /*
1421 * Skip leading attributes until we hit a printer...
1422 */
dcb445bc 1423
9554d4e7
MS
1424 while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
1425 attr = attr->next;
dcb445bc 1426
9554d4e7
MS
1427 if (attr == NULL)
1428 break;
dcb445bc 1429
9554d4e7
MS
1430 /*
1431 * Pull the needed attributes from this printer...
1432 */
7536de1a 1433
9554d4e7
MS
1434 printer_name = NULL;
1435 num_options = 0;
1436 options = NULL;
dcb445bc 1437
9554d4e7
MS
1438 for (; attr && attr->group_tag == IPP_TAG_PRINTER; attr = attr->next)
1439 {
1440 if (attr->value_tag != IPP_TAG_INTEGER &&
1441 attr->value_tag != IPP_TAG_ENUM &&
1442 attr->value_tag != IPP_TAG_BOOLEAN &&
1443 attr->value_tag != IPP_TAG_TEXT &&
1444 attr->value_tag != IPP_TAG_TEXTLANG &&
1445 attr->value_tag != IPP_TAG_NAME &&
1446 attr->value_tag != IPP_TAG_NAMELANG &&
1447 attr->value_tag != IPP_TAG_KEYWORD &&
1448 attr->value_tag != IPP_TAG_RANGE &&
1449 attr->value_tag != IPP_TAG_URI)
1450 continue;
dcb445bc 1451
9554d4e7
MS
1452 if (!strcmp(attr->name, "auth-info-required") ||
1453 !strcmp(attr->name, "device-uri") ||
1454 !strcmp(attr->name, "marker-change-time") ||
1455 !strcmp(attr->name, "marker-colors") ||
1456 !strcmp(attr->name, "marker-high-levels") ||
1457 !strcmp(attr->name, "marker-levels") ||
1458 !strcmp(attr->name, "marker-low-levels") ||
1459 !strcmp(attr->name, "marker-message") ||
1460 !strcmp(attr->name, "marker-names") ||
1461 !strcmp(attr->name, "marker-types") ||
1462 !strcmp(attr->name, "printer-commands") ||
1463 !strcmp(attr->name, "printer-info") ||
1464 !strcmp(attr->name, "printer-is-shared") ||
1465 !strcmp(attr->name, "printer-is-temporary") ||
1466 !strcmp(attr->name, "printer-make-and-model") ||
1467 !strcmp(attr->name, "printer-mandatory-job-attributes") ||
1468 !strcmp(attr->name, "printer-state") ||
1469 !strcmp(attr->name, "printer-state-change-time") ||
1470 !strcmp(attr->name, "printer-type") ||
1471 !strcmp(attr->name, "printer-is-accepting-jobs") ||
1472 !strcmp(attr->name, "printer-location") ||
1473 !strcmp(attr->name, "printer-state-reasons") ||
1474 !strcmp(attr->name, "printer-uri-supported"))
1475 {
1476 /*
1477 * Add a printer description attribute...
1478 */
dcb445bc 1479
9554d4e7
MS
1480 num_options = cupsAddOption(attr->name,
1481 cups_make_string(attr, value,
1482 sizeof(value)),
1483 num_options, &options);
1484 }
1485#ifdef __APPLE__
1486 else if (!strcmp(attr->name, "media-supported") && media_default[0])
1487 {
1488 /*
1489 * See if we can set a default media size...
1490 */
4a366251 1491
9554d4e7 1492 int i; /* Looping var */
4a366251 1493
9554d4e7
MS
1494 for (i = 0; i < attr->num_values; i ++)
1495 if (!_cups_strcasecmp(media_default, attr->values[i].string.text))
1496 {
1497 DEBUG_printf(("1_cupsGetDests: Setting media to '%s'.", media_default));
1498 num_options = cupsAddOption("media", media_default, num_options, &options);
1499 break;
1500 }
1501 }
1502#endif /* __APPLE__ */
1503 else if (!strcmp(attr->name, "printer-name") &&
1504 attr->value_tag == IPP_TAG_NAME)
1505 printer_name = attr->values[0].string.text;
1506 else if (strncmp(attr->name, "notify-", 7) &&
1507 strncmp(attr->name, "print-quality-", 14) &&
1508 (attr->value_tag == IPP_TAG_BOOLEAN ||
1509 attr->value_tag == IPP_TAG_ENUM ||
1510 attr->value_tag == IPP_TAG_INTEGER ||
1511 attr->value_tag == IPP_TAG_KEYWORD ||
1512 attr->value_tag == IPP_TAG_NAME ||
1513 attr->value_tag == IPP_TAG_RANGE) &&
1514 (ptr = strstr(attr->name, "-default")) != NULL)
1515 {
1516 /*
1517 * Add a default option...
1518 */
4a366251 1519
9554d4e7
MS
1520 strlcpy(optname, attr->name, sizeof(optname));
1521 optname[ptr - attr->name] = '\0';
7536de1a 1522
9554d4e7
MS
1523 if (_cups_strcasecmp(optname, "media") || !cupsGetOption("media", num_options, options))
1524 num_options = cupsAddOption(optname, cups_make_string(attr, value, sizeof(value)), num_options, &options);
1525 }
1526 }
dcb445bc 1527
9554d4e7
MS
1528 /*
1529 * See if we have everything needed...
1530 */
4a366251 1531
9554d4e7
MS
1532 if (!printer_name)
1533 {
1534 cupsFreeOptions(num_options, options);
1535
1536 if (attr == NULL)
1537 break;
1538 else
1539 continue;
1540 }
1541
1542 if ((dest = cups_add_dest(printer_name, NULL, &num_dests, dests)) != NULL)
1543 {
1544 dest->num_options = num_options;
1545 dest->options = options;
1546 }
1547 else
1548 cupsFreeOptions(num_options, options);
1549
1550 if (attr == NULL)
1551 break;
4a366251 1552 }
9554d4e7
MS
1553
1554 ippDelete(response);
dcb445bc
MS
1555 }
1556
9554d4e7
MS
1557 /*
1558 * Return the count...
1559 */
7536de1a 1560
9554d4e7 1561 return (num_dests);
dcb445bc
MS
1562}
1563
1564
104fd4ae 1565/*
9554d4e7 1566 * 'cupsGetDests()' - Get the list of destinations from the default server.
104fd4ae 1567 *
9554d4e7
MS
1568 * Starting with CUPS 1.2, the returned list of destinations include the
1569 * "printer-info", "printer-is-accepting-jobs", "printer-is-shared",
1570 * "printer-make-and-model", "printer-state", "printer-state-change-time",
1571 * "printer-state-reasons", "printer-type", and "printer-uri-supported"
1572 * attributes as options.
104fd4ae 1573 *
9554d4e7
MS
1574 * CUPS 1.4 adds the "marker-change-time", "marker-colors",
1575 * "marker-high-levels", "marker-levels", "marker-low-levels", "marker-message",
1576 * "marker-names", "marker-types", and "printer-commands" attributes as options.
104fd4ae 1577 *
9554d4e7
MS
1578 * CUPS 2.2 adds accessible IPP printers to the list of destinations that can
1579 * be used. The "printer-uri-supported" option will be present for those IPP
1580 * printers that have been recently used.
1581 *
1582 * Use the @link cupsFreeDests@ function to free the destination list and
1583 * the @link cupsGetDest@ function to find a particular destination.
1584 *
1585 * @exclude all@
104fd4ae
MS
1586 */
1587
9554d4e7
MS
1588int /* O - Number of destinations */
1589cupsGetDests(cups_dest_t **dests) /* O - Destinations */
104fd4ae 1590{
9554d4e7
MS
1591 return (cupsGetDests2(CUPS_HTTP_DEFAULT, dests));
1592}
104fd4ae
MS
1593
1594
9554d4e7
MS
1595/*
1596 * 'cupsGetDests2()' - Get the list of destinations from the specified server.
1597 *
1598 * Starting with CUPS 1.2, the returned list of destinations include the
1599 * "printer-info", "printer-is-accepting-jobs", "printer-is-shared",
1600 * "printer-make-and-model", "printer-state", "printer-state-change-time",
1601 * "printer-state-reasons", "printer-type", and "printer-uri-supported"
1602 * attributes as options.
1603 *
1604 * CUPS 1.4 adds the "marker-change-time", "marker-colors",
1605 * "marker-high-levels", "marker-levels", "marker-low-levels", "marker-message",
1606 * "marker-names", "marker-types", and "printer-commands" attributes as options.
1607 *
1608 * CUPS 2.2 adds accessible IPP printers to the list of destinations that can
1609 * be used. The "printer-uri-supported" option will be present for those IPP
1610 * printers that have been recently used.
1611 *
1612 * Use the @link cupsFreeDests@ function to free the destination list and
1613 * the @link cupsGetDest@ function to find a particular destination.
1614 *
1615 * @since CUPS 1.1.21/macOS 10.4@
1616 */
1617
1618int /* O - Number of destinations */
1619cupsGetDests2(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
1620 cups_dest_t **dests) /* O - Destinations */
1621{
1622 _cups_getdata_t data; /* Enumeration data */
9554d4e7
MS
1623
1624
1625 DEBUG_printf(("cupsGetDests2(http=%p, dests=%p)", (void *)http, (void *)dests));
1626
1627/*
1628 * Range check the input...
104fd4ae
MS
1629 */
1630
9554d4e7 1631 if (!dests)
104fd4ae 1632 {
9554d4e7
MS
1633 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad NULL dests pointer"), 1);
1634 DEBUG_puts("1cupsGetDests2: NULL dests pointer, returning 0.");
1635 return (0);
104fd4ae
MS
1636 }
1637
6b33281c
MS
1638 /*
1639 * Connect to the server as needed...
1640 */
1641
1642 if (!http)
1643 {
1644 if ((http = _cupsConnect()) == NULL)
1645 {
1646 *dests = NULL;
1647
1648 return (0);
1649 }
1650 }
1651
9554d4e7
MS
1652 /*
1653 * Grab the printers and classes...
1654 */
104fd4ae 1655
9554d4e7
MS
1656 data.num_dests = 0;
1657 data.dests = NULL;
104fd4ae 1658
6b33281c
MS
1659 if (!httpAddrLocalhost(httpGetAddress(http)))
1660 {
1661 /*
1662 * When talking to a remote cupsd, just enumerate printers on the remote
1663 * cupsd.
1664 */
1665
1666 cups_enum_dests(http, 0, _CUPS_DNSSD_GET_DESTS, NULL, 0, CUPS_PRINTER_DISCOVERED, (cups_dest_cb_t)cups_get_cb, &data);
1667 }
1668 else
1669 {
1670 /*
1671 * When talking to a local cupsd, enumerate both local printers and ones we
1672 * can find on the network...
1673 */
1674
1675 cups_enum_dests(http, 0, _CUPS_DNSSD_GET_DESTS, NULL, 0, 0, (cups_dest_cb_t)cups_get_cb, &data);
1676 }
9554d4e7 1677
104fd4ae 1678 /*
9554d4e7 1679 * Return the number of destinations...
104fd4ae
MS
1680 */
1681
9554d4e7 1682 *dests = data.dests;
104fd4ae 1683
9554d4e7
MS
1684 if (data.num_dests > 0)
1685 _cupsSetError(IPP_STATUS_OK, NULL, 0);
104fd4ae 1686
9554d4e7
MS
1687 DEBUG_printf(("1cupsGetDests2: Returning %d destinations.", data.num_dests));
1688
1689 return (data.num_dests);
104fd4ae
MS
1690}
1691
1692
c8fef167 1693/*
9554d4e7 1694 * 'cupsGetNamedDest()' - Get options for the named destination.
c8fef167 1695 *
9554d4e7
MS
1696 * This function is optimized for retrieving a single destination and should
1697 * be used instead of @link cupsGetDests2@ and @link cupsGetDest@ when you
1698 * either know the name of the destination or want to print to the default
1699 * destination. If @code NULL@ is returned, the destination does not exist or
1700 * there is no default destination.
c8fef167 1701 *
9554d4e7
MS
1702 * If "http" is @code CUPS_HTTP_DEFAULT@, the connection to the default print
1703 * server will be used.
c8fef167 1704 *
9554d4e7
MS
1705 * If "name" is @code NULL@, the default printer for the current user will be
1706 * returned.
c8fef167 1707 *
9554d4e7
MS
1708 * The returned destination must be freed using @link cupsFreeDests@ with a
1709 * "num_dests" value of 1.
c8fef167 1710 *
9554d4e7 1711 * @since CUPS 1.4/macOS 10.6@
c8fef167
MS
1712 */
1713
9554d4e7
MS
1714cups_dest_t * /* O - Destination or @code NULL@ */
1715cupsGetNamedDest(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
1716 const char *name, /* I - Destination name or @code NULL@ for the default destination */
1717 const char *instance) /* I - Instance name or @code NULL@ */
c8fef167 1718{
9554d4e7
MS
1719 const char *dest_name; /* Working destination name */
1720 cups_dest_t *dest; /* Destination */
1721 char filename[1024], /* Path to lpoptions */
1722 defname[256]; /* Default printer name */
1723 const char *home = getenv("HOME"); /* Home directory */
1724 int set_as_default = 0; /* Set returned destination as default */
1725 ipp_op_t op = IPP_OP_GET_PRINTER_ATTRIBUTES;
1726 /* IPP operation to get server ops */
1727 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
c8fef167
MS
1728
1729
9554d4e7 1730 DEBUG_printf(("cupsGetNamedDest(http=%p, name=\"%s\", instance=\"%s\")", (void *)http, name, instance));
4a366251 1731
c8fef167 1732 /*
9554d4e7 1733 * If "name" is NULL, find the default destination...
c8fef167
MS
1734 */
1735
9554d4e7 1736 dest_name = name;
c8fef167 1737
9554d4e7
MS
1738 if (!dest_name)
1739 {
1740 set_as_default = 1;
1741 dest_name = _cupsUserDefault(defname, sizeof(defname));
c1420c87 1742
9554d4e7
MS
1743 if (dest_name)
1744 {
1745 char *ptr; /* Temporary pointer... */
c1420c87 1746
9554d4e7
MS
1747 if ((ptr = strchr(defname, '/')) != NULL)
1748 {
1749 *ptr++ = '\0';
1750 instance = ptr;
1751 }
1752 else
1753 instance = NULL;
1754 }
1755 else if (home)
1756 {
1757 /*
1758 * No default in the environment, try the user's lpoptions files...
1759 */
c1420c87 1760
9554d4e7 1761 snprintf(filename, sizeof(filename), "%s/.cups/lpoptions", home);
c8fef167 1762
9554d4e7 1763 dest_name = cups_get_default(filename, defname, sizeof(defname), &instance);
d6db9ea1
MS
1764
1765 if (dest_name)
1766 set_as_default = 2;
9554d4e7 1767 }
0cb67df3 1768
9554d4e7 1769 if (!dest_name)
c8fef167
MS
1770 {
1771 /*
9554d4e7 1772 * Still not there? Try the system lpoptions file...
c8fef167
MS
1773 */
1774
9554d4e7
MS
1775 snprintf(filename, sizeof(filename), "%s/lpoptions", cg->cups_serverroot);
1776 dest_name = cups_get_default(filename, defname, sizeof(defname), &instance);
d6db9ea1
MS
1777
1778 if (dest_name)
1779 set_as_default = 3;
9554d4e7 1780 }
c8fef167 1781
9554d4e7
MS
1782 if (!dest_name)
1783 {
c8fef167 1784 /*
9554d4e7 1785 * No locally-set default destination, ask the server...
c8fef167
MS
1786 */
1787
d6db9ea1
MS
1788 op = IPP_OP_CUPS_GET_DEFAULT;
1789 set_as_default = 4;
c8fef167 1790
9554d4e7
MS
1791 DEBUG_puts("1cupsGetNamedDest: Asking server for default printer...");
1792 }
1793 else
1794 DEBUG_printf(("1cupsGetNamedDest: Using name=\"%s\"...", name));
1795 }
c8fef167 1796
9554d4e7
MS
1797 /*
1798 * Get the printer's attributes...
1799 */
c8fef167 1800
9554d4e7
MS
1801 if (!_cupsGetDests(http, op, dest_name, &dest, 0, 0))
1802 {
1803 if (name)
1804 {
1805 _cups_namedata_t data; /* Callback data */
c8fef167 1806
9554d4e7 1807 DEBUG_puts("1cupsGetNamedDest: No queue found for printer, looking on network...");
c8fef167 1808
9554d4e7
MS
1809 data.name = name;
1810 data.dest = NULL;
c8fef167 1811
9554d4e7 1812 cupsEnumDests(0, 1000, NULL, 0, 0, (cups_dest_cb_t)cups_name_cb, &data);
c8fef167 1813
9554d4e7
MS
1814 if (!data.dest)
1815 return (NULL);
c8fef167 1816
9554d4e7
MS
1817 dest = data.dest;
1818 }
1819 else
d6db9ea1
MS
1820 {
1821 switch (set_as_default)
1822 {
1823 default :
1824 break;
1825
1826 case 1 : /* Set from env vars */
1827 if (getenv("LPDEST"))
1828 _cupsSetError(IPP_STATUS_ERROR_NOT_FOUND, _("LPDEST environment variable names default destination that does not exist."), 1);
1829 else if (getenv("PRINTER"))
1830 _cupsSetError(IPP_STATUS_ERROR_NOT_FOUND, _("PRINTER environment variable names default destination that does not exist."), 1);
1831 else
1832 _cupsSetError(IPP_STATUS_ERROR_NOT_FOUND, _("No default destination."), 1);
1833 break;
1834
1835 case 2 : /* Set from ~/.cups/lpoptions */
1836 _cupsSetError(IPP_STATUS_ERROR_NOT_FOUND, _("~/.cups/lpoptions file names default destination that does not exist."), 1);
1837 break;
1838
1839 case 3 : /* Set from /etc/cups/lpoptions */
1840 _cupsSetError(IPP_STATUS_ERROR_NOT_FOUND, _("/etc/cups/lpoptions file names default destination that does not exist."), 1);
1841 break;
1842
1843 case 4 : /* Set from server */
1844 _cupsSetError(IPP_STATUS_ERROR_NOT_FOUND, _("No default destination."), 1);
1845 break;
1846 }
1847
9554d4e7 1848 return (NULL);
d6db9ea1 1849 }
9554d4e7 1850 }
c8fef167 1851
9554d4e7 1852 DEBUG_printf(("1cupsGetNamedDest: Got dest=%p", (void *)dest));
c8fef167 1853
9554d4e7
MS
1854 if (instance)
1855 dest->instance = _cupsStrAlloc(instance);
c8fef167 1856
9554d4e7
MS
1857 if (set_as_default)
1858 dest->is_default = 1;
c8fef167 1859
9554d4e7
MS
1860 /*
1861 * Then add local options...
1862 */
c8fef167 1863
9554d4e7 1864 snprintf(filename, sizeof(filename), "%s/lpoptions", cg->cups_serverroot);
c5f5c5a7 1865 cups_get_dests(filename, dest_name, instance, 0, 1, 1, &dest);
9554d4e7
MS
1866
1867 if (home)
1868 {
1869 snprintf(filename, sizeof(filename), "%s/.cups/lpoptions", home);
1870
c5f5c5a7 1871 cups_get_dests(filename, dest_name, instance, 0, 1, 1, &dest);
c8fef167
MS
1872 }
1873
1874 /*
9554d4e7 1875 * Return the result...
c8fef167
MS
1876 */
1877
9554d4e7 1878 return (dest);
c8fef167
MS
1879}
1880
1881
ef416fc2 1882/*
9554d4e7 1883 * 'cupsRemoveDest()' - Remove a destination from the destination list.
2abf387c 1884 *
9554d4e7
MS
1885 * Removing a destination/instance does not delete the class or printer
1886 * queue, merely the lpoptions for that destination/instance. Use the
1887 * @link cupsSetDests@ or @link cupsSetDests2@ functions to save the new
1888 * options for the user.
53af7f21 1889 *
9554d4e7 1890 * @since CUPS 1.3/macOS 10.5@
ef416fc2 1891 */
1892
9554d4e7
MS
1893int /* O - New number of destinations */
1894cupsRemoveDest(const char *name, /* I - Destination name */
1895 const char *instance, /* I - Instance name or @code NULL@ */
1896 int num_dests, /* I - Number of destinations */
1897 cups_dest_t **dests) /* IO - Destinations */
ef416fc2 1898{
9554d4e7
MS
1899 int i; /* Index into destinations */
1900 cups_dest_t *dest; /* Pointer to destination */
ef416fc2 1901
1902
9554d4e7
MS
1903 /*
1904 * Find the destination...
1905 */
ef416fc2 1906
9554d4e7
MS
1907 if ((dest = cupsGetDest(name, instance, num_dests, *dests)) == NULL)
1908 return (num_dests);
fb963b8d 1909
9554d4e7
MS
1910 /*
1911 * Free memory...
ef416fc2 1912 */
1913
9554d4e7
MS
1914 _cupsStrFree(dest->name);
1915 _cupsStrFree(dest->instance);
1916 cupsFreeOptions(dest->num_options, dest->options);
ef416fc2 1917
1918 /*
9554d4e7 1919 * Remove the destination from the array...
ef416fc2 1920 */
1921
9554d4e7
MS
1922 num_dests --;
1923
1924 i = (int)(dest - *dests);
1925
1926 if (i < num_dests)
1927 memmove(dest, dest + 1, (size_t)(num_dests - i) * sizeof(cups_dest_t));
1928
1929 return (num_dests);
1930}
1931
1932
1933/*
1934 * 'cupsSetDefaultDest()' - Set the default destination.
1935 *
1936 * @since CUPS 1.3/macOS 10.5@
1937 */
1938
1939void
1940cupsSetDefaultDest(
1941 const char *name, /* I - Destination name */
1942 const char *instance, /* I - Instance name or @code NULL@ */
1943 int num_dests, /* I - Number of destinations */
1944 cups_dest_t *dests) /* I - Destinations */
1945{
1946 int i; /* Looping var */
1947 cups_dest_t *dest; /* Current destination */
7536de1a 1948
b19ccc9e 1949
ef416fc2 1950 /*
9554d4e7 1951 * Range check input...
ef416fc2 1952 */
1953
9554d4e7
MS
1954 if (!name || num_dests <= 0 || !dests)
1955 return;
ef416fc2 1956
1957 /*
9554d4e7
MS
1958 * Loop through the array and set the "is_default" flag for the matching
1959 * destination...
ef416fc2 1960 */
1961
9554d4e7
MS
1962 for (i = num_dests, dest = dests; i > 0; i --, dest ++)
1963 dest->is_default = !_cups_strcasecmp(name, dest->name) &&
1964 ((!instance && !dest->instance) ||
1965 (instance && dest->instance &&
1966 !_cups_strcasecmp(instance, dest->instance)));
1967}
080811b1 1968
ef416fc2 1969
9554d4e7
MS
1970/*
1971 * 'cupsSetDests()' - Save the list of destinations for the default server.
1972 *
1973 * This function saves the destinations to /etc/cups/lpoptions when run
1974 * as root and ~/.cups/lpoptions when run as a normal user.
1975 *
1976 * @exclude all@
1977 */
ef416fc2 1978
9554d4e7
MS
1979void
1980cupsSetDests(int num_dests, /* I - Number of destinations */
1981 cups_dest_t *dests) /* I - Destinations */
1982{
1983 cupsSetDests2(CUPS_HTTP_DEFAULT, num_dests, dests);
1984}
1985
1986
1987/*
1988 * 'cupsSetDests2()' - Save the list of destinations for the specified server.
1989 *
1990 * This function saves the destinations to /etc/cups/lpoptions when run
1991 * as root and ~/.cups/lpoptions when run as a normal user.
1992 *
1993 * @since CUPS 1.1.21/macOS 10.4@
1994 */
1995
1996int /* O - 0 on success, -1 on error */
1997cupsSetDests2(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
1998 int num_dests, /* I - Number of destinations */
1999 cups_dest_t *dests) /* I - Destinations */
2000{
2001 int i, j; /* Looping vars */
2002 int wrote; /* Wrote definition? */
2003 cups_dest_t *dest; /* Current destination */
2004 cups_option_t *option; /* Current option */
2005 _ipp_option_t *match; /* Matching attribute for option */
2006 FILE *fp; /* File pointer */
2007#ifndef WIN32
2008 const char *home; /* HOME environment variable */
2009#endif /* WIN32 */
2010 char filename[1024]; /* lpoptions file */
2011 int num_temps; /* Number of temporary destinations */
2012 cups_dest_t *temps = NULL, /* Temporary destinations */
2013 *temp; /* Current temporary dest */
2014 const char *val; /* Value of temporary option */
2015 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
ef416fc2 2016
ef416fc2 2017
2018 /*
9554d4e7 2019 * Range check the input...
ef416fc2 2020 */
2021
9554d4e7
MS
2022 if (!num_dests || !dests)
2023 return (-1);
ef416fc2 2024
9554d4e7
MS
2025 /*
2026 * Get the server destinations...
2027 */
b423cd4c 2028
9554d4e7
MS
2029 num_temps = _cupsGetDests(http, IPP_OP_CUPS_GET_PRINTERS, NULL, &temps, 0, 0);
2030
2031 if (cupsLastError() >= IPP_STATUS_REDIRECTION_OTHER_SITE)
2032 {
2033 cupsFreeDests(num_temps, temps);
2034 return (-1);
ef416fc2 2035 }
2036
2037 /*
9554d4e7 2038 * Figure out which file to write to...
ef416fc2 2039 */
2040
9554d4e7
MS
2041 snprintf(filename, sizeof(filename), "%s/lpoptions", cg->cups_serverroot);
2042
2043#ifndef WIN32
2044 if (getuid())
ef416fc2 2045 {
9554d4e7
MS
2046 /*
2047 * Point to user defaults...
2048 */
2049
2050 if ((home = getenv("HOME")) != NULL)
ef416fc2 2051 {
2052 /*
9554d4e7 2053 * Create ~/.cups subdirectory...
ef416fc2 2054 */
2055
9554d4e7
MS
2056 snprintf(filename, sizeof(filename), "%s/.cups", home);
2057 if (access(filename, 0))
2058 mkdir(filename, 0700);
ef416fc2 2059
9554d4e7 2060 snprintf(filename, sizeof(filename), "%s/.cups/lpoptions", home);
ef416fc2 2061 }
ef416fc2 2062 }
9554d4e7 2063#endif /* !WIN32 */
ef416fc2 2064
2065 /*
9554d4e7 2066 * Try to open the file...
ef416fc2 2067 */
2068
9554d4e7
MS
2069 if ((fp = fopen(filename, "w")) == NULL)
2070 {
2071 cupsFreeDests(num_temps, temps);
2072 return (-1);
2073 }
1ff0402e 2074
9554d4e7
MS
2075#ifndef WIN32
2076 /*
2077 * Set the permissions to 0644 when saving to the /etc/cups/lpoptions
2078 * file...
2079 */
fb963b8d 2080
9554d4e7
MS
2081 if (!getuid())
2082 fchmod(fileno(fp), 0644);
2083#endif /* !WIN32 */
ef416fc2 2084
9554d4e7
MS
2085 /*
2086 * Write each printer; each line looks like:
2087 *
2088 * Dest name[/instance] options
2089 * Default name[/instance] options
2090 */
ef416fc2 2091
9554d4e7
MS
2092 for (i = num_dests, dest = dests; i > 0; i --, dest ++)
2093 if (dest->instance != NULL || dest->num_options != 0 || dest->is_default)
7cf5915e 2094 {
9554d4e7 2095 if (dest->is_default)
7cf5915e 2096 {
9554d4e7
MS
2097 fprintf(fp, "Default %s", dest->name);
2098 if (dest->instance)
2099 fprintf(fp, "/%s", dest->instance);
2100
2101 wrote = 1;
7cf5915e
MS
2102 }
2103 else
9554d4e7 2104 wrote = 0;
a4924f6c 2105
42a5ae2a 2106 temp = cupsGetDest(dest->name, NULL, num_temps, temps);
a4924f6c 2107
9554d4e7
MS
2108 for (j = dest->num_options, option = dest->options; j > 0; j --, option ++)
2109 {
2110 /*
2111 * See if this option is a printer attribute; if so, skip it...
2112 */
a4924f6c 2113
42a5ae2a 2114 if ((match = _ippFindOption(option->name)) != NULL && match->group_tag == IPP_TAG_PRINTER)
9554d4e7 2115 continue;
a4924f6c 2116
9554d4e7 2117 /*
42a5ae2a 2118 * See if the server options match these; if so, don't write 'em.
9554d4e7 2119 */
a4924f6c 2120
42a5ae2a 2121 if (temp && (val = cupsGetOption(option->name, temp->num_options, temp->options)) != NULL && !_cups_strcasecmp(val, option->value))
9554d4e7 2122 continue;
a4924f6c 2123
9554d4e7
MS
2124 /*
2125 * Options don't match, write to the file...
2126 */
4a366251 2127
9554d4e7
MS
2128 if (!wrote)
2129 {
2130 fprintf(fp, "Dest %s", dest->name);
2131 if (dest->instance)
2132 fprintf(fp, "/%s", dest->instance);
2133 wrote = 1;
2134 }
a4924f6c 2135
9554d4e7
MS
2136 if (option->value[0])
2137 {
42a5ae2a 2138 if (strchr(option->value, ' ') || strchr(option->value, '\\') || strchr(option->value, '\"') || strchr(option->value, '\''))
9554d4e7
MS
2139 {
2140 /*
2141 * Quote the value...
2142 */
a4924f6c 2143
9554d4e7 2144 fprintf(fp, " %s=\"", option->name);
4a366251 2145
9554d4e7
MS
2146 for (val = option->value; *val; val ++)
2147 {
2148 if (strchr("\"\'\\", *val))
2149 putc('\\', fp);
4a366251 2150
9554d4e7
MS
2151 putc(*val, fp);
2152 }
4a366251 2153
9554d4e7
MS
2154 putc('\"', fp);
2155 }
2156 else
2157 {
2158 /*
2159 * Store the literal value...
2160 */
4a366251 2161
9554d4e7
MS
2162 fprintf(fp, " %s=%s", option->name, option->value);
2163 }
2164 }
2165 else
2166 fprintf(fp, " %s", option->name);
2167 }
4a366251 2168
9554d4e7
MS
2169 if (wrote)
2170 fputs("\n", fp);
4a366251 2171 }
4a366251 2172
9554d4e7
MS
2173 /*
2174 * Free the temporary destinations and close the file...
2175 */
a4924f6c 2176
9554d4e7 2177 cupsFreeDests(num_temps, temps);
a4924f6c 2178
9554d4e7 2179 fclose(fp);
38e73f87 2180
9554d4e7 2181#ifdef __APPLE__
a4924f6c 2182 /*
9554d4e7
MS
2183 * Set the default printer for this location - this allows command-line
2184 * and GUI applications to share the same default destination...
a4924f6c
MS
2185 */
2186
9554d4e7 2187 if ((dest = cupsGetDest(NULL, NULL, num_dests, dests)) != NULL)
a4924f6c 2188 {
42a5ae2a 2189 CFStringRef name = CFStringCreateWithCString(kCFAllocatorDefault, dest->name, kCFStringEncodingUTF8);
9554d4e7 2190 /* Default printer name */
a4924f6c 2191
9554d4e7
MS
2192 if (name)
2193 {
2194 _cupsAppleSetDefaultPrinter(name);
2195 CFRelease(name);
2196 }
a4924f6c 2197 }
9554d4e7 2198#endif /* __APPLE__ */
a4924f6c 2199
9554d4e7 2200#ifdef HAVE_NOTIFY_POST
a4924f6c 2201 /*
9554d4e7
MS
2202 * Send a notification so that macOS applications can know about the
2203 * change, too.
a4924f6c
MS
2204 */
2205
9554d4e7
MS
2206 notify_post("com.apple.printerListChange");
2207#endif /* HAVE_NOTIFY_POST */
2208
2209 return (0);
a4924f6c
MS
2210}
2211
2212
f7deaa1a 2213/*
9554d4e7
MS
2214 * '_cupsUserDefault()' - Get the user default printer from environment
2215 * variables and location information.
f7deaa1a 2216 */
2217
9554d4e7
MS
2218char * /* O - Default printer or NULL */
2219_cupsUserDefault(char *name, /* I - Name buffer */
2220 size_t namesize) /* I - Size of name buffer */
f7deaa1a 2221{
9554d4e7
MS
2222 const char *env; /* LPDEST or PRINTER env variable */
2223#ifdef __APPLE__
2224 CFStringRef locprinter; /* Last printer as this location */
2225#endif /* __APPLE__ */
f7deaa1a 2226
2227
9554d4e7
MS
2228 if ((env = getenv("LPDEST")) == NULL)
2229 if ((env = getenv("PRINTER")) != NULL && !strcmp(env, "lp"))
2230 env = NULL;
f7deaa1a 2231
9554d4e7
MS
2232 if (env)
2233 {
2234 strlcpy(name, env, namesize);
2235 return (name);
2236 }
f7deaa1a 2237
9554d4e7 2238#ifdef __APPLE__
f7deaa1a 2239 /*
9554d4e7
MS
2240 * Use location-based defaults if "use last printer" is selected in the
2241 * system preferences...
f7deaa1a 2242 */
2243
9554d4e7
MS
2244 if ((locprinter = _cupsAppleCopyDefaultPrinter()) != NULL)
2245 {
2246 CFStringGetCString(locprinter, name, (CFIndex)namesize, kCFStringEncodingUTF8);
2247 CFRelease(locprinter);
2248 }
2249 else
2250 name[0] = '\0';
f7deaa1a 2251
9554d4e7 2252 DEBUG_printf(("1_cupsUserDefault: Returning \"%s\".", name));
f7deaa1a 2253
9554d4e7 2254 return (*name ? name : NULL);
f7deaa1a 2255
9554d4e7
MS
2256#else
2257 /*
2258 * No location-based defaults on this platform...
2259 */
f7deaa1a 2260
9554d4e7
MS
2261 name[0] = '\0';
2262 return (NULL);
2263#endif /* __APPLE__ */
f7deaa1a 2264}
2265
2266
9554d4e7 2267#if _CUPS_LOCATION_DEFAULTS
f7deaa1a 2268/*
9554d4e7 2269 * 'appleCopyLocations()' - Copy the location history array.
f7deaa1a 2270 */
2271
9554d4e7
MS
2272static CFArrayRef /* O - Location array or NULL */
2273appleCopyLocations(void)
f7deaa1a 2274{
9554d4e7 2275 CFArrayRef locations; /* Location array */
f7deaa1a 2276
f7deaa1a 2277
2278 /*
9554d4e7 2279 * Look up the location array in the preferences...
f7deaa1a 2280 */
2281
9554d4e7
MS
2282 if ((locations = CFPreferencesCopyAppValue(kLastUsedPrintersKey,
2283 kCUPSPrintingPrefs)) == NULL)
2284 return (NULL);
f7deaa1a 2285
9554d4e7
MS
2286 if (CFGetTypeID(locations) != CFArrayGetTypeID())
2287 {
2288 CFRelease(locations);
2289 return (NULL);
2290 }
ef416fc2 2291
9554d4e7 2292 return (locations);
ef416fc2 2293}
2294
2295
2296/*
9554d4e7 2297 * 'appleCopyNetwork()' - Get the network ID for the current location.
ef416fc2 2298 */
2299
9554d4e7
MS
2300static CFStringRef /* O - Network ID */
2301appleCopyNetwork(void)
ef416fc2 2302{
9554d4e7
MS
2303 SCDynamicStoreRef dynamicStore; /* System configuration data */
2304 CFStringRef key; /* Current network configuration key */
2305 CFDictionaryRef ip_dict; /* Network configuration data */
2306 CFStringRef network = NULL; /* Current network ID */
ef416fc2 2307
ef416fc2 2308
9554d4e7
MS
2309 if ((dynamicStore = SCDynamicStoreCreate(NULL, CFSTR("libcups"), NULL,
2310 NULL)) != NULL)
ef416fc2 2311 {
2312 /*
9554d4e7
MS
2313 * First use the IPv6 router address, if available, since that will generally
2314 * be a globally-unique link-local address.
ef416fc2 2315 */
2316
9554d4e7
MS
2317 if ((key = SCDynamicStoreKeyCreateNetworkGlobalEntity(
2318 NULL, kSCDynamicStoreDomainState, kSCEntNetIPv6)) != NULL)
2319 {
2320 if ((ip_dict = SCDynamicStoreCopyValue(dynamicStore, key)) != NULL)
2321 {
2322 if ((network = CFDictionaryGetValue(ip_dict,
2323 kSCPropNetIPv6Router)) != NULL)
2324 CFRetain(network);
2325
2326 CFRelease(ip_dict);
2327 }
2328
2329 CFRelease(key);
2330 }
ef416fc2 2331
2332 /*
9554d4e7
MS
2333 * If that doesn't work, try the IPv4 router address. This isn't as unique
2334 * and will likely be a 10.x.y.z or 192.168.y.z address...
ef416fc2 2335 */
2336
9554d4e7 2337 if (!network)
b423cd4c 2338 {
9554d4e7
MS
2339 if ((key = SCDynamicStoreKeyCreateNetworkGlobalEntity(
2340 NULL, kSCDynamicStoreDomainState, kSCEntNetIPv4)) != NULL)
2341 {
2342 if ((ip_dict = SCDynamicStoreCopyValue(dynamicStore, key)) != NULL)
2343 {
2344 if ((network = CFDictionaryGetValue(ip_dict,
2345 kSCPropNetIPv4Router)) != NULL)
2346 CFRetain(network);
b423cd4c 2347
9554d4e7
MS
2348 CFRelease(ip_dict);
2349 }
b423cd4c 2350
9554d4e7
MS
2351 CFRelease(key);
2352 }
b423cd4c 2353 }
ef416fc2 2354
9554d4e7 2355 CFRelease(dynamicStore);
ef416fc2 2356 }
2357
9554d4e7
MS
2358 return (network);
2359}
2360#endif /* _CUPS_LOCATION_DEFAULTS */
d6ae789d 2361
d6ae789d 2362
9554d4e7
MS
2363#ifdef __APPLE__
2364/*
2365 * 'appleGetPaperSize()' - Get the default paper size.
2366 */
ef416fc2 2367
9554d4e7
MS
2368static char * /* O - Default paper size */
2369appleGetPaperSize(char *name, /* I - Paper size name buffer */
2370 size_t namesize) /* I - Size of buffer */
2371{
2372 CFStringRef defaultPaperID; /* Default paper ID */
2373 pwg_media_t *pwgmedia; /* PWG media size */
ef416fc2 2374
ef416fc2 2375
9554d4e7
MS
2376 defaultPaperID = _cupsAppleCopyDefaultPaperID();
2377 if (!defaultPaperID ||
2378 CFGetTypeID(defaultPaperID) != CFStringGetTypeID() ||
2379 !CFStringGetCString(defaultPaperID, name, (CFIndex)namesize, kCFStringEncodingUTF8))
2380 name[0] = '\0';
2381 else if ((pwgmedia = pwgMediaForLegacy(name)) != NULL)
2382 strlcpy(name, pwgmedia->pwg, namesize);
ef416fc2 2383
9554d4e7
MS
2384 if (defaultPaperID)
2385 CFRelease(defaultPaperID);
8ca02f3c 2386
9554d4e7
MS
2387 return (name);
2388}
2389#endif /* __APPLE__ */
8ca02f3c 2390
ef416fc2 2391
9554d4e7
MS
2392#if _CUPS_LOCATION_DEFAULTS
2393/*
2394 * 'appleGetPrinter()' - Get a printer from the history array.
2395 */
ef416fc2 2396
9554d4e7
MS
2397static CFStringRef /* O - Printer name or NULL */
2398appleGetPrinter(CFArrayRef locations, /* I - Location array */
2399 CFStringRef network, /* I - Network name */
2400 CFIndex *locindex) /* O - Index in array */
2401{
2402 CFIndex i, /* Looping var */
2403 count; /* Number of locations */
2404 CFDictionaryRef location; /* Current location */
2405 CFStringRef locnetwork, /* Current network */
2406 locprinter; /* Current printer */
ef416fc2 2407
88f9aafc 2408
9554d4e7
MS
2409 for (i = 0, count = CFArrayGetCount(locations); i < count; i ++)
2410 if ((location = CFArrayGetValueAtIndex(locations, i)) != NULL &&
2411 CFGetTypeID(location) == CFDictionaryGetTypeID())
2412 {
2413 if ((locnetwork = CFDictionaryGetValue(location,
2414 kLocationNetworkKey)) != NULL &&
2415 CFGetTypeID(locnetwork) == CFStringGetTypeID() &&
2416 CFStringCompare(network, locnetwork, 0) == kCFCompareEqualTo &&
2417 (locprinter = CFDictionaryGetValue(location,
2418 kLocationPrinterIDKey)) != NULL &&
2419 CFGetTypeID(locprinter) == CFStringGetTypeID())
2420 {
2421 if (locindex)
2422 *locindex = i;
8ca02f3c 2423
9554d4e7
MS
2424 return (locprinter);
2425 }
2426 }
8ca02f3c 2427
9554d4e7
MS
2428 return (NULL);
2429}
2430#endif /* _CUPS_LOCATION_DEFAULTS */
8ca02f3c 2431
8ca02f3c 2432
9554d4e7
MS
2433/*
2434 * 'cups_add_dest()' - Add a destination to the array.
2435 *
2436 * Unlike cupsAddDest(), this function does not check for duplicates.
2437 */
8ca02f3c 2438
9554d4e7
MS
2439static cups_dest_t * /* O - New destination */
2440cups_add_dest(const char *name, /* I - Name of destination */
2441 const char *instance, /* I - Instance or NULL */
2442 int *num_dests, /* IO - Number of destinations */
2443 cups_dest_t **dests) /* IO - Destinations */
2444{
2445 int insert, /* Insertion point */
2446 diff; /* Result of comparison */
2447 cups_dest_t *dest; /* Destination pointer */
ef416fc2 2448
ef416fc2 2449
2450 /*
9554d4e7 2451 * Add new destination...
ef416fc2 2452 */
2453
9554d4e7
MS
2454 if (*num_dests == 0)
2455 dest = malloc(sizeof(cups_dest_t));
2456 else
2457 dest = realloc(*dests, sizeof(cups_dest_t) * (size_t)(*num_dests + 1));
ef416fc2 2458
9554d4e7
MS
2459 if (!dest)
2460 return (NULL);
2461
2462 *dests = dest;
fa73b229 2463
080811b1 2464 /*
9554d4e7 2465 * Find where to insert the destination...
080811b1
MS
2466 */
2467
9554d4e7
MS
2468 if (*num_dests == 0)
2469 insert = 0;
2470 else
f14324a7 2471 {
9554d4e7
MS
2472 insert = cups_find_dest(name, instance, *num_dests, *dests, *num_dests - 1,
2473 &diff);
f14324a7 2474
9554d4e7
MS
2475 if (diff > 0)
2476 insert ++;
f14324a7 2477 }
080811b1 2478
ef416fc2 2479 /*
9554d4e7 2480 * Move the array elements as needed...
ef416fc2 2481 */
2482
9554d4e7
MS
2483 if (insert < *num_dests)
2484 memmove(*dests + insert + 1, *dests + insert, (size_t)(*num_dests - insert) * sizeof(cups_dest_t));
ef416fc2 2485
9554d4e7
MS
2486 (*num_dests) ++;
2487
2488 /*
2489 * Initialize the destination...
2490 */
2491
2492 dest = *dests + insert;
2493 dest->name = _cupsStrAlloc(name);
2494 dest->instance = _cupsStrAlloc(instance);
2495 dest->is_default = 0;
2496 dest->num_options = 0;
2497 dest->options = (cups_option_t *)0;
2498
2499 return (dest);
ef416fc2 2500}
2501
2502
9554d4e7 2503# ifdef __BLOCKS__
38e73f87 2504/*
9554d4e7 2505 * 'cups_block_cb()' - Enumeration callback for block API.
38e73f87
MS
2506 */
2507
9554d4e7
MS
2508static int /* O - 1 to continue, 0 to stop */
2509cups_block_cb(
2510 cups_dest_block_t block, /* I - Block */
2511 unsigned flags, /* I - Destination flags */
2512 cups_dest_t *dest) /* I - Destination */
38e73f87 2513{
9554d4e7
MS
2514 return ((block)(flags, dest));
2515}
2516# endif /* __BLOCKS__ */
38e73f87
MS
2517
2518
9554d4e7
MS
2519/*
2520 * 'cups_compare_dests()' - Compare two destinations.
2521 */
38e73f87 2522
9554d4e7
MS
2523static int /* O - Result of comparison */
2524cups_compare_dests(cups_dest_t *a, /* I - First destination */
2525 cups_dest_t *b) /* I - Second destination */
2526{
2527 int diff; /* Difference */
38e73f87 2528
38e73f87 2529
9554d4e7
MS
2530 if ((diff = _cups_strcasecmp(a->name, b->name)) != 0)
2531 return (diff);
2532 else if (a->instance && b->instance)
2533 return (_cups_strcasecmp(a->instance, b->instance));
38e73f87 2534 else
9554d4e7 2535 return ((a->instance && !b->instance) - (!a->instance && b->instance));
38e73f87
MS
2536}
2537
2538
9554d4e7
MS
2539#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
2540# ifdef HAVE_DNSSD
06d4e77b 2541/*
9554d4e7 2542 * 'cups_dnssd_browse_cb()' - Browse for printers.
06d4e77b
MS
2543 */
2544
9554d4e7
MS
2545static void
2546cups_dnssd_browse_cb(
2547 DNSServiceRef sdRef, /* I - Service reference */
2548 DNSServiceFlags flags, /* I - Option flags */
2549 uint32_t interfaceIndex, /* I - Interface number */
2550 DNSServiceErrorType errorCode, /* I - Error, if any */
2551 const char *serviceName, /* I - Name of service/device */
2552 const char *regtype, /* I - Type of service */
2553 const char *replyDomain, /* I - Service domain */
2554 void *context) /* I - Enumeration data */
06d4e77b 2555{
9554d4e7
MS
2556 _cups_dnssd_data_t *data = (_cups_dnssd_data_t *)context;
2557 /* Enumeration data */
2558
06d4e77b 2559
9554d4e7 2560 DEBUG_printf(("5cups_dnssd_browse_cb(sdRef=%p, flags=%x, interfaceIndex=%d, errorCode=%d, serviceName=\"%s\", regtype=\"%s\", replyDomain=\"%s\", context=%p)", (void *)sdRef, flags, interfaceIndex, errorCode, serviceName, regtype, replyDomain, context));
06d4e77b
MS
2561
2562 /*
9554d4e7 2563 * Don't do anything on error...
06d4e77b
MS
2564 */
2565
9554d4e7
MS
2566 if (errorCode != kDNSServiceErr_NoError)
2567 return;
06d4e77b 2568
9554d4e7
MS
2569 /*
2570 * Get the device...
2571 */
06d4e77b 2572
9554d4e7 2573 cups_dnssd_get_device(data, serviceName, regtype, replyDomain);
06d4e77b
MS
2574}
2575
2576
9554d4e7 2577# else /* HAVE_AVAHI */
749b1e90 2578/*
9554d4e7 2579 * 'cups_dnssd_browse_cb()' - Browse for printers.
749b1e90
MS
2580 */
2581
9554d4e7
MS
2582static void
2583cups_dnssd_browse_cb(
2584 AvahiServiceBrowser *browser, /* I - Browser */
2585 AvahiIfIndex interface, /* I - Interface index (unused) */
2586 AvahiProtocol protocol, /* I - Network protocol (unused) */
2587 AvahiBrowserEvent event, /* I - What happened */
2588 const char *name, /* I - Service name */
2589 const char *type, /* I - Registration type */
2590 const char *domain, /* I - Domain */
2591 AvahiLookupResultFlags flags, /* I - Flags */
2592 void *context) /* I - Devices array */
749b1e90 2593{
9554d4e7
MS
2594#ifdef DEBUG
2595 AvahiClient *client = avahi_service_browser_get_client(browser);
2596 /* Client information */
2597#endif /* DEBUG */
2598 _cups_dnssd_data_t *data = (_cups_dnssd_data_t *)context;
2599 /* Enumeration data */
88f9aafc 2600
749b1e90 2601
9554d4e7
MS
2602 (void)interface;
2603 (void)protocol;
2604 (void)context;
f14324a7 2605
9554d4e7 2606 DEBUG_printf(("cups_dnssd_browse_cb(..., name=\"%s\", type=\"%s\", domain=\"%s\", ...);", name, type, domain));
749b1e90 2607
9554d4e7
MS
2608 switch (event)
2609 {
2610 case AVAHI_BROWSER_FAILURE:
2611 DEBUG_printf(("cups_dnssd_browse_cb: %s", avahi_strerror(avahi_client_errno(client))));
2612 avahi_simple_poll_quit(data->simple_poll);
2613 break;
749b1e90 2614
9554d4e7
MS
2615 case AVAHI_BROWSER_NEW:
2616 /*
2617 * This object is new on the network.
2618 */
749b1e90 2619
625bb9de 2620 cups_dnssd_get_device(data, name, type, domain);
9554d4e7 2621 break;
f14324a7 2622
9554d4e7
MS
2623 case AVAHI_BROWSER_REMOVE :
2624 case AVAHI_BROWSER_CACHE_EXHAUSTED :
2625 break;
f14324a7 2626
9554d4e7
MS
2627 case AVAHI_BROWSER_ALL_FOR_NOW :
2628 DEBUG_puts("cups_dnssd_browse_cb: ALL_FOR_NOW");
2629 data->browsers --;
2630 break;
749b1e90 2631 }
749b1e90
MS
2632}
2633
2634
426c6a59 2635/*
9554d4e7 2636 * 'cups_dnssd_client_cb()' - Avahi client callback function.
426c6a59
MS
2637 */
2638
9554d4e7
MS
2639static void
2640cups_dnssd_client_cb(
2641 AvahiClient *client, /* I - Client information (unused) */
2642 AvahiClientState state, /* I - Current state */
2643 void *context) /* I - User data (unused) */
426c6a59 2644{
9554d4e7
MS
2645 _cups_dnssd_data_t *data = (_cups_dnssd_data_t *)context;
2646 /* Enumeration data */
426c6a59
MS
2647
2648
9554d4e7 2649 (void)client;
426c6a59 2650
9554d4e7 2651 DEBUG_printf(("cups_dnssd_client_cb(client=%p, state=%d, context=%p)", client, state, context));
426c6a59 2652
9554d4e7
MS
2653 /*
2654 * If the connection drops, quit.
2655 */
2656
2657 if (state == AVAHI_CLIENT_FAILURE)
2658 {
2659 DEBUG_puts("cups_dnssd_client_cb: Avahi connection failed.");
2660 avahi_simple_poll_quit(data->simple_poll);
2661 }
426c6a59 2662}
9554d4e7 2663# endif /* HAVE_DNSSD */
426c6a59
MS
2664
2665
080811b1 2666/*
9554d4e7 2667 * 'cups_dnssd_compare_device()' - Compare two devices.
080811b1
MS
2668 */
2669
9554d4e7
MS
2670static int /* O - Result of comparison */
2671cups_dnssd_compare_devices(
2672 _cups_dnssd_device_t *a, /* I - First device */
2673 _cups_dnssd_device_t *b) /* I - Second device */
080811b1 2674{
9554d4e7
MS
2675 return (strcmp(a->dest.name, b->dest.name));
2676}
080811b1
MS
2677
2678
9554d4e7
MS
2679/*
2680 * 'cups_dnssd_free_device()' - Free the memory used by a device.
2681 */
080811b1 2682
9554d4e7
MS
2683static void
2684cups_dnssd_free_device(
2685 _cups_dnssd_device_t *device, /* I - Device */
2686 _cups_dnssd_data_t *data) /* I - Enumeration data */
2687{
2688 DEBUG_printf(("5cups_dnssd_free_device(device=%p(%s), data=%p)", (void *)device, device->dest.name, (void *)data));
080811b1 2689
9554d4e7
MS
2690# ifdef HAVE_DNSSD
2691 if (device->ref)
2692 DNSServiceRefDeallocate(device->ref);
2693# else /* HAVE_AVAHI */
2694 if (device->ref)
2695 avahi_record_browser_free(device->ref);
2696# endif /* HAVE_DNSSD */
2697
2698 _cupsStrFree(device->domain);
2699 _cupsStrFree(device->fullName);
2700 _cupsStrFree(device->regtype);
2701 _cupsStrFree(device->dest.name);
2702
2703 cupsFreeOptions(device->dest.num_options, device->dest.options);
2704
2705 free(device);
080811b1 2706}
080811b1
MS
2707
2708
426c6a59 2709/*
9554d4e7 2710 * 'cups_dnssd_get_device()' - Lookup a device and create it as needed.
426c6a59
MS
2711 */
2712
9554d4e7
MS
2713static _cups_dnssd_device_t * /* O - Device */
2714cups_dnssd_get_device(
2715 _cups_dnssd_data_t *data, /* I - Enumeration data */
2716 const char *serviceName, /* I - Service name */
2717 const char *regtype, /* I - Registration type */
2718 const char *replyDomain) /* I - Domain name */
426c6a59 2719{
9554d4e7
MS
2720 _cups_dnssd_device_t key, /* Search key */
2721 *device; /* Device */
2722 char fullName[kDNSServiceMaxDomainName],
2723 /* Full name for query */
2724 name[128]; /* Queue name */
2725
426c6a59 2726
9554d4e7 2727 DEBUG_printf(("5cups_dnssd_get_device(data=%p, serviceName=\"%s\", regtype=\"%s\", replyDomain=\"%s\")", (void *)data, serviceName, regtype, replyDomain));
426c6a59
MS
2728
2729 /*
9554d4e7 2730 * See if this is an existing device...
426c6a59
MS
2731 */
2732
9554d4e7 2733 cups_queue_name(name, serviceName, sizeof(name));
426c6a59 2734
9554d4e7 2735 key.dest.name = name;
426c6a59 2736
9554d4e7 2737 if ((device = cupsArrayFind(data->devices, &key)) != NULL)
426c6a59 2738 {
9554d4e7
MS
2739 /*
2740 * Yes, see if we need to do anything with this...
2741 */
426c6a59 2742
9554d4e7 2743 int update = 0; /* Non-zero if we need to update */
426c6a59 2744
9554d4e7
MS
2745 if (!_cups_strcasecmp(replyDomain, "local.") &&
2746 _cups_strcasecmp(device->domain, replyDomain))
2747 {
2748 /*
2749 * Update the "global" listing to use the .local domain name instead.
2750 */
426c6a59 2751
9554d4e7
MS
2752 _cupsStrFree(device->domain);
2753 device->domain = _cupsStrAlloc(replyDomain);
426c6a59 2754
9554d4e7
MS
2755 DEBUG_printf(("6cups_dnssd_get_device: Updating '%s' to use local "
2756 "domain.", device->dest.name));
426c6a59 2757
9554d4e7
MS
2758 update = 1;
2759 }
426c6a59 2760
9554d4e7
MS
2761 if (!_cups_strcasecmp(regtype, "_ipps._tcp") &&
2762 _cups_strcasecmp(device->regtype, regtype))
2763 {
2764 /*
2765 * Prefer IPPS over IPP.
2766 */
426c6a59 2767
9554d4e7
MS
2768 _cupsStrFree(device->regtype);
2769 device->regtype = _cupsStrAlloc(regtype);
426c6a59 2770
9554d4e7
MS
2771 DEBUG_printf(("6cups_dnssd_get_device: Updating '%s' to use IPPS.",
2772 device->dest.name));
426c6a59 2773
9554d4e7
MS
2774 update = 1;
2775 }
9c80ffa2 2776
9554d4e7
MS
2777 if (!update)
2778 {
2779 DEBUG_printf(("6cups_dnssd_get_device: No changes to '%s'.",
2780 device->dest.name));
2781 return (device);
2782 }
2783 }
2784 else
2785 {
2786 /*
2787 * No, add the device...
2788 */
9c80ffa2 2789
9554d4e7
MS
2790 DEBUG_printf(("6cups_dnssd_get_device: Adding '%s' for %s with domain "
2791 "'%s'.", serviceName,
2792 !strcmp(regtype, "_ipps._tcp") ? "IPPS" : "IPP",
2793 replyDomain));
9c80ffa2 2794
9554d4e7
MS
2795 device = calloc(sizeof(_cups_dnssd_device_t), 1);
2796 device->dest.name = _cupsStrAlloc(name);
2797 device->domain = _cupsStrAlloc(replyDomain);
2798 device->regtype = _cupsStrAlloc(regtype);
426c6a59 2799
9554d4e7 2800 device->dest.num_options = cupsAddOption("printer-info", serviceName, 0, &device->dest.options);
426c6a59 2801
9554d4e7
MS
2802 cupsArrayAdd(data->devices, device);
2803 }
426c6a59 2804
9554d4e7
MS
2805 /*
2806 * Set the "full name" of this service, which is used for queries...
2807 */
2808
2809# ifdef HAVE_DNSSD
2810 DNSServiceConstructFullName(fullName, serviceName, regtype, replyDomain);
2811# else /* HAVE_AVAHI */
2812 avahi_service_name_join(fullName, kDNSServiceMaxDomainName, serviceName, regtype, replyDomain);
2813# endif /* HAVE_DNSSD */
2814
2815 _cupsStrFree(device->fullName);
2816 device->fullName = _cupsStrAlloc(fullName);
2817
2818 if (device->ref)
2819 {
2820# ifdef HAVE_DNSSD
2821 DNSServiceRefDeallocate(device->ref);
2822# else /* HAVE_AVAHI */
2823 avahi_record_browser_free(device->ref);
2824# endif /* HAVE_DNSSD */
2825
2826 device->ref = 0;
2827 }
2828
2829 if (device->state == _CUPS_DNSSD_ACTIVE)
2830 {
2831 DEBUG_printf(("6cups_dnssd_get_device: Remove callback for \"%s\".", device->dest.name));
2832
2833 (*data->cb)(data->user_data, CUPS_DEST_FLAGS_REMOVED, &device->dest);
2834 device->state = _CUPS_DNSSD_NEW;
2835 }
2836
2837 return (device);
426c6a59
MS
2838}
2839
2840
9554d4e7 2841# ifdef HAVE_AVAHI
a29fd7dd 2842/*
9554d4e7
MS
2843 * 'cups_dnssd_poll_cb()' - Wait for input on the specified file descriptors.
2844 *
2845 * Note: This function is needed because avahi_simple_poll_iterate is broken
2846 * and always uses a timeout of 0 (!) milliseconds.
2847 * (https://github.com/lathiat/avahi/issues/127)
2848 *
2849 * @private@
a29fd7dd
MS
2850 */
2851
9554d4e7
MS
2852static int /* O - Number of file descriptors matching */
2853cups_dnssd_poll_cb(
2854 struct pollfd *pollfds, /* I - File descriptors */
2855 unsigned int num_pollfds, /* I - Number of file descriptors */
2856 int timeout, /* I - Timeout in milliseconds (unused) */
2857 void *context) /* I - User data (unused) */
a29fd7dd 2858{
a29fd7dd
MS
2859 _cups_dnssd_data_t *data = (_cups_dnssd_data_t *)context;
2860 /* Enumeration data */
9554d4e7 2861 int val; /* Return value */
a29fd7dd
MS
2862
2863
9554d4e7 2864 DEBUG_printf(("cups_dnssd_poll_cb(pollfds=%p, num_pollfds=%d, timeout=%d, context=%p)", pollfds, num_pollfds, timeout, context));
a29fd7dd 2865
9554d4e7 2866 (void)timeout;
a29fd7dd 2867
9554d4e7 2868 val = poll(pollfds, num_pollfds, _CUPS_DNSSD_MAXTIME);
a29fd7dd 2869
9554d4e7 2870 DEBUG_printf(("cups_dnssd_poll_cb: poll() returned %d", val));
657c5b5f 2871
9554d4e7
MS
2872 if (val < 0)
2873 {
2874 DEBUG_printf(("cups_dnssd_poll_cb: %s", strerror(errno)));
2875 }
2876 else if (val > 0)
2877 {
2878 data->got_data = 1;
a29fd7dd 2879 }
9554d4e7
MS
2880
2881 return (val);
a29fd7dd 2882}
9554d4e7 2883# endif /* HAVE_AVAHI */
a29fd7dd
MS
2884
2885
2886/*
9554d4e7 2887 * 'cups_dnssd_query_cb()' - Process query data.
a29fd7dd
MS
2888 */
2889
9554d4e7 2890# ifdef HAVE_DNSSD
a29fd7dd 2891static void
9554d4e7
MS
2892cups_dnssd_query_cb(
2893 DNSServiceRef sdRef, /* I - Service reference */
2894 DNSServiceFlags flags, /* I - Data flags */
2895 uint32_t interfaceIndex, /* I - Interface */
2896 DNSServiceErrorType errorCode, /* I - Error, if any */
2897 const char *fullName, /* I - Full service name */
2898 uint16_t rrtype, /* I - Record type */
2899 uint16_t rrclass, /* I - Record class */
2900 uint16_t rdlen, /* I - Length of record data */
2901 const void *rdata, /* I - Record data */
2902 uint32_t ttl, /* I - Time-to-live */
2903 void *context) /* I - Enumeration data */
2904{
2905# else /* HAVE_AVAHI */
2906static void
2907cups_dnssd_query_cb(
2908 AvahiRecordBrowser *browser, /* I - Record browser */
2909 AvahiIfIndex interfaceIndex,
2910 /* I - Interface index (unused) */
2911 AvahiProtocol protocol, /* I - Network protocol (unused) */
2912 AvahiBrowserEvent event, /* I - What happened? */
2913 const char *fullName, /* I - Service name */
2914 uint16_t rrclass, /* I - Record class */
2915 uint16_t rrtype, /* I - Record type */
2916 const void *rdata, /* I - TXT record */
2917 size_t rdlen, /* I - Length of TXT record */
2918 AvahiLookupResultFlags flags, /* I - Flags */
c5f5c5a7 2919 void *context) /* I - Enumeration data */
9554d4e7
MS
2920{
2921# ifdef DEBUG
2922 AvahiClient *client = avahi_record_browser_get_client(browser);
2923 /* Client information */
2924# endif /* DEBUG */
2925# endif /* HAVE_DNSSD */
a29fd7dd
MS
2926 _cups_dnssd_data_t *data = (_cups_dnssd_data_t *)context;
2927 /* Enumeration data */
9554d4e7
MS
2928 char serviceName[256],/* Service name */
2929 name[128], /* Queue name */
2930 *ptr; /* Pointer into string */
2931 _cups_dnssd_device_t dkey, /* Search key */
2932 *device; /* Device */
a29fd7dd
MS
2933
2934
9554d4e7
MS
2935# ifdef HAVE_DNSSD
2936 DEBUG_printf(("5cups_dnssd_query_cb(sdRef=%p, flags=%x, interfaceIndex=%d, errorCode=%d, fullName=\"%s\", rrtype=%u, rrclass=%u, rdlen=%u, rdata=%p, ttl=%u, context=%p)", (void *)sdRef, flags, interfaceIndex, errorCode, fullName, rrtype, rrclass, rdlen, rdata, ttl, context));
657c5b5f 2937
a29fd7dd 2938 /*
9554d4e7 2939 * Only process "add" data...
a29fd7dd
MS
2940 */
2941
9554d4e7
MS
2942 if (errorCode != kDNSServiceErr_NoError || !(flags & kDNSServiceFlagsAdd))
2943 return;
dcb445bc 2944
a29fd7dd 2945# else /* HAVE_AVAHI */
9554d4e7 2946 DEBUG_printf(("cups_dnssd_query_cb(browser=%p, interfaceIndex=%d, protocol=%d, event=%d, fullName=\"%s\", rrclass=%u, rrtype=%u, rdata=%p, rdlen=%u, flags=%x, context=%p)", browser, interfaceIndex, protocol, event, fullName, rrclass, rrtype, rdata, (unsigned)rdlen, flags, context));
dcb445bc 2947
9554d4e7
MS
2948 /*
2949 * Only process "add" data...
2950 */
dcb445bc 2951
9554d4e7
MS
2952 if (event != AVAHI_BROWSER_NEW)
2953 {
2954 if (event == AVAHI_BROWSER_FAILURE)
2955 DEBUG_printf(("cups_dnssd_query_cb: %s", avahi_strerror(avahi_client_errno(client))));
dcb445bc 2956
9554d4e7
MS
2957 return;
2958 }
2959# endif /* HAVE_DNSSD */
dcb445bc
MS
2960
2961 /*
9554d4e7 2962 * Lookup the service in the devices array.
dcb445bc
MS
2963 */
2964
9554d4e7
MS
2965 cups_dnssd_unquote(serviceName, fullName, sizeof(serviceName));
2966
2967 if ((ptr = strstr(serviceName, "._")) != NULL)
2968 *ptr = '\0';
2969
efa72f61 2970 cups_queue_name(name, serviceName, sizeof(name));
a2187a63 2971
9554d4e7 2972 dkey.dest.name = name;
dcb445bc 2973
9554d4e7 2974 if ((device = cupsArrayFind(data->devices, &dkey)) != NULL && device->state == _CUPS_DNSSD_NEW)
dcb445bc
MS
2975 {
2976 /*
9554d4e7 2977 * Found it, pull out the make and model from the TXT record and save it...
dcb445bc
MS
2978 */
2979
9554d4e7
MS
2980 const uint8_t *txt, /* Pointer into data */
2981 *txtnext, /* Next key/value pair */
2982 *txtend; /* End of entire TXT record */
2983 uint8_t txtlen; /* Length of current key/value pair */
2984 char key[256], /* Key string */
2985 value[256], /* Value string */
2986 make_and_model[512],
2987 /* Manufacturer and model */
2988 model[256], /* Model */
2989 uriname[1024], /* Name for URI */
2990 uri[1024]; /* Printer URI */
2991 cups_ptype_t type = CUPS_PRINTER_DISCOVERED | CUPS_PRINTER_BW;
2992 /* Printer type */
2993 int saw_printer_type = 0;
2994 /* Did we see a printer-type key? */
dcb445bc 2995
9554d4e7
MS
2996 device->state = _CUPS_DNSSD_PENDING;
2997 make_and_model[0] = '\0';
dcb445bc 2998
9554d4e7 2999 strlcpy(model, "Unknown", sizeof(model));
dcb445bc 3000
9554d4e7
MS
3001 for (txt = rdata, txtend = txt + rdlen;
3002 txt < txtend;
3003 txt = txtnext)
dcb445bc
MS
3004 {
3005 /*
9554d4e7
MS
3006 * Read a key/value pair starting with an 8-bit length. Since the
3007 * length is 8 bits and the size of the key/value buffers is 256, we
3008 * don't need to check for overflow...
dcb445bc
MS
3009 */
3010
9554d4e7 3011 txtlen = *txt++;
dcb445bc 3012
9554d4e7
MS
3013 if (!txtlen || (txt + txtlen) > txtend)
3014 break;
dcb445bc 3015
9554d4e7 3016 txtnext = txt + txtlen;
dcb445bc 3017
9554d4e7
MS
3018 for (ptr = key; txt < txtnext && *txt != '='; txt ++)
3019 *ptr++ = (char)*txt;
3020 *ptr = '\0';
dcb445bc 3021
9554d4e7
MS
3022 if (txt < txtnext && *txt == '=')
3023 {
3024 txt ++;
dcb445bc 3025
9554d4e7
MS
3026 if (txt < txtnext)
3027 memcpy(value, txt, (size_t)(txtnext - txt));
3028 value[txtnext - txt] = '\0';
a2187a63 3029
9554d4e7
MS
3030 DEBUG_printf(("6cups_dnssd_query_cb: %s=%s", key, value));
3031 }
3032 else
3033 {
3034 DEBUG_printf(("6cups_dnssd_query_cb: '%s' with no value.", key));
3035 continue;
3036 }
dcb445bc 3037
9554d4e7
MS
3038 if (!_cups_strcasecmp(key, "usb_MFG") ||
3039 !_cups_strcasecmp(key, "usb_MANU") ||
3040 !_cups_strcasecmp(key, "usb_MANUFACTURER"))
3041 strlcpy(make_and_model, value, sizeof(make_and_model));
3042 else if (!_cups_strcasecmp(key, "usb_MDL") ||
3043 !_cups_strcasecmp(key, "usb_MODEL"))
3044 strlcpy(model, value, sizeof(model));
3045 else if (!_cups_strcasecmp(key, "product") && !strstr(value, "Ghostscript"))
3046 {
3047 if (value[0] == '(')
3048 {
3049 /*
3050 * Strip parenthesis...
3051 */
dcb445bc 3052
9554d4e7
MS
3053 if ((ptr = value + strlen(value) - 1) > value && *ptr == ')')
3054 *ptr = '\0';
a29fd7dd 3055
9554d4e7
MS
3056 strlcpy(model, value + 1, sizeof(model));
3057 }
3058 else
3059 strlcpy(model, value, sizeof(model));
3060 }
3061 else if (!_cups_strcasecmp(key, "ty"))
3062 {
3063 strlcpy(model, value, sizeof(model));
dcb445bc 3064
9554d4e7
MS
3065 if ((ptr = strchr(model, ',')) != NULL)
3066 *ptr = '\0';
3067 }
3068 else if (!_cups_strcasecmp(key, "note"))
3069 device->dest.num_options = cupsAddOption("printer-location", value,
3070 device->dest.num_options,
3071 &device->dest.options);
3072 else if (!_cups_strcasecmp(key, "pdl"))
3073 {
3074 /*
3075 * Look for PDF-capable printers; only PDF-capable printers are shown.
3076 */
a29fd7dd 3077
9554d4e7
MS
3078 const char *start, *next; /* Pointer into value */
3079 int have_pdf = 0, /* Have PDF? */
3080 have_raster = 0;/* Have raster format support? */
dcb445bc 3081
9554d4e7
MS
3082 for (start = value; start && *start; start = next)
3083 {
3084 if (!_cups_strncasecmp(start, "application/pdf", 15) && (!start[15] || start[15] == ','))
3085 {
3086 have_pdf = 1;
3087 break;
3088 }
3089 else if ((!_cups_strncasecmp(start, "image/pwg-raster", 16) && (!start[16] || start[16] == ',')) ||
3090 (!_cups_strncasecmp(start, "image/urf", 9) && (!start[9] || start[9] == ',')))
3091 {
3092 have_raster = 1;
3093 break;
3094 }
a2187a63 3095
9554d4e7
MS
3096 if ((next = strchr(start, ',')) != NULL)
3097 next ++;
3098 }
dcb445bc 3099
9554d4e7
MS
3100 if (!have_pdf && !have_raster)
3101 device->state = _CUPS_DNSSD_INCOMPATIBLE;
3102 }
3103 else if (!_cups_strcasecmp(key, "printer-type"))
3104 {
3105 /*
3106 * Value is either NNNN or 0xXXXX
3107 */
3108
3109 saw_printer_type = 1;
3110 type = (cups_ptype_t)strtol(value, NULL, 0) | CUPS_PRINTER_DISCOVERED;
3111 }
3112 else if (!saw_printer_type)
3113 {
3114 if (!_cups_strcasecmp(key, "air") &&
3115 !_cups_strcasecmp(value, "t"))
3116 type |= CUPS_PRINTER_AUTHENTICATED;
3117 else if (!_cups_strcasecmp(key, "bind") &&
3118 !_cups_strcasecmp(value, "t"))
3119 type |= CUPS_PRINTER_BIND;
3120 else if (!_cups_strcasecmp(key, "collate") &&
3121 !_cups_strcasecmp(value, "t"))
3122 type |= CUPS_PRINTER_COLLATE;
3123 else if (!_cups_strcasecmp(key, "color") &&
3124 !_cups_strcasecmp(value, "t"))
3125 type |= CUPS_PRINTER_COLOR;
3126 else if (!_cups_strcasecmp(key, "copies") &&
3127 !_cups_strcasecmp(value, "t"))
3128 type |= CUPS_PRINTER_COPIES;
3129 else if (!_cups_strcasecmp(key, "duplex") &&
3130 !_cups_strcasecmp(value, "t"))
3131 type |= CUPS_PRINTER_DUPLEX;
3132 else if (!_cups_strcasecmp(key, "fax") &&
3133 !_cups_strcasecmp(value, "t"))
3134 type |= CUPS_PRINTER_MFP;
3135 else if (!_cups_strcasecmp(key, "papercustom") &&
3136 !_cups_strcasecmp(value, "t"))
3137 type |= CUPS_PRINTER_VARIABLE;
3138 else if (!_cups_strcasecmp(key, "papermax"))
3139 {
3140 if (!_cups_strcasecmp(value, "legal-a4"))
3141 type |= CUPS_PRINTER_SMALL;
3142 else if (!_cups_strcasecmp(value, "isoc-a2"))
3143 type |= CUPS_PRINTER_MEDIUM;
3144 else if (!_cups_strcasecmp(value, ">isoc-a2"))
3145 type |= CUPS_PRINTER_LARGE;
3146 }
3147 else if (!_cups_strcasecmp(key, "punch") &&
3148 !_cups_strcasecmp(value, "t"))
3149 type |= CUPS_PRINTER_PUNCH;
3150 else if (!_cups_strcasecmp(key, "scan") &&
3151 !_cups_strcasecmp(value, "t"))
3152 type |= CUPS_PRINTER_MFP;
3153 else if (!_cups_strcasecmp(key, "sort") &&
3154 !_cups_strcasecmp(value, "t"))
3155 type |= CUPS_PRINTER_SORT;
3156 else if (!_cups_strcasecmp(key, "staple") &&
3157 !_cups_strcasecmp(value, "t"))
3158 type |= CUPS_PRINTER_STAPLE;
3159 }
3160 }
3161
3162 /*
3163 * Save the printer-xxx values...
3164 */
3165
3166 if (make_and_model[0])
3167 {
3168 strlcat(make_and_model, " ", sizeof(make_and_model));
3169 strlcat(make_and_model, model, sizeof(make_and_model));
3170
3171 device->dest.num_options = cupsAddOption("printer-make-and-model", make_and_model, device->dest.num_options, &device->dest.options);
3172 }
3173 else
3174 device->dest.num_options = cupsAddOption("printer-make-and-model", model, device->dest.num_options, &device->dest.options);
3175
3176 device->type = type;
3177 snprintf(value, sizeof(value), "%u", type);
3178 device->dest.num_options = cupsAddOption("printer-type", value, device->dest.num_options, &device->dest.options);
3179
3180 /*
3181 * Save the URI...
3182 */
3183
3184 cups_dnssd_unquote(uriname, device->fullName, sizeof(uriname));
3185 httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri),
3186 !strcmp(device->regtype, "_ipps._tcp") ? "ipps" : "ipp",
3187 NULL, uriname, 0, saw_printer_type ? "/cups" : "/");
3188
3189 DEBUG_printf(("6cups_dnssd_query: device-uri=\"%s\"", uri));
3190
3191 device->dest.num_options = cupsAddOption("device-uri", uri, device->dest.num_options, &device->dest.options);
3192 }
3193 else
3194 DEBUG_printf(("6cups_dnssd_query: Ignoring TXT record for '%s'.",
3195 fullName));
dcb445bc
MS
3196}
3197
3198
3199/*
9554d4e7 3200 * 'cups_dnssd_resolve()' - Resolve a Bonjour printer URI.
dcb445bc
MS
3201 */
3202
9554d4e7
MS
3203static const char * /* O - Resolved URI or NULL */
3204cups_dnssd_resolve(
3205 cups_dest_t *dest, /* I - Destination */
3206 const char *uri, /* I - Current printer URI */
3207 int msec, /* I - Time in milliseconds */
3208 int *cancel, /* I - Pointer to "cancel" variable */
3209 cups_dest_cb_t cb, /* I - Callback */
3210 void *user_data) /* I - User data for callback */
dcb445bc 3211{
9554d4e7
MS
3212 char tempuri[1024]; /* Temporary URI buffer */
3213 _cups_dnssd_resolve_t resolve; /* Resolve data */
dcb445bc 3214
dcb445bc
MS
3215
3216 /*
9554d4e7 3217 * Resolve the URI...
dcb445bc
MS
3218 */
3219
9554d4e7
MS
3220 resolve.cancel = cancel;
3221 gettimeofday(&resolve.end_time, NULL);
3222 if (msec > 0)
3223 {
3224 resolve.end_time.tv_sec += msec / 1000;
3225 resolve.end_time.tv_usec += (msec % 1000) * 1000;
dcb445bc 3226
9554d4e7
MS
3227 while (resolve.end_time.tv_usec >= 1000000)
3228 {
3229 resolve.end_time.tv_sec ++;
3230 resolve.end_time.tv_usec -= 1000000;
3231 }
3232 }
3233 else
3234 resolve.end_time.tv_sec += 75;
dcb445bc 3235
9554d4e7
MS
3236 if (cb)
3237 (*cb)(user_data, CUPS_DEST_FLAGS_UNCONNECTED | CUPS_DEST_FLAGS_RESOLVING, dest);
dcb445bc 3238
9554d4e7
MS
3239 if ((uri = _httpResolveURI(uri, tempuri, sizeof(tempuri), _HTTP_RESOLVE_DEFAULT, cups_dnssd_resolve_cb, &resolve)) == NULL)
3240 {
3241 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to resolve printer-uri."), 1);
dcb445bc 3242
9554d4e7
MS
3243 if (cb)
3244 (*cb)(user_data, CUPS_DEST_FLAGS_UNCONNECTED | CUPS_DEST_FLAGS_ERROR, dest);
dcb445bc 3245
9554d4e7 3246 return (NULL);
dcb445bc
MS
3247 }
3248
9554d4e7
MS
3249 /*
3250 * Save the resolved URI...
3251 */
dcb445bc 3252
9554d4e7
MS
3253 dest->num_options = cupsAddOption("device-uri", uri, dest->num_options, &dest->options);
3254
3255 return (cupsGetOption("device-uri", dest->num_options, dest->options));
dcb445bc 3256}
a29fd7dd
MS
3257
3258
a29fd7dd 3259/*
9554d4e7 3260 * 'cups_dnssd_resolve_cb()' - See if we should continue resolving.
a29fd7dd
MS
3261 */
3262
9554d4e7
MS
3263static int /* O - 1 to continue, 0 to stop */
3264cups_dnssd_resolve_cb(void *context) /* I - Resolve data */
a29fd7dd 3265{
9554d4e7
MS
3266 _cups_dnssd_resolve_t *resolve = (_cups_dnssd_resolve_t *)context;
3267 /* Resolve data */
3268 struct timeval curtime; /* Current time */
a29fd7dd 3269
657c5b5f 3270
9554d4e7
MS
3271 /*
3272 * If the cancel variable is set, return immediately.
3273 */
a29fd7dd 3274
9554d4e7 3275 if (resolve->cancel && *(resolve->cancel))
657c5b5f 3276 {
9554d4e7
MS
3277 DEBUG_puts("4cups_dnssd_resolve_cb: Canceled.");
3278 return (0);
657c5b5f 3279 }
a29fd7dd 3280
9554d4e7
MS
3281 /*
3282 * Otherwise check the end time...
3283 */
3284
3285 gettimeofday(&curtime, NULL);
3286
3287 DEBUG_printf(("4cups_dnssd_resolve_cb: curtime=%d.%06d, end_time=%d.%06d", (int)curtime.tv_sec, (int)curtime.tv_usec, (int)resolve->end_time.tv_sec, (int)resolve->end_time.tv_usec));
3288
3289 return (curtime.tv_sec < resolve->end_time.tv_sec ||
3290 (curtime.tv_sec == resolve->end_time.tv_sec &&
3291 curtime.tv_usec < resolve->end_time.tv_usec));
a29fd7dd 3292}
dcb445bc
MS
3293
3294
3295/*
9554d4e7 3296 * 'cups_dnssd_unquote()' - Unquote a name string.
dcb445bc
MS
3297 */
3298
3299static void
9554d4e7
MS
3300cups_dnssd_unquote(char *dst, /* I - Destination buffer */
3301 const char *src, /* I - Source string */
3302 size_t dstsize) /* I - Size of destination buffer */
dcb445bc 3303{
9554d4e7
MS
3304 char *dstend = dst + dstsize - 1; /* End of destination buffer */
3305
3306
3307 while (*src && dst < dstend)
3308 {
3309 if (*src == '\\')
3310 {
3311 src ++;
3312 if (isdigit(src[0] & 255) && isdigit(src[1] & 255) &&
3313 isdigit(src[2] & 255))
3314 {
3315 *dst++ = ((((src[0] - '0') * 10) + src[1] - '0') * 10) + src[2] - '0';
3316 src += 3;
3317 }
3318 else
3319 *dst++ = *src++;
3320 }
3321 else
3322 *dst++ = *src ++;
3323 }
3324
3325 *dst = '\0';
3326}
3327#endif /* HAVE_DNSSD */
3328
3329
3330#if defined(HAVE_AVAHI) || defined(HAVE_DNSSD)
3331/*
3332 * 'cups_elapsed()' - Return the elapsed time in milliseconds.
3333 */
3334
3335static int /* O - Elapsed time in milliseconds */
3336cups_elapsed(struct timeval *t) /* IO - Previous time */
a29fd7dd 3337{
9554d4e7
MS
3338 int msecs; /* Milliseconds */
3339 struct timeval nt; /* New time */
dcb445bc
MS
3340
3341
9554d4e7 3342 gettimeofday(&nt, NULL);
dcb445bc 3343
9554d4e7 3344 msecs = (int)(1000 * (nt.tv_sec - t->tv_sec) + (nt.tv_usec - t->tv_usec) / 1000);
dcb445bc 3345
9554d4e7 3346 *t = nt;
dcb445bc 3347
9554d4e7
MS
3348 return (msecs);
3349}
3350#endif /* HAVE_AVAHI || HAVE_DNSSD */
3351
3352
3353/*
3354 * 'cups_enum_dests()' - Enumerate destinations from a specific server.
3355 */
3356
3357static int /* O - 1 on success, 0 on failure */
3358cups_enum_dests(
3359 http_t *http, /* I - Connection to scheduler */
3360 unsigned flags, /* I - Enumeration flags */
3361 int msec, /* I - Timeout in milliseconds, -1 for indefinite */
3362 int *cancel, /* I - Pointer to "cancel" variable */
3363 cups_ptype_t type, /* I - Printer type bits */
3364 cups_ptype_t mask, /* I - Mask for printer type bits */
3365 cups_dest_cb_t cb, /* I - Callback function */
3366 void *user_data) /* I - User data */
3367{
c5f5c5a7 3368 int i, j, /* Looping vars */
9554d4e7
MS
3369 num_dests; /* Number of destinations */
3370 cups_dest_t *dests = NULL, /* Destinations */
c5f5c5a7
MS
3371 *dest, /* Current destination */
3372 *user_dest; /* User destination */
3373 cups_option_t *option; /* Current option */
3374 char *user_default; /* User default printer */
9554d4e7
MS
3375#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
3376 int count, /* Number of queries started */
3377 completed, /* Number of completed queries */
3378 remaining; /* Remainder of timeout */
3379 struct timeval curtime; /* Current time */
c5f5c5a7 3380 _cups_dnssd_data_t data; /* Data for callback */
9554d4e7
MS
3381 _cups_dnssd_device_t *device; /* Current device */
3382# ifdef HAVE_DNSSD
3383 int nfds, /* Number of files responded */
3384 main_fd; /* File descriptor for lookups */
625bb9de 3385 DNSServiceRef ipp_ref = NULL; /* IPP browser */
9554d4e7 3386# ifdef HAVE_SSL
625bb9de 3387 DNSServiceRef ipps_ref = NULL; /* IPPS browser */
9554d4e7
MS
3388# endif /* HAVE_SSL */
3389# ifdef HAVE_POLL
3390 struct pollfd pfd; /* Polling data */
3391# else
3392 fd_set input; /* Input set for select() */
3393 struct timeval timeout; /* Timeout for select() */
3394# endif /* HAVE_POLL */
a29fd7dd 3395# else /* HAVE_AVAHI */
9554d4e7
MS
3396 int error; /* Error value */
3397 AvahiServiceBrowser *ipp_ref = NULL; /* IPP browser */
3398# ifdef HAVE_SSL
3399 AvahiServiceBrowser *ipps_ref = NULL; /* IPPS browser */
3400# endif /* HAVE_SSL */
3401# endif /* HAVE_DNSSD */
c5f5c5a7
MS
3402#else
3403 _cups_getdata_t data; /* Data for callback */
9554d4e7 3404#endif /* HAVE_DNSSD || HAVE_AVAHI */
c5f5c5a7
MS
3405 const char *home; /* HOME environment variable */
3406 char filename[1024]; /* Local lpoptions file */
3407 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
9554d4e7
MS
3408
3409
3410 DEBUG_printf(("cups_enum_dests(flags=%x, msec=%d, cancel=%p, type=%x, mask=%x, cb=%p, user_data=%p)", flags, msec, (void *)cancel, type, mask, (void *)cb, (void *)user_data));
a29fd7dd
MS
3411
3412 /*
9554d4e7 3413 * Range check input...
a29fd7dd
MS
3414 */
3415
9554d4e7 3416 (void)flags;
a29fd7dd 3417
9554d4e7
MS
3418 if (!cb)
3419 {
3420 DEBUG_puts("1cups_enum_dests: No callback, returning 0.");
3421 return (0);
a29fd7dd 3422 }
a29fd7dd 3423
dcb445bc 3424 /*
c5f5c5a7 3425 * Load the /etc/cups/lpoptions and ~/.cups/lpoptions files...
dcb445bc
MS
3426 */
3427
9554d4e7 3428 memset(&data, 0, sizeof(data));
a2187a63 3429
c5f5c5a7
MS
3430 if ((user_default = _cupsUserDefault(data.def_name, sizeof(data.def_name))) == NULL)
3431 {
3432 const char *defprinter = cupsGetDefault2(http);
3433 /* Server default, if any */
3434
3435 if (defprinter)
3436 strlcpy(data.def_name, defprinter, sizeof(data.def_name));
3437 }
3438
3439 if (data.def_name[0])
3440 {
3441 /*
3442 * Separate printer and instance name...
3443 */
3444
3445 if ((data.def_instance = strchr(data.def_name, '/')) != NULL)
3446 *data.def_instance++ = '\0';
3447 }
3448
3449 snprintf(filename, sizeof(filename), "%s/lpoptions", cg->cups_serverroot);
3450 data.num_dests = cups_get_dests(filename, NULL, NULL, 1, user_default != NULL, data.num_dests, &data.dests);
3451
3452 if ((home = getenv("HOME")) != NULL)
3453 {
3454 snprintf(filename, sizeof(filename), "%s/.cups/lpoptions", home);
3455
3456 data.num_dests = cups_get_dests(filename, NULL, NULL, 1, user_default != NULL, data.num_dests, &data.dests);
3457 }
3458
3459 /*
3460 * Get ready to enumerate...
3461 */
3462
3463#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
9554d4e7
MS
3464 data.type = type;
3465 data.mask = mask;
3466 data.cb = cb;
3467 data.user_data = user_data;
3468 data.devices = cupsArrayNew3((cups_array_func_t)cups_dnssd_compare_devices, NULL, NULL, 0, NULL, (cups_afree_func_t)cups_dnssd_free_device);
3469#endif /* HAVE_DNSSD || HAVE_AVAHI */
a2187a63 3470
9554d4e7 3471 if (!(mask & CUPS_PRINTER_DISCOVERED) || !(type & CUPS_PRINTER_DISCOVERED))
dcb445bc
MS
3472 {
3473 /*
9554d4e7 3474 * Get the list of local printers and pass them to the callback function...
dcb445bc
MS
3475 */
3476
9554d4e7 3477 num_dests = _cupsGetDests(http, IPP_OP_CUPS_GET_PRINTERS, NULL, &dests, type, mask);
dcb445bc 3478
c5f5c5a7 3479 if (data.def_name[0])
dcb445bc
MS
3480 {
3481 /*
c5f5c5a7 3482 * Lookup the named default printer and instance and make it the default...
9554d4e7 3483 */
dcb445bc 3484
c5f5c5a7 3485 if ((dest = cupsGetDest(data.def_name, data.def_instance, num_dests, dests)) != NULL)
9554d4e7
MS
3486 dest->is_default = 1;
3487 }
dcb445bc 3488
9554d4e7
MS
3489 for (i = num_dests, dest = dests;
3490 i > 0 && (!cancel || !*cancel);
3491 i --, dest ++)
3492 {
3493#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
3494 const char *device_uri; /* Device URI */
3495#endif /* HAVE_DNSSD || HAVE_AVAHI */
dcb445bc 3496
c5f5c5a7
MS
3497 if ((user_dest = cupsGetDest(dest->name, dest->instance, data.num_dests, data.dests)) != NULL)
3498 {
3499 /*
3500 * Apply user defaults to this destination...
3501 */
3502
3503 for (j = user_dest->num_options, option = user_dest->options; j > 0; j --, option ++)
3504 dest->num_options = cupsAddOption(option->name, option->value, dest->num_options, &dest->options);
3505 }
3506
9554d4e7
MS
3507 if (!(*cb)(user_data, i > 1 ? CUPS_DEST_FLAGS_MORE : CUPS_DEST_FLAGS_NONE, dest))
3508 break;
dcb445bc 3509
9554d4e7
MS
3510#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
3511 if (!dest->instance && (device_uri = cupsGetOption("device-uri", dest->num_options, dest->options)) != NULL && !strncmp(device_uri, "dnssd://", 8))
a29fd7dd
MS
3512 {
3513 /*
9554d4e7 3514 * Add existing queue using service name, etc. so we don't list it again...
a29fd7dd
MS
3515 */
3516
9554d4e7
MS
3517 char scheme[32], /* URI scheme */
3518 userpass[32], /* Username:password */
3519 serviceName[256], /* Service name (host field) */
3520 resource[256], /* Resource (options) */
3521 *regtype, /* Registration type */
3522 *replyDomain; /* Registration domain */
3523 int port; /* Port number (not used) */
a29fd7dd 3524
9554d4e7 3525 if (httpSeparateURI(HTTP_URI_CODING_ALL, device_uri, scheme, sizeof(scheme), userpass, sizeof(userpass), serviceName, sizeof(serviceName), &port, resource, sizeof(resource)) >= HTTP_URI_STATUS_OK)
a29fd7dd 3526 {
9554d4e7 3527 if ((regtype = strstr(serviceName, "._ipp")) != NULL)
46385a1a 3528 {
9554d4e7 3529 *regtype++ = '\0';
a29fd7dd 3530
9554d4e7
MS
3531 if ((replyDomain = strstr(regtype, "._tcp.")) != NULL)
3532 {
3533 replyDomain[5] = '\0';
3534 replyDomain += 6;
a29fd7dd 3535
9554d4e7
MS
3536 if ((device = cups_dnssd_get_device(&data, serviceName, regtype, replyDomain)) != NULL)
3537 device->state = _CUPS_DNSSD_ACTIVE;
3538 }
3539 }
3540 }
dcb445bc 3541 }
9554d4e7 3542#endif /* HAVE_DNSSD || HAVE_AVAHI */
dcb445bc
MS
3543 }
3544
9554d4e7 3545 cupsFreeDests(num_dests, dests);
dcb445bc 3546
9554d4e7
MS
3547 if (i > 0 || msec == 0)
3548 goto enum_finished;
3549 }
dcb445bc 3550
9554d4e7
MS
3551 /*
3552 * Return early if the caller doesn't want to do discovery...
3553 */
dcb445bc 3554
9554d4e7
MS
3555 if ((mask & CUPS_PRINTER_DISCOVERED) && !(type & CUPS_PRINTER_DISCOVERED))
3556 goto enum_finished;
a29fd7dd 3557
9554d4e7
MS
3558#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
3559 /*
3560 * Get Bonjour-shared printers...
3561 */
dcb445bc 3562
9554d4e7 3563 gettimeofday(&curtime, NULL);
dcb445bc 3564
9554d4e7
MS
3565# ifdef HAVE_DNSSD
3566 if (DNSServiceCreateConnection(&data.main_ref) != kDNSServiceErr_NoError)
3567 {
3568 DEBUG_puts("1cups_enum_dests: Unable to create service browser, returning 0.");
c5f5c5a7
MS
3569
3570 cupsFreeDests(data.num_dests, data.dests);
3571
9554d4e7
MS
3572 return (0);
3573 }
dcb445bc 3574
9554d4e7
MS
3575 main_fd = DNSServiceRefSockFD(data.main_ref);
3576
3577 ipp_ref = data.main_ref;
3578 if (DNSServiceBrowse(&ipp_ref, kDNSServiceFlagsShareConnection, 0, "_ipp._tcp", NULL, (DNSServiceBrowseReply)cups_dnssd_browse_cb, &data) != kDNSServiceErr_NoError)
3579 {
3580 DEBUG_puts("1cups_enum_dests: Unable to create IPP browser, returning 0.");
3581 DNSServiceRefDeallocate(data.main_ref);
c5f5c5a7
MS
3582
3583 cupsFreeDests(data.num_dests, data.dests);
3584
9554d4e7 3585 return (0);
dcb445bc 3586 }
dcb445bc 3587
9554d4e7
MS
3588# ifdef HAVE_SSL
3589 ipps_ref = data.main_ref;
3590 if (DNSServiceBrowse(&ipps_ref, kDNSServiceFlagsShareConnection, 0, "_ipps._tcp", NULL, (DNSServiceBrowseReply)cups_dnssd_browse_cb, &data) != kDNSServiceErr_NoError)
3591 {
3592 DEBUG_puts("1cups_enum_dests: Unable to create IPPS browser, returning 0.");
3593 DNSServiceRefDeallocate(data.main_ref);
c5f5c5a7
MS
3594
3595 cupsFreeDests(data.num_dests, data.dests);
3596
9554d4e7
MS
3597 return (0);
3598 }
9554d4e7
MS
3599# endif /* HAVE_SSL */
3600
3601# else /* HAVE_AVAHI */
3602 if ((data.simple_poll = avahi_simple_poll_new()) == NULL)
3603 {
3604 DEBUG_puts("1cups_enum_dests: Unable to create Avahi poll, returning 0.");
c5f5c5a7
MS
3605
3606 cupsFreeDests(data.num_dests, data.dests);
3607
9554d4e7
MS
3608 return (0);
3609 }
dcb445bc 3610
9554d4e7 3611 avahi_simple_poll_set_func(data.simple_poll, cups_dnssd_poll_cb, &data);
dcb445bc 3612
9554d4e7
MS
3613 data.client = avahi_client_new(avahi_simple_poll_get(data.simple_poll),
3614 0, cups_dnssd_client_cb, &data,
3615 &error);
3616 if (!data.client)
3617 {
3618 DEBUG_puts("1cups_enum_dests: Unable to create Avahi client, returning 0.");
3619 avahi_simple_poll_free(data.simple_poll);
c5f5c5a7
MS
3620
3621 cupsFreeDests(data.num_dests, data.dests);
3622
9554d4e7
MS
3623 return (0);
3624 }
dcb445bc 3625
9554d4e7
MS
3626 data.browsers = 1;
3627 if ((ipp_ref = avahi_service_browser_new(data.client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_ipp._tcp", NULL, 0, cups_dnssd_browse_cb, &data)) == NULL)
3628 {
3629 DEBUG_puts("1cups_enum_dests: Unable to create Avahi IPP browser, returning 0.");
dcb445bc 3630
9554d4e7
MS
3631 avahi_client_free(data.client);
3632 avahi_simple_poll_free(data.simple_poll);
c5f5c5a7
MS
3633
3634 cupsFreeDests(data.num_dests, data.dests);
3635
9554d4e7
MS
3636 return (0);
3637 }
dcb445bc 3638
9554d4e7
MS
3639# ifdef HAVE_SSL
3640 data.browsers ++;
3641 if ((ipps_ref = avahi_service_browser_new(data.client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_ipps._tcp", NULL, 0, cups_dnssd_browse_cb, &data)) == NULL)
dcb445bc 3642 {
9554d4e7 3643 DEBUG_puts("1cups_enum_dests: Unable to create Avahi IPPS browser, returning 0.");
dcb445bc 3644
9554d4e7
MS
3645 avahi_service_browser_free(ipp_ref);
3646 avahi_client_free(data.client);
3647 avahi_simple_poll_free(data.simple_poll);
c5f5c5a7
MS
3648
3649 cupsFreeDests(data.num_dests, data.dests);
3650
9554d4e7 3651 return (0);
dcb445bc 3652 }
9554d4e7
MS
3653# endif /* HAVE_SSL */
3654# endif /* HAVE_DNSSD */
dcb445bc 3655
9554d4e7
MS
3656 if (msec < 0)
3657 remaining = INT_MAX;
3658 else
3659 remaining = msec;
dcb445bc 3660
9554d4e7 3661 while (remaining > 0 && (!cancel || !*cancel))
dcb445bc 3662 {
9554d4e7
MS
3663 /*
3664 * Check for input...
3665 */
dcb445bc 3666
9554d4e7 3667 DEBUG_printf(("1cups_enum_dests: remaining=%d", remaining));
dcb445bc 3668
9554d4e7 3669 cups_elapsed(&curtime);
dcb445bc 3670
9554d4e7
MS
3671# ifdef HAVE_DNSSD
3672# ifdef HAVE_POLL
3673 pfd.fd = main_fd;
3674 pfd.events = POLLIN;
dcb445bc 3675
9554d4e7 3676 nfds = poll(&pfd, 1, remaining > _CUPS_DNSSD_MAXTIME ? _CUPS_DNSSD_MAXTIME : remaining);
dcb445bc 3677
9554d4e7
MS
3678# else
3679 FD_ZERO(&input);
3680 FD_SET(main_fd, &input);
dcb445bc 3681
9554d4e7
MS
3682 timeout.tv_sec = 0;
3683 timeout.tv_usec = 1000 * (remaining > _CUPS_DNSSD_MAXTIME ? _CUPS_DNSSD_MAXTIME : remaining);
dcb445bc 3684
9554d4e7
MS
3685 nfds = select(main_fd + 1, &input, NULL, NULL, &timeout);
3686# endif /* HAVE_POLL */
dcb445bc 3687
9554d4e7
MS
3688 if (nfds > 0)
3689 DNSServiceProcessResult(data.main_ref);
3690 else if (nfds < 0 && errno != EINTR && errno != EAGAIN)
3691 break;
dcb445bc 3692
9554d4e7
MS
3693# else /* HAVE_AVAHI */
3694 data.got_data = 0;
dcb445bc 3695
9554d4e7
MS
3696 if ((error = avahi_simple_poll_iterate(data.simple_poll, _CUPS_DNSSD_MAXTIME)) > 0)
3697 {
3698 /*
3699 * We've been told to exit the loop. Perhaps the connection to
3700 * Avahi failed.
3701 */
dcb445bc 3702
9554d4e7
MS
3703 break;
3704 }
dcb445bc 3705
9554d4e7
MS
3706 DEBUG_printf(("1cups_enum_dests: got_data=%d", data.got_data));
3707# endif /* HAVE_DNSSD */
dcb445bc 3708
9554d4e7 3709 remaining -= cups_elapsed(&curtime);
dcb445bc 3710
9554d4e7
MS
3711 for (device = (_cups_dnssd_device_t *)cupsArrayFirst(data.devices),
3712 count = 0, completed = 0;
3713 device;
3714 device = (_cups_dnssd_device_t *)cupsArrayNext(data.devices))
3715 {
3716 if (device->ref)
3717 count ++;
4db7fcee 3718
9554d4e7
MS
3719 if (device->state == _CUPS_DNSSD_ACTIVE)
3720 completed ++;
dcb445bc 3721
9554d4e7
MS
3722 if (!device->ref && device->state == _CUPS_DNSSD_NEW)
3723 {
3724 DEBUG_printf(("1cups_enum_dests: Querying '%s'.", device->fullName));
dcb445bc 3725
9554d4e7
MS
3726# ifdef HAVE_DNSSD
3727 device->ref = data.main_ref;
dcb445bc 3728
9554d4e7
MS
3729 if (DNSServiceQueryRecord(&(device->ref), kDNSServiceFlagsShareConnection, 0, device->fullName, kDNSServiceType_TXT, kDNSServiceClass_IN, (DNSServiceQueryRecordReply)cups_dnssd_query_cb, &data) == kDNSServiceErr_NoError)
3730 {
3731 count ++;
3732 }
3733 else
3734 {
3735 device->ref = 0;
3736 device->state = _CUPS_DNSSD_ERROR;
dcb445bc 3737
9554d4e7
MS
3738 DEBUG_puts("1cups_enum_dests: Query failed.");
3739 }
dcb445bc 3740
9554d4e7
MS
3741# else /* HAVE_AVAHI */
3742 if ((device->ref = avahi_record_browser_new(data.client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, device->fullName, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT, 0, cups_dnssd_query_cb, &data)) != NULL)
3743 {
3744 DEBUG_printf(("1cups_enum_dests: Query ref=%p", device->ref));
3745 count ++;
3746 }
3747 else
3748 {
3749 device->state = _CUPS_DNSSD_ERROR;
3750
3751 DEBUG_printf(("1cups_enum_dests: Query failed: %s", avahi_strerror(avahi_client_errno(data.client))));
3752 }
3753# endif /* HAVE_DNSSD */
3754 }
3755 else if (device->ref && device->state == _CUPS_DNSSD_PENDING)
dcb445bc 3756 {
9554d4e7
MS
3757 completed ++;
3758
3759 DEBUG_printf(("1cups_enum_dests: Query for \"%s\" is complete.", device->fullName));
3760
3761 if ((device->type & mask) == type)
3762 {
c5f5c5a7
MS
3763 dest = &device->dest;
3764
3765 if ((user_dest = cupsGetDest(dest->name, dest->instance, data.num_dests, data.dests)) != NULL)
3766 {
3767 /*
3768 * Apply user defaults to this destination...
3769 */
3770
3771 for (j = user_dest->num_options, option = user_dest->options; j > 0; j --, option ++)
3772 dest->num_options = cupsAddOption(option->name, option->value, dest->num_options, &dest->options);
3773 }
3774
3775 if (!strcasecmp(dest->name, data.def_name) && !data.def_instance)
3776 dest->is_default = 1;
3777
9554d4e7 3778 DEBUG_printf(("1cups_enum_dests: Add callback for \"%s\".", device->dest.name));
c5f5c5a7 3779 if (!(*cb)(user_data, CUPS_DEST_FLAGS_NONE, dest))
9554d4e7
MS
3780 {
3781 remaining = -1;
3782 break;
3783 }
3784 }
3785
3786 device->state = _CUPS_DNSSD_ACTIVE;
dcb445bc 3787 }
dcb445bc 3788 }
9554d4e7
MS
3789
3790# ifdef HAVE_AVAHI
3791 DEBUG_printf(("1cups_enum_dests: remaining=%d, browsers=%d, completed=%d, count=%d, devices count=%d", remaining, data.browsers, completed, count, cupsArrayCount(data.devices)));
3792
3793 if (data.browsers == 0 && completed == cupsArrayCount(data.devices))
3794 break;
3795# else
3796 DEBUG_printf(("1cups_enum_dests: remaining=%d, completed=%d, count=%d, devices count=%d", remaining, completed, count, cupsArrayCount(data.devices)));
3797
3798 if (completed == cupsArrayCount(data.devices))
3799 break;
3800# endif /* HAVE_AVAHI */
dcb445bc 3801 }
9554d4e7 3802#endif /* HAVE_DNSSD || HAVE_AVAHI */
dcb445bc 3803
9554d4e7
MS
3804 /*
3805 * Return...
3806 */
dcb445bc 3807
9554d4e7 3808 enum_finished:
dcb445bc 3809
c5f5c5a7
MS
3810 cupsFreeDests(data.num_dests, data.dests);
3811
9554d4e7
MS
3812#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
3813 cupsArrayDelete(data.devices);
3fae3b33 3814
9554d4e7
MS
3815# ifdef HAVE_DNSSD
3816 if (ipp_ref)
3817 DNSServiceRefDeallocate(ipp_ref);
3fae3b33 3818
9554d4e7
MS
3819# ifdef HAVE_SSL
3820 if (ipps_ref)
3821 DNSServiceRefDeallocate(ipps_ref);
9554d4e7 3822# endif /* HAVE_SSL */
3fae3b33 3823
9554d4e7
MS
3824 if (data.main_ref)
3825 DNSServiceRefDeallocate(data.main_ref);
3fae3b33 3826
9554d4e7
MS
3827# else /* HAVE_AVAHI */
3828 if (ipp_ref)
3829 avahi_service_browser_free(ipp_ref);
3830# ifdef HAVE_SSL
3831 if (ipps_ref)
3832 avahi_service_browser_free(ipps_ref);
3833# endif /* HAVE_SSL */
3fae3b33 3834
9554d4e7
MS
3835 if (data.client)
3836 avahi_client_free(data.client);
3837 if (data.simple_poll)
3838 avahi_simple_poll_free(data.simple_poll);
3839# endif /* HAVE_DNSSD */
3840#endif /* HAVE_DNSSD || HAVE_AVAHI */
3fae3b33 3841
9554d4e7
MS
3842 DEBUG_puts("1cups_enum_dests: Returning 1.");
3843
3844 return (1);
3fae3b33
MS
3845}
3846
798d6e29 3847
426c6a59
MS
3848/*
3849 * 'cups_find_dest()' - Find a destination using a binary search.
3850 */
3851
3852static int /* O - Index of match */
3853cups_find_dest(const char *name, /* I - Destination name */
3854 const char *instance, /* I - Instance or NULL */
3855 int num_dests, /* I - Number of destinations */
3856 cups_dest_t *dests, /* I - Destinations */
3857 int prev, /* I - Previous index */
3858 int *rdiff) /* O - Difference of match */
3859{
3860 int left, /* Low mark for binary search */
3861 right, /* High mark for binary search */
3862 current, /* Current index */
3863 diff; /* Result of comparison */
3864 cups_dest_t key; /* Search key */
3865
3866
3867 key.name = (char *)name;
3868 key.instance = (char *)instance;
3869
3870 if (prev >= 0)
3871 {
3872 /*
3873 * Start search on either side of previous...
3874 */
3875
3876 if ((diff = cups_compare_dests(&key, dests + prev)) == 0 ||
3877 (diff < 0 && prev == 0) ||
3878 (diff > 0 && prev == (num_dests - 1)))
3879 {
3880 *rdiff = diff;
3881 return (prev);
3882 }
3883 else if (diff < 0)
3884 {
3885 /*
3886 * Start with previous on right side...
3887 */
3888
3889 left = 0;
3890 right = prev;
3891 }
3892 else
3893 {
3894 /*
3895 * Start wih previous on left side...
3896 */
3897
3898 left = prev;
3899 right = num_dests - 1;
3900 }
3901 }
3902 else
3903 {
3904 /*
3905 * Start search in the middle...
3906 */
3907
3908 left = 0;
3909 right = num_dests - 1;
3910 }
3911
3912 do
3913 {
3914 current = (left + right) / 2;
3915 diff = cups_compare_dests(&key, dests + current);
3916
3917 if (diff == 0)
3918 break;
3919 else if (diff < 0)
3920 right = current;
3921 else
3922 left = current;
3923 }
3924 while ((right - left) > 1);
3925
3926 if (diff != 0)
3927 {
3928 /*
3929 * Check the last 1 or 2 elements...
3930 */
3931
3932 if ((diff = cups_compare_dests(&key, dests + left)) <= 0)
3933 current = left;
3934 else
3935 {
3936 diff = cups_compare_dests(&key, dests + right);
3937 current = right;
3938 }
3939 }
3940
3941 /*
3942 * Return the closest destination and the difference...
3943 */
3944
3945 *rdiff = diff;
3946
3947 return (current);
3948}
3949
3950
7536de1a
MS
3951/*
3952 * 'cups_get_cb()' - Collect enumerated destinations.
3953 */
3954
3955static int /* O - 1 to continue, 0 to stop */
3956cups_get_cb(_cups_getdata_t *data, /* I - Data from cupsGetDests */
3957 unsigned flags, /* I - Enumeration flags */
3958 cups_dest_t *dest) /* I - Destination */
3959{
3960 if (flags & CUPS_DEST_FLAGS_REMOVED)
3961 {
3962 /*
3963 * Remove destination from array...
3964 */
3965
3966 data->num_dests = cupsRemoveDest(dest->name, dest->instance, data->num_dests, &data->dests);
3967 }
3968 else
3969 {
3970 /*
3971 * Add destination to array...
3972 */
3973
3974 data->num_dests = cupsCopyDest(dest, data->num_dests, &data->dests);
3975 }
3976
3977 return (1);
3978}
3979
3980
a4924f6c
MS
3981/*
3982 * 'cups_get_default()' - Get the default destination from an lpoptions file.
3983 */
3984
080811b1 3985static char * /* O - Default destination or NULL */
a4924f6c
MS
3986cups_get_default(const char *filename, /* I - File to read */
3987 char *namebuf, /* I - Name buffer */
3988 size_t namesize, /* I - Size of name buffer */
3989 const char **instance) /* I - Instance */
3990{
3991 cups_file_t *fp; /* lpoptions file */
3992 char line[8192], /* Line from file */
3993 *value, /* Value for line */
3994 *nameptr; /* Pointer into name */
88f9aafc 3995 int linenum; /* Current line */
a4924f6c
MS
3996
3997
3998 *namebuf = '\0';
3999
4000 if ((fp = cupsFileOpen(filename, "r")) != NULL)
4001 {
4002 linenum = 0;
4003
4004 while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
4005 {
88f9aafc 4006 if (!_cups_strcasecmp(line, "default") && value)
a4924f6c
MS
4007 {
4008 strlcpy(namebuf, value, namesize);
4009
4010 if ((nameptr = strchr(namebuf, ' ')) != NULL)
4011 *nameptr = '\0';
4012 if ((nameptr = strchr(namebuf, '\t')) != NULL)
4013 *nameptr = '\0';
4014
4015 if ((nameptr = strchr(namebuf, '/')) != NULL)
4016 *nameptr++ = '\0';
4017
4018 *instance = nameptr;
4019 break;
4020 }
4021 }
4022
4023 cupsFileClose(fp);
4024 }
4025
4026 return (*namebuf ? namebuf : NULL);
4027}
4028
4029
ef416fc2 4030/*
4031 * 'cups_get_dests()' - Get destinations from a file.
4032 */
4033
4034static int /* O - Number of destinations */
38e73f87
MS
4035cups_get_dests(
4036 const char *filename, /* I - File to read from */
4037 const char *match_name, /* I - Destination name we want */
4038 const char *match_inst, /* I - Instance name we want */
c5f5c5a7 4039 int load_all, /* I - Load all saved destinations? */
38e73f87
MS
4040 int user_default_set, /* I - User default printer set? */
4041 int num_dests, /* I - Number of destinations */
4042 cups_dest_t **dests) /* IO - Destinations */
ef416fc2 4043{
4044 int i; /* Looping var */
4045 cups_dest_t *dest; /* Current destination */
a4924f6c 4046 cups_file_t *fp; /* File pointer */
ef416fc2 4047 char line[8192], /* Line from file */
4048 *lineptr, /* Pointer into line */
4049 *name, /* Name of destination/option */
4050 *instance; /* Instance of destination */
a4924f6c 4051 int linenum; /* Current line number */
ef416fc2 4052
4053
c5f5c5a7 4054 DEBUG_printf(("7cups_get_dests(filename=\"%s\", match_name=\"%s\", match_inst=\"%s\", load_all=%d, user_default_set=%d, num_dests=%d, dests=%p)", filename, match_name, match_inst, load_all, user_default_set, num_dests, (void *)dests));
a4924f6c
MS
4055
4056 /*
4057 * Try to open the file...
4058 */
4059
4060 if ((fp = cupsFileOpen(filename, "r")) == NULL)
4061 return (num_dests);
4062
ef416fc2 4063 /*
4064 * Read each printer; each line looks like:
4065 *
4066 * Dest name[/instance] options
4067 * Default name[/instance] options
4068 */
4069
a4924f6c
MS
4070 linenum = 0;
4071
4072 while (cupsFileGetConf(fp, line, sizeof(line), &lineptr, &linenum))
ef416fc2 4073 {
4074 /*
4075 * See what type of line it is...
4076 */
4077
e07d4801 4078 DEBUG_printf(("9cups_get_dests: linenum=%d line=\"%s\" lineptr=\"%s\"",
38e73f87 4079 linenum, line, lineptr));
ef416fc2 4080
88f9aafc 4081 if ((_cups_strcasecmp(line, "dest") && _cups_strcasecmp(line, "default")) || !lineptr)
a4924f6c 4082 {
e07d4801 4083 DEBUG_puts("9cups_get_dests: Not a dest or default line...");
ef416fc2 4084 continue;
a4924f6c 4085 }
ef416fc2 4086
4087 name = lineptr;
4088
4089 /*
4090 * Search for an instance...
4091 */
4092
4093 while (!isspace(*lineptr & 255) && *lineptr && *lineptr != '/')
4094 lineptr ++;
4095
ef416fc2 4096 if (*lineptr == '/')
4097 {
4098 /*
4099 * Found an instance...
4100 */
4101
4102 *lineptr++ = '\0';
4103 instance = lineptr;
4104
4105 /*
4106 * Search for an instance...
4107 */
4108
4109 while (!isspace(*lineptr & 255) && *lineptr)
4110 lineptr ++;
4111 }
4112 else
4113 instance = NULL;
4114
a4924f6c
MS
4115 if (*lineptr)
4116 *lineptr++ = '\0';
4117
e07d4801 4118 DEBUG_printf(("9cups_get_dests: name=\"%s\", instance=\"%s\"", name,
a4924f6c 4119 instance));
ef416fc2 4120
4121 /*
c5f5c5a7 4122 * Match and/or ignore missing destinations...
ef416fc2 4123 */
4124
a4924f6c
MS
4125 if (match_name)
4126 {
88f9aafc 4127 if (_cups_strcasecmp(name, match_name) ||
a4924f6c
MS
4128 (!instance && match_inst) ||
4129 (instance && !match_inst) ||
88f9aafc 4130 (instance && _cups_strcasecmp(instance, match_inst)))
a4924f6c 4131 continue;
ef416fc2 4132
a4924f6c
MS
4133 dest = *dests;
4134 }
c5f5c5a7 4135 else if (!load_all && cupsGetDest(name, NULL, num_dests, *dests) == NULL)
a4924f6c 4136 {
e07d4801 4137 DEBUG_puts("9cups_get_dests: Not found!");
a4924f6c
MS
4138 continue;
4139 }
4140 else
ef416fc2 4141 {
4142 /*
a4924f6c 4143 * Add the destination...
ef416fc2 4144 */
4145
a4924f6c
MS
4146 num_dests = cupsAddDest(name, instance, num_dests, dests);
4147
4148 if ((dest = cupsGetDest(name, instance, num_dests, *dests)) == NULL)
4149 {
4150 /*
4151 * Out of memory!
4152 */
4153
e07d4801 4154 DEBUG_puts("9cups_get_dests: Out of memory!");
a4924f6c
MS
4155 break;
4156 }
ef416fc2 4157 }
4158
4159 /*
4160 * Add options until we hit the end of the line...
4161 */
4162
c5f5c5a7 4163 dest->num_options = cupsParseOptions(lineptr, dest->num_options, &(dest->options));
ef416fc2 4164
a4924f6c
MS
4165 /*
4166 * If we found what we were looking for, stop now...
4167 */
4168
4169 if (match_name)
4170 break;
4171
ef416fc2 4172 /*
4173 * Set this as default if needed...
4174 */
4175
88f9aafc 4176 if (!user_default_set && !_cups_strcasecmp(line, "default"))
ef416fc2 4177 {
e07d4801 4178 DEBUG_puts("9cups_get_dests: Setting as default...");
a4924f6c 4179
ef416fc2 4180 for (i = 0; i < num_dests; i ++)
4181 (*dests)[i].is_default = 0;
4182
4183 dest->is_default = 1;
4184 }
4185 }
4186
4187 /*
4188 * Close the file and return...
4189 */
4190
88f9aafc 4191 cupsFileClose(fp);
ef416fc2 4192
4193 return (num_dests);
4194}
4195
4196
426c6a59
MS
4197/*
4198 * 'cups_make_string()' - Make a comma-separated string of values from an IPP
4199 * attribute.
4200 */
4201
4202static char * /* O - New string */
4203cups_make_string(
4204 ipp_attribute_t *attr, /* I - Attribute to convert */
4205 char *buffer, /* I - Buffer */
4206 size_t bufsize) /* I - Size of buffer */
4207{
4208 int i; /* Looping var */
4209 char *ptr, /* Pointer into buffer */
4210 *end, /* Pointer to end of buffer */
4211 *valptr; /* Pointer into string attribute */
4212
4213
4214 /*
4215 * Return quickly if we have a single string value...
4216 */
4217
4218 if (attr->num_values == 1 &&
4219 attr->value_tag != IPP_TAG_INTEGER &&
4220 attr->value_tag != IPP_TAG_ENUM &&
4221 attr->value_tag != IPP_TAG_BOOLEAN &&
4222 attr->value_tag != IPP_TAG_RANGE)
4223 return (attr->values[0].string.text);
4224
4225 /*
4226 * Copy the values to the string, separating with commas and escaping strings
4227 * as needed...
4228 */
4229
4230 end = buffer + bufsize - 1;
4231
4232 for (i = 0, ptr = buffer; i < attr->num_values && ptr < end; i ++)
4233 {
4234 if (i)
4235 *ptr++ = ',';
4236
4237 switch (attr->value_tag)
4238 {
4239 case IPP_TAG_INTEGER :
4240 case IPP_TAG_ENUM :
07623986 4241 snprintf(ptr, (size_t)(end - ptr + 1), "%d", attr->values[i].integer);
426c6a59
MS
4242 break;
4243
4244 case IPP_TAG_BOOLEAN :
4245 if (attr->values[i].boolean)
07623986 4246 strlcpy(ptr, "true", (size_t)(end - ptr + 1));
426c6a59 4247 else
07623986 4248 strlcpy(ptr, "false", (size_t)(end - ptr + 1));
426c6a59
MS
4249 break;
4250
4251 case IPP_TAG_RANGE :
4252 if (attr->values[i].range.lower == attr->values[i].range.upper)
07623986 4253 snprintf(ptr, (size_t)(end - ptr + 1), "%d", attr->values[i].range.lower);
426c6a59 4254 else
07623986 4255 snprintf(ptr, (size_t)(end - ptr + 1), "%d-%d", attr->values[i].range.lower, attr->values[i].range.upper);
426c6a59
MS
4256 break;
4257
4258 default :
4259 for (valptr = attr->values[i].string.text;
4260 *valptr && ptr < end;)
4261 {
4262 if (strchr(" \t\n\\\'\"", *valptr))
4263 {
4264 if (ptr >= (end - 1))
4265 break;
4266
4267 *ptr++ = '\\';
4268 }
4269
4270 *ptr++ = *valptr++;
4271 }
4272
4273 *ptr = '\0';
4274 break;
4275 }
4276
4277 ptr += strlen(ptr);
4278 }
4279
4280 *ptr = '\0';
4281
4282 return (buffer);
4283}
efa72f61
MS
4284
4285
4a366251
MS
4286/*
4287 * 'cups_name_cb()' - Find an enumerated destination.
4288 */
4289
4290static int /* O - 1 to continue, 0 to stop */
4291cups_name_cb(_cups_namedata_t *data, /* I - Data from cupsGetNamedDest */
4292 unsigned flags, /* I - Enumeration flags */
4293 cups_dest_t *dest) /* I - Destination */
4294{
2a75f21b 4295 DEBUG_printf(("2cups_name_cb(data=%p(%s), flags=%x, dest=%p(%s)", (void *)data, data->name, flags, (void *)dest, dest->name));
4a366251
MS
4296
4297 if (!(flags & CUPS_DEST_FLAGS_REMOVED) && !dest->instance && !strcasecmp(data->name, dest->name))
4298 {
4299 /*
4300 * Copy destination and stop enumeration...
4301 */
4302
4303 cupsCopyDest(dest, 0, &data->dest);
4304 return (0);
4305 }
4306
4307 return (1);
4308}
4309
4310
efa72f61
MS
4311/*
4312 * 'cups_queue_name()' - Create a local queue name based on the service name.
4313 */
4314
4315static void
4316cups_queue_name(
4317 char *name, /* I - Name buffer */
4318 const char *serviceName, /* I - Service name */
4319 size_t namesize) /* I - Size of name buffer */
4320{
4321 const char *ptr; /* Pointer into serviceName */
4322 char *nameptr; /* Pointer into name */
4323
4324
4325 for (nameptr = name, ptr = serviceName; *ptr && nameptr < (name + namesize - 1); ptr ++)
4326 {
4327 /*
4328 * Sanitize the printer name...
4329 */
4330
4331 if (_cups_isalnum(*ptr))
4332 *nameptr++ = *ptr;
4333 else if (nameptr == name || nameptr[-1] != '_')
4334 *nameptr++ = '_';
4335 }
4336
4337 *nameptr = '\0';
4338}