Coverage Report

Created: 2022-10-31 07:00

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