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