1 /* 2 * Destination option/media support for CUPS. 3 * 4 * Copyright 2012-2017 by Apple Inc. 5 * 6 * These coded instructions, statements, and computer programs are the 7 * property of Apple Inc. and are protected by Federal copyright 8 * law. Distribution and use rights are outlined in the file "LICENSE.txt" 9 * which should have been included with this file. If this file is 10 * missing or damaged, see the license at "http://www.cups.org/". 11 * 12 * This file is subject to the Apple OS-Developed Software exception. 13 */ 14 15 /* 16 * Include necessary headers... 17 */ 18 19 #include "cups-private.h" 20 21 22 /* 23 * Local constants... 24 */ 25 26 #define _CUPS_MEDIA_READY_TTL 30 /* Life of xxx-ready values */ 27 28 29 /* 30 * Local functions... 31 */ 32 33 static void cups_add_dconstres(cups_array_t *a, ipp_t *collection); 34 static int cups_compare_dconstres(_cups_dconstres_t *a, 35 _cups_dconstres_t *b); 36 static int cups_compare_media_db(_cups_media_db_t *a, 37 _cups_media_db_t *b); 38 static _cups_media_db_t *cups_copy_media_db(_cups_media_db_t *mdb); 39 static void cups_create_cached(http_t *http, cups_dinfo_t *dinfo, 40 unsigned flags); 41 static void cups_create_constraints(cups_dinfo_t *dinfo); 42 static void cups_create_defaults(cups_dinfo_t *dinfo); 43 static void cups_create_media_db(cups_dinfo_t *dinfo, 44 unsigned flags); 45 static void cups_free_media_db(_cups_media_db_t *mdb); 46 static int cups_get_media_db(http_t *http, cups_dinfo_t *dinfo, 47 pwg_media_t *pwg, unsigned flags, 48 cups_size_t *size); 49 static int cups_is_close_media_db(_cups_media_db_t *a, 50 _cups_media_db_t *b); 51 static cups_array_t *cups_test_constraints(cups_dinfo_t *dinfo, 52 const char *new_option, 53 const char *new_value, 54 int num_options, 55 cups_option_t *options, 56 int *num_conflicts, 57 cups_option_t **conflicts); 58 static void cups_update_ready(http_t *http, cups_dinfo_t *dinfo); 59 60 61 /* 62 * 'cupsCheckDestSupported()' - Check that the option and value are supported 63 * by the destination. 64 * 65 * Returns 1 if supported, 0 otherwise. 66 * 67 * @since CUPS 1.6/macOS 10.8@ 68 */ 69 70 int /* O - 1 if supported, 0 otherwise */ 71 cupsCheckDestSupported( 72 http_t *http, /* I - Connection to destination */ 73 cups_dest_t *dest, /* I - Destination */ 74 cups_dinfo_t *dinfo, /* I - Destination information */ 75 const char *option, /* I - Option */ 76 const char *value) /* I - Value or @code NULL@ */ 77 { 78 int i; /* Looping var */ 79 char temp[1024]; /* Temporary string */ 80 int int_value; /* Integer value */ 81 int xres_value, /* Horizontal resolution */ 82 yres_value; /* Vertical resolution */ 83 ipp_res_t units_value; /* Resolution units */ 84 ipp_attribute_t *attr; /* Attribute */ 85 _ipp_value_t *attrval; /* Current attribute value */ 86 87 88 /* 89 * Get the default connection as needed... 90 */ 91 92 if (!http) 93 http = _cupsConnect(); 94 95 /* 96 * Range check input... 97 */ 98 99 if (!http || !dest || !dinfo || !option) 100 return (0); 101 102 /* 103 * Lookup the attribute... 104 */ 105 106 if (strstr(option, "-supported")) 107 attr = ippFindAttribute(dinfo->attrs, option, IPP_TAG_ZERO); 108 else 109 { 110 snprintf(temp, sizeof(temp), "%s-supported", option); 111 attr = ippFindAttribute(dinfo->attrs, temp, IPP_TAG_ZERO); 112 } 113 114 if (!attr) 115 return (0); 116 117 if (!value) 118 return (1); 119 120 /* 121 * Compare values... 122 */ 123 124 if (!strcmp(option, "media") && !strncmp(value, "custom_", 7)) 125 { 126 /* 127 * Check range of custom media sizes... 128 */ 129 130 pwg_media_t *pwg; /* Current PWG media size info */ 131 int min_width, /* Minimum width */ 132 min_length, /* Minimum length */ 133 max_width, /* Maximum width */ 134 max_length; /* Maximum length */ 135 136 /* 137 * Get the minimum and maximum size... 138 */ 139 140 min_width = min_length = INT_MAX; 141 max_width = max_length = 0; 142 143 for (i = attr->num_values, attrval = attr->values; 144 i > 0; 145 i --, attrval ++) 146 { 147 if (!strncmp(attrval->string.text, "custom_min_", 11) && 148 (pwg = pwgMediaForPWG(attrval->string.text)) != NULL) 149 { 150 min_width = pwg->width; 151 min_length = pwg->length; 152 } 153 else if (!strncmp(attrval->string.text, "custom_max_", 11) && 154 (pwg = pwgMediaForPWG(attrval->string.text)) != NULL) 155 { 156 max_width = pwg->width; 157 max_length = pwg->length; 158 } 159 } 160 161 /* 162 * Check the range... 163 */ 164 165 if (min_width < INT_MAX && max_width > 0 && 166 (pwg = pwgMediaForPWG(value)) != NULL && 167 pwg->width >= min_width && pwg->width <= max_width && 168 pwg->length >= min_length && pwg->length <= max_length) 169 return (1); 170 } 171 else 172 { 173 /* 174 * Check literal values... 175 */ 176 177 switch (attr->value_tag) 178 { 179 case IPP_TAG_INTEGER : 180 case IPP_TAG_ENUM : 181 int_value = atoi(value); 182 183 for (i = 0; i < attr->num_values; i ++) 184 if (attr->values[i].integer == int_value) 185 return (1); 186 break; 187 188 case IPP_TAG_BOOLEAN : 189 return (attr->values[0].boolean); 190 191 case IPP_TAG_RANGE : 192 int_value = atoi(value); 193 194 for (i = 0; i < attr->num_values; i ++) 195 if (int_value >= attr->values[i].range.lower && 196 int_value <= attr->values[i].range.upper) 197 return (1); 198 break; 199 200 case IPP_TAG_RESOLUTION : 201 if (sscanf(value, "%dx%d%15s", &xres_value, &yres_value, temp) != 3) 202 { 203 if (sscanf(value, "%d%15s", &xres_value, temp) != 2) 204 return (0); 205 206 yres_value = xres_value; 207 } 208 209 if (!strcmp(temp, "dpi")) 210 units_value = IPP_RES_PER_INCH; 211 else if (!strcmp(temp, "dpc") || !strcmp(temp, "dpcm")) 212 units_value = IPP_RES_PER_CM; 213 else 214 return (0); 215 216 for (i = attr->num_values, attrval = attr->values; 217 i > 0; 218 i --, attrval ++) 219 { 220 if (attrval->resolution.xres == xres_value && 221 attrval->resolution.yres == yres_value && 222 attrval->resolution.units == units_value) 223 return (1); 224 } 225 break; 226 227 case IPP_TAG_TEXT : 228 case IPP_TAG_NAME : 229 case IPP_TAG_KEYWORD : 230 case IPP_TAG_CHARSET : 231 case IPP_TAG_URI : 232 case IPP_TAG_URISCHEME : 233 case IPP_TAG_MIMETYPE : 234 case IPP_TAG_LANGUAGE : 235 case IPP_TAG_TEXTLANG : 236 case IPP_TAG_NAMELANG : 237 for (i = 0; i < attr->num_values; i ++) 238 if (!strcmp(attr->values[i].string.text, value)) 239 return (1); 240 break; 241 242 default : 243 break; 244 } 245 } 246 247 /* 248 * If we get there the option+value is not supported... 249 */ 250 251 return (0); 252 } 253 254 255 /* 256 * 'cupsCopyDestConflicts()' - Get conflicts and resolutions for a new 257 * option/value pair. 258 * 259 * "num_options" and "options" represent the currently selected options by the 260 * user. "new_option" and "new_value" are the setting the user has just 261 * changed. 262 * 263 * Returns 1 if there is a conflict, 0 if there are no conflicts, and -1 if 264 * there was an unrecoverable error such as a resolver loop. 265 * 266 * If "num_conflicts" and "conflicts" are not @code NULL@, they are set to 267 * contain the list of conflicting option/value pairs. Similarly, if 268 * "num_resolved" and "resolved" are not @code NULL@ they will be set to the 269 * list of changes needed to resolve the conflict. 270 * 271 * If cupsCopyDestConflicts returns 1 but "num_resolved" and "resolved" are set 272 * to 0 and @code NULL@, respectively, then the conflict cannot be resolved. 273 * 274 * @since CUPS 1.6/macOS 10.8@ 275 */ 276 277 int /* O - 1 if there is a conflict, 0 if none, -1 on error */ 278 cupsCopyDestConflicts( 279 http_t *http, /* I - Connection to destination */ 280 cups_dest_t *dest, /* I - Destination */ 281 cups_dinfo_t *dinfo, /* I - Destination information */ 282 int num_options, /* I - Number of current options */ 283 cups_option_t *options, /* I - Current options */ 284 const char *new_option, /* I - New option */ 285 const char *new_value, /* I - New value */ 286 int *num_conflicts, /* O - Number of conflicting options */ 287 cups_option_t **conflicts, /* O - Conflicting options */ 288 int *num_resolved, /* O - Number of options to resolve */ 289 cups_option_t **resolved) /* O - Resolved options */ 290 { 291 int i, /* Looping var */ 292 have_conflicts = 0, /* Do we have conflicts? */ 293 changed, /* Did we change something? */ 294 tries, /* Number of tries for resolution */ 295 num_myconf = 0, /* My number of conflicting options */ 296 num_myres = 0; /* My number of resolved options */ 297 cups_option_t *myconf = NULL, /* My conflicting options */ 298 *myres = NULL, /* My resolved options */ 299 *myoption, /* My current option */ 300 *option; /* Current option */ 301 cups_array_t *active = NULL, /* Active conflicts */ 302 *pass = NULL, /* Resolvers for this pass */ 303 *resolvers = NULL, /* Resolvers we have used */ 304 *test; /* Test array for conflicts */ 305 _cups_dconstres_t *c, /* Current constraint */ 306 *r; /* Current resolver */ 307 ipp_attribute_t *attr; /* Current attribute */ 308 char value[2048]; /* Current attribute value as string */ 309 const char *myvalue; /* Current value of an option */ 310 311 312 /* 313 * Clear returned values... 314 */ 315 316 if (num_conflicts) 317 *num_conflicts = 0; 318 319 if (conflicts) 320 *conflicts = NULL; 321 322 if (num_resolved) 323 *num_resolved = 0; 324 325 if (resolved) 326 *resolved = NULL; 327 328 /* 329 * Get the default connection as needed... 330 */ 331 332 if (!http) 333 http = _cupsConnect(); 334 335 /* 336 * Range check input... 337 */ 338 339 if (!http || !dest || !dinfo || 340 (num_conflicts != NULL) != (conflicts != NULL) || 341 (num_resolved != NULL) != (resolved != NULL)) 342 return (0); 343 344 /* 345 * Load constraints as needed... 346 */ 347 348 if (!dinfo->constraints) 349 cups_create_constraints(dinfo); 350 351 if (cupsArrayCount(dinfo->constraints) == 0) 352 return (0); 353 354 if (!dinfo->num_defaults) 355 cups_create_defaults(dinfo); 356 357 /* 358 * If we are resolving, create a shadow array... 359 */ 360 361 if (num_resolved) 362 { 363 for (i = num_options, option = options; i > 0; i --, option ++) 364 num_myres = cupsAddOption(option->name, option->value, num_myres, &myres); 365 366 if (new_option && new_value) 367 num_myres = cupsAddOption(new_option, new_value, num_myres, &myres); 368 } 369 else 370 { 371 num_myres = num_options; 372 myres = options; 373 } 374 375 /* 376 * Check for any conflicts... 377 */ 378 379 if (num_resolved) 380 pass = cupsArrayNew((cups_array_func_t)cups_compare_dconstres, NULL); 381 382 for (tries = 0; tries < 100; tries ++) 383 { 384 /* 385 * Check for any conflicts... 386 */ 387 388 if (num_conflicts || num_resolved) 389 { 390 cupsFreeOptions(num_myconf, myconf); 391 392 num_myconf = 0; 393 myconf = NULL; 394 active = cups_test_constraints(dinfo, new_option, new_value, 395 num_myres, myres, &num_myconf, 396 &myconf); 397 } 398 else 399 active = cups_test_constraints(dinfo, new_option, new_value, num_myres, 400 myres, NULL, NULL); 401 402 have_conflicts = (active != NULL); 403 404 if (!active || !num_resolved) 405 break; /* All done */ 406 407 /* 408 * Scan the constraints that were triggered to apply resolvers... 409 */ 410 411 if (!resolvers) 412 resolvers = cupsArrayNew((cups_array_func_t)cups_compare_dconstres, NULL); 413 414 for (c = (_cups_dconstres_t *)cupsArrayFirst(active), changed = 0; 415 c; 416 c = (_cups_dconstres_t *)cupsArrayNext(active)) 417 { 418 if (cupsArrayFind(pass, c)) 419 continue; /* Already applied this resolver... */ 420 421 if (cupsArrayFind(resolvers, c)) 422 { 423 DEBUG_printf(("1cupsCopyDestConflicts: Resolver loop with %s.", 424 c->name)); 425 have_conflicts = -1; 426 goto cleanup; 427 } 428 429 if ((r = cupsArrayFind(dinfo->resolvers, c)) == NULL) 430 { 431 DEBUG_printf(("1cupsCopyDestConflicts: Resolver %s not found.", 432 c->name)); 433 have_conflicts = -1; 434 goto cleanup; 435 } 436 437 /* 438 * Add the options from the resolver... 439 */ 440 441 cupsArrayAdd(pass, r); 442 cupsArrayAdd(resolvers, r); 443 444 for (attr = ippFirstAttribute(r->collection); 445 attr; 446 attr = ippNextAttribute(r->collection)) 447 { 448 if (new_option && !strcmp(attr->name, new_option)) 449 continue; /* Ignore this if we just changed it */ 450 451 if (ippAttributeString(attr, value, sizeof(value)) >= sizeof(value)) 452 continue; /* Ignore if the value is too long */ 453 454 if ((test = cups_test_constraints(dinfo, attr->name, value, num_myres, 455 myres, NULL, NULL)) == NULL) 456 { 457 /* 458 * That worked, flag it... 459 */ 460 461 changed = 1; 462 } 463 else 464 cupsArrayDelete(test); 465 466 /* 467 * Add the option/value from the resolver regardless of whether it 468 * worked; this makes sure that we can cascade several changes to 469 * make things resolve... 470 */ 471 472 num_myres = cupsAddOption(attr->name, value, num_myres, &myres); 473 } 474 } 475 476 if (!changed) 477 { 478 DEBUG_puts("1cupsCopyDestConflicts: Unable to resolve constraints."); 479 have_conflicts = -1; 480 goto cleanup; 481 } 482 483 cupsArrayClear(pass); 484 485 cupsArrayDelete(active); 486 active = NULL; 487 } 488 489 if (tries >= 100) 490 { 491 DEBUG_puts("1cupsCopyDestConflicts: Unable to resolve after 100 tries."); 492 have_conflicts = -1; 493 goto cleanup; 494 } 495 496 /* 497 * Copy resolved options as needed... 498 */ 499 500 if (num_resolved) 501 { 502 for (i = num_myres, myoption = myres; i > 0; i --, myoption ++) 503 { 504 if ((myvalue = cupsGetOption(myoption->name, num_options, 505 options)) == NULL || 506 strcmp(myvalue, myoption->value)) 507 { 508 if (new_option && !strcmp(new_option, myoption->name) && 509 new_value && !strcmp(new_value, myoption->value)) 510 continue; 511 512 *num_resolved = cupsAddOption(myoption->name, myoption->value, 513 *num_resolved, resolved); 514 } 515 } 516 } 517 518 /* 519 * Clean up... 520 */ 521 522 cleanup: 523 524 cupsArrayDelete(active); 525 cupsArrayDelete(pass); 526 cupsArrayDelete(resolvers); 527 528 if (num_resolved) 529 { 530 /* 531 * Free shadow copy of options... 532 */ 533 534 cupsFreeOptions(num_myres, myres); 535 } 536 537 if (num_conflicts) 538 { 539 /* 540 * Return conflicting options to caller... 541 */ 542 543 *num_conflicts = num_myconf; 544 *conflicts = myconf; 545 } 546 else 547 { 548 /* 549 * Free conflicting options... 550 */ 551 552 cupsFreeOptions(num_myconf, myconf); 553 } 554 555 return (have_conflicts); 556 } 557 558 559 /* 560 * 'cupsCopyDestInfo()' - Get the supported values/capabilities for the 561 * destination. 562 * 563 * The caller is responsible for calling @link cupsFreeDestInfo@ on the return 564 * value. @code NULL@ is returned on error. 565 * 566 * @since CUPS 1.6/macOS 10.8@ 567 */ 568 569 cups_dinfo_t * /* O - Destination information */ 570 cupsCopyDestInfo( 571 http_t *http, /* I - Connection to destination */ 572 cups_dest_t *dest) /* I - Destination */ 573 { 574 cups_dinfo_t *dinfo; /* Destination information */ 575 ipp_t *request, /* Get-Printer-Attributes request */ 576 *response; /* Supported attributes */ 577 int tries, /* Number of tries so far */ 578 delay, /* Current retry delay */ 579 prev_delay; /* Next retry delay */ 580 const char *uri; /* Printer URI */ 581 char resource[1024]; /* Resource path */ 582 int version; /* IPP version */ 583 ipp_status_t status; /* Status of request */ 584 static const char * const requested_attrs[] = 585 { /* Requested attributes */ 586 "job-template", 587 "media-col-database", 588 "printer-description" 589 }; 590 591 592 DEBUG_printf(("cupsCopyDestSupported(http=%p, dest=%p(%s))", (void *)http, (void *)dest, dest ? dest->name : "")); 593 594 /* 595 * Get the default connection as needed... 596 */ 597 598 if (!http) 599 http = _cupsConnect(); 600 601 /* 602 * Range check input... 603 */ 604 605 if (!http || !dest) 606 return (NULL); 607 608 /* 609 * Get the printer URI and resource path... 610 */ 611 612 if ((uri = _cupsGetDestResource(dest, resource, sizeof(resource))) == NULL) 613 return (NULL); 614 615 /* 616 * Get the supported attributes... 617 */ 618 619 delay = 1; 620 prev_delay = 1; 621 tries = 0; 622 version = 20; 623 624 do 625 { 626 /* 627 * Send a Get-Printer-Attributes request... 628 */ 629 630 request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES); 631 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, 632 uri); 633 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, 634 "requesting-user-name", NULL, cupsUser()); 635 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, 636 "requested-attributes", 637 (int)(sizeof(requested_attrs) / sizeof(requested_attrs[0])), 638 NULL, requested_attrs); 639 response = cupsDoRequest(http, request, resource); 640 status = cupsLastError(); 641 642 if (status > IPP_STATUS_OK_IGNORED_OR_SUBSTITUTED) 643 { 644 DEBUG_printf(("cupsCopyDestSupported: Get-Printer-Attributes for '%s' " 645 "returned %s (%s)", dest->name, ippErrorString(status), 646 cupsLastErrorString())); 647 648 ippDelete(response); 649 response = NULL; 650 651 if (status == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED && version > 11) 652 version = 11; 653 else if (status == IPP_STATUS_ERROR_BUSY) 654 { 655 sleep((unsigned)delay); 656 657 delay = _cupsNextDelay(delay, &prev_delay); 658 } 659 else 660 return (NULL); 661 } 662 663 tries ++; 664 } 665 while (!response && tries < 10); 666 667 if (!response) 668 return (NULL); 669 670 /* 671 * Allocate a cups_dinfo_t structure and return it... 672 */ 673 674 if ((dinfo = calloc(1, sizeof(cups_dinfo_t))) == NULL) 675 { 676 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0); 677 ippDelete(response); 678 return (NULL); 679 } 680 681 dinfo->version = version; 682 dinfo->uri = uri; 683 dinfo->resource = _cupsStrAlloc(resource); 684 dinfo->attrs = response; 685 686 return (dinfo); 687 } 688 689 690 /* 691 * 'cupsFindDestDefault()' - Find the default value(s) for the given option. 692 * 693 * The returned value is an IPP attribute. Use the @code ippGetBoolean@, 694 * @code ippGetCollection@, @code ippGetCount@, @code ippGetDate@, 695 * @code ippGetInteger@, @code ippGetOctetString@, @code ippGetRange@, 696 * @code ippGetResolution@, @code ippGetString@, and @code ippGetValueTag@ 697 * functions to inspect the default value(s) as needed. 698 * 699 * @since CUPS 1.7/macOS 10.9@ 700 */ 701 702 ipp_attribute_t * /* O - Default attribute or @code NULL@ for none */ 703 cupsFindDestDefault( 704 http_t *http, /* I - Connection to destination */ 705 cups_dest_t *dest, /* I - Destination */ 706 cups_dinfo_t *dinfo, /* I - Destination information */ 707 const char *option) /* I - Option/attribute name */ 708 { 709 char name[IPP_MAX_NAME]; /* Attribute name */ 710 711 712 /* 713 * Get the default connection as needed... 714 */ 715 716 if (!http) 717 http = _cupsConnect(); 718 719 /* 720 * Range check input... 721 */ 722 723 if (!http || !dest || !dinfo || !option) 724 { 725 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0); 726 return (NULL); 727 } 728 729 /* 730 * Find and return the attribute... 731 */ 732 733 snprintf(name, sizeof(name), "%s-default", option); 734 return (ippFindAttribute(dinfo->attrs, name, IPP_TAG_ZERO)); 735 } 736 737 738 /* 739 * 'cupsFindDestReady()' - Find the default value(s) for the given option. 740 * 741 * The returned value is an IPP attribute. Use the @code ippGetBoolean@, 742 * @code ippGetCollection@, @code ippGetCount@, @code ippGetDate@, 743 * @code ippGetInteger@, @code ippGetOctetString@, @code ippGetRange@, 744 * @code ippGetResolution@, @code ippGetString@, and @code ippGetValueTag@ 745 * functions to inspect the default value(s) as needed. 746 * 747 * @since CUPS 1.7/macOS 10.9@ 748 */ 749 750 ipp_attribute_t * /* O - Default attribute or @code NULL@ for none */ 751 cupsFindDestReady( 752 http_t *http, /* I - Connection to destination */ 753 cups_dest_t *dest, /* I - Destination */ 754 cups_dinfo_t *dinfo, /* I - Destination information */ 755 const char *option) /* I - Option/attribute name */ 756 { 757 char name[IPP_MAX_NAME]; /* Attribute name */ 758 759 760 /* 761 * Get the default connection as needed... 762 */ 763 764 if (!http) 765 http = _cupsConnect(); 766 767 /* 768 * Range check input... 769 */ 770 771 if (!http || !dest || !dinfo || !option) 772 { 773 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0); 774 return (NULL); 775 } 776 777 /* 778 * Find and return the attribute... 779 */ 780 781 cups_update_ready(http, dinfo); 782 783 snprintf(name, sizeof(name), "%s-ready", option); 784 return (ippFindAttribute(dinfo->ready_attrs, name, IPP_TAG_ZERO)); 785 } 786 787 788 /* 789 * 'cupsFindDestSupported()' - Find the default value(s) for the given option. 790 * 791 * The returned value is an IPP attribute. Use the @code ippGetBoolean@, 792 * @code ippGetCollection@, @code ippGetCount@, @code ippGetDate@, 793 * @code ippGetInteger@, @code ippGetOctetString@, @code ippGetRange@, 794 * @code ippGetResolution@, @code ippGetString@, and @code ippGetValueTag@ 795 * functions to inspect the default value(s) as needed. 796 * 797 * @since CUPS 1.7/macOS 10.9@ 798 */ 799 800 ipp_attribute_t * /* O - Default attribute or @code NULL@ for none */ 801 cupsFindDestSupported( 802 http_t *http, /* I - Connection to destination */ 803 cups_dest_t *dest, /* I - Destination */ 804 cups_dinfo_t *dinfo, /* I - Destination information */ 805 const char *option) /* I - Option/attribute name */ 806 { 807 char name[IPP_MAX_NAME]; /* Attribute name */ 808 809 810 /* 811 * Get the default connection as needed... 812 */ 813 814 if (!http) 815 http = _cupsConnect(); 816 817 /* 818 * Range check input... 819 */ 820 821 if (!http || !dest || !dinfo || !option) 822 { 823 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0); 824 return (NULL); 825 } 826 827 /* 828 * Find and return the attribute... 829 */ 830 831 snprintf(name, sizeof(name), "%s-supported", option); 832 return (ippFindAttribute(dinfo->attrs, name, IPP_TAG_ZERO)); 833 } 834 835 836 /* 837 * 'cupsFreeDestInfo()' - Free destination information obtained using 838 * @link cupsCopyDestInfo@. 839 * 840 * @since CUPS 1.6/macOS 10.8@ 841 */ 842 843 void 844 cupsFreeDestInfo(cups_dinfo_t *dinfo) /* I - Destination information */ 845 { 846 /* 847 * Range check input... 848 */ 849 850 if (!dinfo) 851 return; 852 853 /* 854 * Free memory and return... 855 */ 856 857 _cupsStrFree(dinfo->resource); 858 859 cupsArrayDelete(dinfo->constraints); 860 cupsArrayDelete(dinfo->resolvers); 861 862 cupsArrayDelete(dinfo->localizations); 863 864 cupsArrayDelete(dinfo->media_db); 865 866 cupsArrayDelete(dinfo->cached_db); 867 868 ippDelete(dinfo->ready_attrs); 869 cupsArrayDelete(dinfo->ready_db); 870 871 ippDelete(dinfo->attrs); 872 873 free(dinfo); 874 } 875 876 877 /* 878 * 'cupsGetDestMediaByIndex()' - Get a media name, dimension, and margins for a 879 * specific size. 880 * 881 * The @code flags@ parameter determines which set of media are indexed. For 882 * example, passing @code CUPS_MEDIA_FLAGS_BORDERLESS@ will get the Nth 883 * borderless size supported by the printer. 884 * 885 * @since CUPS 1.7/macOS 10.9@ 886 */ 887 888 int /* O - 1 on success, 0 on failure */ 889 cupsGetDestMediaByIndex( 890 http_t *http, /* I - Connection to destination */ 891 cups_dest_t *dest, /* I - Destination */ 892 cups_dinfo_t *dinfo, /* I - Destination information */ 893 int n, /* I - Media size number (0-based) */ 894 unsigned flags, /* I - Media flags */ 895 cups_size_t *size) /* O - Media size information */ 896 { 897 _cups_media_db_t *nsize; /* Size for N */ 898 pwg_media_t *pwg; /* PWG media name for size */ 899 900 901 /* 902 * Get the default connection as needed... 903 */ 904 905 if (!http) 906 http = _cupsConnect(); 907 908 /* 909 * Range check input... 910 */ 911 912 if (size) 913 memset(size, 0, sizeof(cups_size_t)); 914 915 if (!http || !dest || !dinfo || n < 0 || !size) 916 { 917 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0); 918 return (0); 919 } 920 921 /* 922 * Load media list as needed... 923 */ 924 925 if (flags & CUPS_MEDIA_FLAGS_READY) 926 cups_update_ready(http, dinfo); 927 928 if (!dinfo->cached_db || dinfo->cached_flags != flags) 929 cups_create_cached(http, dinfo, flags); 930 931 /* 932 * Copy the size over and return... 933 */ 934 935 if ((nsize = (_cups_media_db_t *)cupsArrayIndex(dinfo->cached_db, n)) == NULL) 936 { 937 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0); 938 return (0); 939 } 940 941 if (nsize->size_name) 942 strlcpy(size->media, nsize->size_name, sizeof(size->media)); 943 else if (nsize->key) 944 strlcpy(size->media, nsize->key, sizeof(size->media)); 945 else if ((pwg = pwgMediaForSize(nsize->width, nsize->length)) != NULL) 946 strlcpy(size->media, pwg->pwg, sizeof(size->media)); 947 else 948 { 949 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0); 950 return (0); 951 } 952 953 size->width = nsize->width; 954 size->length = nsize->length; 955 size->bottom = nsize->bottom; 956 size->left = nsize->left; 957 size->right = nsize->right; 958 size->top = nsize->top; 959 960 return (1); 961 } 962 963 964 /* 965 * 'cupsGetDestMediaByName()' - Get media names, dimensions, and margins. 966 * 967 * The "media" string is a PWG media name. "Flags" provides some matching 968 * guidance (multiple flags can be combined): 969 * 970 * CUPS_MEDIA_FLAGS_DEFAULT = find the closest size supported by the printer, 971 * CUPS_MEDIA_FLAGS_BORDERLESS = find a borderless size, 972 * CUPS_MEDIA_FLAGS_DUPLEX = find a size compatible with 2-sided printing, 973 * CUPS_MEDIA_FLAGS_EXACT = find an exact match for the size, and 974 * CUPS_MEDIA_FLAGS_READY = if the printer supports media sensing, find the 975 * size amongst the "ready" media. 976 * 977 * The matching result (if any) is returned in the "cups_size_t" structure. 978 * 979 * Returns 1 when there is a match and 0 if there is not a match. 980 * 981 * @since CUPS 1.6/macOS 10.8@ 982 */ 983 984 int /* O - 1 on match, 0 on failure */ 985 cupsGetDestMediaByName( 986 http_t *http, /* I - Connection to destination */ 987 cups_dest_t *dest, /* I - Destination */ 988 cups_dinfo_t *dinfo, /* I - Destination information */ 989 const char *media, /* I - Media name */ 990 unsigned flags, /* I - Media matching flags */ 991 cups_size_t *size) /* O - Media size information */ 992 { 993 pwg_media_t *pwg; /* PWG media info */ 994 995 996 /* 997 * Get the default connection as needed... 998 */ 999 1000 if (!http) 1001 http = _cupsConnect(); 1002 1003 /* 1004 * Range check input... 1005 */ 1006 1007 if (size) 1008 memset(size, 0, sizeof(cups_size_t)); 1009 1010 if (!http || !dest || !dinfo || !media || !size) 1011 { 1012 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0); 1013 return (0); 1014 } 1015 1016 /* 1017 * Lookup the media size name... 1018 */ 1019 1020 if ((pwg = pwgMediaForPWG(media)) == NULL) 1021 if ((pwg = pwgMediaForLegacy(media)) == NULL) 1022 { 1023 DEBUG_printf(("1cupsGetDestMediaByName: Unknown size '%s'.", media)); 1024 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unknown media size name."), 1); 1025 return (0); 1026 } 1027 1028 /* 1029 * Lookup the size... 1030 */ 1031 1032 return (cups_get_media_db(http, dinfo, pwg, flags, size)); 1033 } 1034 1035 1036 /* 1037 * 'cupsGetDestMediaBySize()' - Get media names, dimensions, and margins. 1038 * 1039 * "Width" and "length" are the dimensions in hundredths of millimeters. 1040 * "Flags" provides some matching guidance (multiple flags can be combined): 1041 * 1042 * CUPS_MEDIA_FLAGS_DEFAULT = find the closest size supported by the printer, 1043 * CUPS_MEDIA_FLAGS_BORDERLESS = find a borderless size, 1044 * CUPS_MEDIA_FLAGS_DUPLEX = find a size compatible with 2-sided printing, 1045 * CUPS_MEDIA_FLAGS_EXACT = find an exact match for the size, and 1046 * CUPS_MEDIA_FLAGS_READY = if the printer supports media sensing, find the 1047 * size amongst the "ready" media. 1048 * 1049 * The matching result (if any) is returned in the "cups_size_t" structure. 1050 * 1051 * Returns 1 when there is a match and 0 if there is not a match. 1052 * 1053 * @since CUPS 1.6/macOS 10.8@ 1054 */ 1055 1056 int /* O - 1 on match, 0 on failure */ 1057 cupsGetDestMediaBySize( 1058 http_t *http, /* I - Connection to destination */ 1059 cups_dest_t *dest, /* I - Destination */ 1060 cups_dinfo_t *dinfo, /* I - Destination information */ 1061 int width, /* I - Media width in hundredths of 1062 * of millimeters */ 1063 int length, /* I - Media length in hundredths of 1064 * of millimeters */ 1065 unsigned flags, /* I - Media matching flags */ 1066 cups_size_t *size) /* O - Media size information */ 1067 { 1068 pwg_media_t *pwg; /* PWG media info */ 1069 1070 1071 /* 1072 * Get the default connection as needed... 1073 */ 1074 1075 if (!http) 1076 http = _cupsConnect(); 1077 1078 /* 1079 * Range check input... 1080 */ 1081 1082 if (size) 1083 memset(size, 0, sizeof(cups_size_t)); 1084 1085 if (!http || !dest || !dinfo || width <= 0 || length <= 0 || !size) 1086 { 1087 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0); 1088 return (0); 1089 } 1090 1091 /* 1092 * Lookup the media size name... 1093 */ 1094 1095 if ((pwg = pwgMediaForSize(width, length)) == NULL) 1096 { 1097 DEBUG_printf(("1cupsGetDestMediaBySize: Invalid size %dx%d.", width, 1098 length)); 1099 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Invalid media size."), 1); 1100 return (0); 1101 } 1102 1103 /* 1104 * Lookup the size... 1105 */ 1106 1107 return (cups_get_media_db(http, dinfo, pwg, flags, size)); 1108 } 1109 1110 1111 /* 1112 * 'cupsGetDestMediaCount()' - Get the number of sizes supported by a 1113 * destination. 1114 * 1115 * The @code flags@ parameter determines the set of media sizes that are 1116 * counted. For example, passing @code CUPS_MEDIA_FLAGS_BORDERLESS@ will return 1117 * the number of borderless sizes. 1118 * 1119 * @since CUPS 1.7/macOS 10.9@ 1120 */ 1121 1122 int /* O - Number of sizes */ 1123 cupsGetDestMediaCount( 1124 http_t *http, /* I - Connection to destination */ 1125 cups_dest_t *dest, /* I - Destination */ 1126 cups_dinfo_t *dinfo, /* I - Destination information */ 1127 unsigned flags) /* I - Media flags */ 1128 { 1129 /* 1130 * Get the default connection as needed... 1131 */ 1132 1133 if (!http) 1134 http = _cupsConnect(); 1135 1136 /* 1137 * Range check input... 1138 */ 1139 1140 if (!http || !dest || !dinfo) 1141 { 1142 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0); 1143 return (0); 1144 } 1145 1146 /* 1147 * Load media list as needed... 1148 */ 1149 1150 if (flags & CUPS_MEDIA_FLAGS_READY) 1151 cups_update_ready(http, dinfo); 1152 1153 if (!dinfo->cached_db || dinfo->cached_flags != flags) 1154 cups_create_cached(http, dinfo, flags); 1155 1156 return (cupsArrayCount(dinfo->cached_db)); 1157 } 1158 1159 1160 /* 1161 * 'cupsGetDestMediaDefault()' - Get the default size for a destination. 1162 * 1163 * The @code flags@ parameter determines which default size is returned. For 1164 * example, passing @code CUPS_MEDIA_FLAGS_BORDERLESS@ will return the default 1165 * borderless size, typically US Letter or A4, but sometimes 4x6 photo media. 1166 * 1167 * @since CUPS 1.7/macOS 10.9@ 1168 */ 1169 1170 int /* O - 1 on success, 0 on failure */ 1171 cupsGetDestMediaDefault( 1172 http_t *http, /* I - Connection to destination */ 1173 cups_dest_t *dest, /* I - Destination */ 1174 cups_dinfo_t *dinfo, /* I - Destination information */ 1175 unsigned flags, /* I - Media flags */ 1176 cups_size_t *size) /* O - Media size information */ 1177 { 1178 const char *media; /* Default media size */ 1179 1180 1181 /* 1182 * Get the default connection as needed... 1183 */ 1184 1185 if (!http) 1186 http = _cupsConnect(); 1187 1188 /* 1189 * Range check input... 1190 */ 1191 1192 if (size) 1193 memset(size, 0, sizeof(cups_size_t)); 1194 1195 if (!http || !dest || !dinfo || !size) 1196 { 1197 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0); 1198 return (0); 1199 } 1200 1201 /* 1202 * Get the default media size, if any... 1203 */ 1204 1205 if ((media = cupsGetOption("media", dest->num_options, 1206 dest->options)) == NULL) 1207 media = "na_letter_8.5x11in"; 1208 1209 if (cupsGetDestMediaByName(http, dest, dinfo, media, flags, size)) 1210 return (1); 1211 1212 if (strcmp(media, "na_letter_8.5x11in") && 1213 cupsGetDestMediaByName(http, dest, dinfo, "iso_a4_210x297mm", flags, 1214 size)) 1215 return (1); 1216 1217 if (strcmp(media, "iso_a4_210x297mm") && 1218 cupsGetDestMediaByName(http, dest, dinfo, "na_letter_8.5x11in", flags, 1219 size)) 1220 return (1); 1221 1222 if ((flags & CUPS_MEDIA_FLAGS_BORDERLESS) && 1223 cupsGetDestMediaByName(http, dest, dinfo, "na_index_4x6in", flags, size)) 1224 return (1); 1225 1226 /* 1227 * Fall back to the first matching media size... 1228 */ 1229 1230 return (cupsGetDestMediaByIndex(http, dest, dinfo, 0, flags, size)); 1231 } 1232 1233 1234 /* 1235 * 'cups_add_dconstres()' - Add a constraint or resolver to an array. 1236 */ 1237 1238 static void 1239 cups_add_dconstres( 1240 cups_array_t *a, /* I - Array */ 1241 ipp_t *collection) /* I - Collection value */ 1242 { 1243 ipp_attribute_t *attr; /* Attribute */ 1244 _cups_dconstres_t *temp; /* Current constraint/resolver */ 1245 1246 1247 if ((attr = ippFindAttribute(collection, "resolver-name", 1248 IPP_TAG_NAME)) == NULL) 1249 return; 1250 1251 if ((temp = calloc(1, sizeof(_cups_dconstres_t))) == NULL) 1252 return; 1253 1254 temp->name = attr->values[0].string.text; 1255 temp->collection = collection; 1256 1257 cupsArrayAdd(a, temp); 1258 } 1259 1260 1261 /* 1262 * 'cups_compare_dconstres()' - Compare to resolver entries. 1263 */ 1264 1265 static int /* O - Result of comparison */ 1266 cups_compare_dconstres( 1267 _cups_dconstres_t *a, /* I - First resolver */ 1268 _cups_dconstres_t *b) /* I - Second resolver */ 1269 { 1270 return (strcmp(a->name, b->name)); 1271 } 1272 1273 1274 /* 1275 * 'cups_compare_media_db()' - Compare two media entries. 1276 */ 1277 1278 static int /* O - Result of comparison */ 1279 cups_compare_media_db( 1280 _cups_media_db_t *a, /* I - First media entries */ 1281 _cups_media_db_t *b) /* I - Second media entries */ 1282 { 1283 int result; /* Result of comparison */ 1284 1285 1286 if ((result = a->width - b->width) == 0) 1287 result = a->length - b->length; 1288 1289 return (result); 1290 } 1291 1292 1293 /* 1294 * 'cups_copy_media_db()' - Copy a media entry. 1295 */ 1296 1297 static _cups_media_db_t * /* O - New media entry */ 1298 cups_copy_media_db( 1299 _cups_media_db_t *mdb) /* I - Media entry to copy */ 1300 { 1301 _cups_media_db_t *temp; /* New media entry */ 1302 1303 1304 if ((temp = calloc(1, sizeof(_cups_media_db_t))) == NULL) 1305 return (NULL); 1306 1307 if (mdb->color) 1308 temp->color = _cupsStrAlloc(mdb->color); 1309 if (mdb->key) 1310 temp->key = _cupsStrAlloc(mdb->key); 1311 if (mdb->info) 1312 temp->info = _cupsStrAlloc(mdb->info); 1313 if (mdb->size_name) 1314 temp->size_name = _cupsStrAlloc(mdb->size_name); 1315 if (mdb->source) 1316 temp->source = _cupsStrAlloc(mdb->source); 1317 if (mdb->type) 1318 temp->type = _cupsStrAlloc(mdb->type); 1319 1320 temp->width = mdb->width; 1321 temp->length = mdb->length; 1322 temp->bottom = mdb->bottom; 1323 temp->left = mdb->left; 1324 temp->right = mdb->right; 1325 temp->top = mdb->top; 1326 1327 return (temp); 1328 } 1329 1330 1331 /* 1332 * 'cups_create_cached()' - Create the media selection cache. 1333 */ 1334 1335 static void 1336 cups_create_cached(http_t *http, /* I - Connection to destination */ 1337 cups_dinfo_t *dinfo, /* I - Destination information */ 1338 unsigned flags) /* I - Media selection flags */ 1339 { 1340 cups_array_t *db; /* Media database array to use */ 1341 _cups_media_db_t *mdb, /* Media database entry */ 1342 *first; /* First entry this size */ 1343 1344 1345 DEBUG_printf(("3cups_create_cached(http=%p, dinfo=%p, flags=%u)", (void *)http, (void *)dinfo, flags)); 1346 1347 if (dinfo->cached_db) 1348 cupsArrayDelete(dinfo->cached_db); 1349 1350 dinfo->cached_db = cupsArrayNew(NULL, NULL); 1351 dinfo->cached_flags = flags; 1352 1353 if (flags & CUPS_MEDIA_FLAGS_READY) 1354 { 1355 DEBUG_puts("4cups_create_cached: ready media"); 1356 1357 cups_update_ready(http, dinfo); 1358 db = dinfo->ready_db; 1359 } 1360 else 1361 { 1362 DEBUG_puts("4cups_create_cached: supported media"); 1363 1364 if (!dinfo->media_db) 1365 cups_create_media_db(dinfo, CUPS_MEDIA_FLAGS_DEFAULT); 1366 1367 db = dinfo->media_db; 1368 } 1369 1370 for (mdb = (_cups_media_db_t *)cupsArrayFirst(db), first = mdb; 1371 mdb; 1372 mdb = (_cups_media_db_t *)cupsArrayNext(db)) 1373 { 1374 DEBUG_printf(("4cups_create_cached: %p key=\"%s\", type=\"%s\", %dx%d, B%d L%d R%d T%d", (void *)mdb, mdb->key, mdb->type, mdb->width, mdb->length, mdb->bottom, mdb->left, mdb->right, mdb->top)); 1375 1376 if (flags & CUPS_MEDIA_FLAGS_BORDERLESS) 1377 { 1378 if (!mdb->left && !mdb->right && !mdb->top && !mdb->bottom) 1379 { 1380 DEBUG_printf(("4cups_create_cached: add %p", (void *)mdb)); 1381 cupsArrayAdd(dinfo->cached_db, mdb); 1382 } 1383 } 1384 else if (flags & CUPS_MEDIA_FLAGS_DUPLEX) 1385 { 1386 if (first->width != mdb->width || first->length != mdb->length) 1387 { 1388 DEBUG_printf(("4cups_create_cached: add %p", (void *)first)); 1389 cupsArrayAdd(dinfo->cached_db, first); 1390 first = mdb; 1391 } 1392 else if (mdb->left >= first->left && mdb->right >= first->right && mdb->top >= first->top && mdb->bottom >= first->bottom && 1393 (mdb->left != first->left || mdb->right != first->right || mdb->top != first->top || mdb->bottom != first->bottom)) 1394 first = mdb; 1395 } 1396 else 1397 { 1398 DEBUG_printf(("4cups_create_cached: add %p", (void *)mdb)); 1399 cupsArrayAdd(dinfo->cached_db, mdb); 1400 } 1401 } 1402 1403 if (flags & CUPS_MEDIA_FLAGS_DUPLEX) 1404 { 1405 DEBUG_printf(("4cups_create_cached: add %p", (void *)first)); 1406 cupsArrayAdd(dinfo->cached_db, first); 1407 } 1408 } 1409 1410 1411 /* 1412 * 'cups_create_constraints()' - Create the constraints and resolvers arrays. 1413 */ 1414 1415 static void 1416 cups_create_constraints( 1417 cups_dinfo_t *dinfo) /* I - Destination information */ 1418 { 1419 int i; /* Looping var */ 1420 ipp_attribute_t *attr; /* Attribute */ 1421 _ipp_value_t *val; /* Current value */ 1422 1423 1424 dinfo->constraints = cupsArrayNew3(NULL, NULL, NULL, 0, NULL, 1425 (cups_afree_func_t)free); 1426 dinfo->resolvers = cupsArrayNew3((cups_array_func_t)cups_compare_dconstres, 1427 NULL, NULL, 0, NULL, 1428 (cups_afree_func_t)free); 1429 1430 if ((attr = ippFindAttribute(dinfo->attrs, "job-constraints-supported", 1431 IPP_TAG_BEGIN_COLLECTION)) != NULL) 1432 { 1433 for (i = attr->num_values, val = attr->values; i > 0; i --, val ++) 1434 cups_add_dconstres(dinfo->constraints, val->collection); 1435 } 1436 1437 if ((attr = ippFindAttribute(dinfo->attrs, "job-resolvers-supported", 1438 IPP_TAG_BEGIN_COLLECTION)) != NULL) 1439 { 1440 for (i = attr->num_values, val = attr->values; i > 0; i --, val ++) 1441 cups_add_dconstres(dinfo->resolvers, val->collection); 1442 } 1443 } 1444 1445 1446 /* 1447 * 'cups_create_defaults()' - Create the -default option array. 1448 * 1449 * TODO: Need to support collection defaults... 1450 */ 1451 1452 static void 1453 cups_create_defaults( 1454 cups_dinfo_t *dinfo) /* I - Destination information */ 1455 { 1456 ipp_attribute_t *attr; /* Current attribute */ 1457 char name[IPP_MAX_NAME + 1], 1458 /* Current name */ 1459 *nameptr, /* Pointer into current name */ 1460 value[2048]; /* Current value */ 1461 1462 1463 /* 1464 * Iterate through the printer attributes looking for xxx-default and adding 1465 * xxx=value to the defaults option array. 1466 */ 1467 1468 for (attr = ippFirstAttribute(dinfo->attrs); 1469 attr; 1470 attr = ippNextAttribute(dinfo->attrs)) 1471 { 1472 if (!attr->name || attr->group_tag != IPP_TAG_PRINTER) 1473 continue; 1474 1475 if (attr->value_tag == IPP_TAG_BEGIN_COLLECTION) 1476 continue; /* TODO: STR #4096 */ 1477 1478 if ((nameptr = attr->name + strlen(attr->name) - 8) <= attr->name || 1479 strcmp(nameptr, "-default")) 1480 continue; 1481 1482 strlcpy(name, attr->name, sizeof(name)); 1483 if ((nameptr = name + strlen(name) - 8) <= name || 1484 strcmp(nameptr, "-default")) 1485 continue; 1486 1487 *nameptr = '\0'; 1488 1489 if (ippAttributeString(attr, value, sizeof(value)) >= sizeof(value)) 1490 continue; 1491 1492 dinfo->num_defaults = cupsAddOption(name, value, dinfo->num_defaults, 1493 &dinfo->defaults); 1494 } 1495 } 1496 1497 1498 /* 1499 * 'cups_create_media_db()' - Create the media database. 1500 */ 1501 1502 static void 1503 cups_create_media_db( 1504 cups_dinfo_t *dinfo, /* I - Destination information */ 1505 unsigned flags) /* I - Media flags */ 1506 { 1507 int i; /* Looping var */ 1508 _ipp_value_t *val; /* Current value */ 1509 ipp_attribute_t *media_col_db, /* media-col-database */ 1510 *media_attr, /* media-xxx */ 1511 *x_dimension, /* x-dimension */ 1512 *y_dimension; /* y-dimension */ 1513 pwg_media_t *pwg; /* PWG media info */ 1514 cups_array_t *db; /* New media database array */ 1515 _cups_media_db_t mdb; /* Media entry */ 1516 1517 1518 db = cupsArrayNew3((cups_array_func_t)cups_compare_media_db, 1519 NULL, NULL, 0, 1520 (cups_acopy_func_t)cups_copy_media_db, 1521 (cups_afree_func_t)cups_free_media_db); 1522 1523 if (flags == CUPS_MEDIA_FLAGS_READY) 1524 { 1525 dinfo->ready_db = db; 1526 1527 media_col_db = ippFindAttribute(dinfo->ready_attrs, "media-col-ready", 1528 IPP_TAG_BEGIN_COLLECTION); 1529 media_attr = ippFindAttribute(dinfo->ready_attrs, "media-ready", 1530 IPP_TAG_ZERO); 1531 } 1532 else 1533 { 1534 dinfo->media_db = db; 1535 dinfo->min_size.width = INT_MAX; 1536 dinfo->min_size.length = INT_MAX; 1537 dinfo->max_size.width = 0; 1538 dinfo->max_size.length = 0; 1539 1540 media_col_db = ippFindAttribute(dinfo->attrs, "media-col-database", 1541 IPP_TAG_BEGIN_COLLECTION); 1542 media_attr = ippFindAttribute(dinfo->attrs, "media-supported", 1543 IPP_TAG_ZERO); 1544 } 1545 1546 if (media_col_db) 1547 { 1548 _ipp_value_t *custom = NULL; /* Custom size range value */ 1549 1550 for (i = media_col_db->num_values, val = media_col_db->values; 1551 i > 0; 1552 i --, val ++) 1553 { 1554 memset(&mdb, 0, sizeof(mdb)); 1555 1556 if ((media_attr = ippFindAttribute(val->collection, "media-size", 1557 IPP_TAG_BEGIN_COLLECTION)) != NULL) 1558 { 1559 ipp_t *media_size = media_attr->values[0].collection; 1560 /* media-size collection value */ 1561 1562 if ((x_dimension = ippFindAttribute(media_size, "x-dimension", 1563 IPP_TAG_INTEGER)) != NULL && 1564 (y_dimension = ippFindAttribute(media_size, "y-dimension", 1565 IPP_TAG_INTEGER)) != NULL) 1566 { 1567 /* 1568 * Fixed size... 1569 */ 1570 1571 mdb.width = x_dimension->values[0].integer; 1572 mdb.length = y_dimension->values[0].integer; 1573 } 1574 else if ((x_dimension = ippFindAttribute(media_size, "x-dimension", 1575 IPP_TAG_INTEGER)) != NULL && 1576 (y_dimension = ippFindAttribute(media_size, "y-dimension", 1577 IPP_TAG_RANGE)) != NULL) 1578 { 1579 /* 1580 * Roll limits... 1581 */ 1582 1583 mdb.width = x_dimension->values[0].integer; 1584 mdb.length = y_dimension->values[0].range.upper; 1585 } 1586 else if (flags != CUPS_MEDIA_FLAGS_READY && 1587 (x_dimension = ippFindAttribute(media_size, "x-dimension", 1588 IPP_TAG_RANGE)) != NULL && 1589 (y_dimension = ippFindAttribute(media_size, "y-dimension", 1590 IPP_TAG_RANGE)) != NULL) 1591 { 1592 /* 1593 * Custom size range; save this as the custom size value with default 1594 * margins, then continue; we'll capture the real margins below... 1595 */ 1596 1597 custom = val; 1598 1599 dinfo->min_size.width = x_dimension->values[0].range.lower; 1600 dinfo->min_size.length = y_dimension->values[0].range.lower; 1601 dinfo->min_size.left = 1602 dinfo->min_size.right = 635; /* Default 1/4" side margins */ 1603 dinfo->min_size.top = 1604 dinfo->min_size.bottom = 1270; /* Default 1/2" top/bottom margins */ 1605 1606 dinfo->max_size.width = x_dimension->values[0].range.upper; 1607 dinfo->max_size.length = y_dimension->values[0].range.upper; 1608 dinfo->max_size.left = 1609 dinfo->max_size.right = 635; /* Default 1/4" side margins */ 1610 dinfo->max_size.top = 1611 dinfo->max_size.bottom = 1270; /* Default 1/2" top/bottom margins */ 1612 continue; 1613 } 1614 } 1615 1616 if ((media_attr = ippFindAttribute(val->collection, "media-color", 1617 IPP_TAG_ZERO)) != NULL && 1618 (media_attr->value_tag == IPP_TAG_NAME || 1619 media_attr->value_tag == IPP_TAG_NAMELANG || 1620 media_attr->value_tag == IPP_TAG_KEYWORD)) 1621 mdb.color = media_attr->values[0].string.text; 1622 1623 if ((media_attr = ippFindAttribute(val->collection, "media-info", 1624 IPP_TAG_TEXT)) != NULL) 1625 mdb.info = media_attr->values[0].string.text; 1626 1627 if ((media_attr = ippFindAttribute(val->collection, "media-key", 1628 IPP_TAG_ZERO)) != NULL && 1629 (media_attr->value_tag == IPP_TAG_NAME || 1630 media_attr->value_tag == IPP_TAG_NAMELANG || 1631 media_attr->value_tag == IPP_TAG_KEYWORD)) 1632 mdb.key = media_attr->values[0].string.text; 1633 1634 if ((media_attr = ippFindAttribute(val->collection, "media-size-name", 1635 IPP_TAG_ZERO)) != NULL && 1636 (media_attr->value_tag == IPP_TAG_NAME || 1637 media_attr->value_tag == IPP_TAG_NAMELANG || 1638 media_attr->value_tag == IPP_TAG_KEYWORD)) 1639 mdb.size_name = media_attr->values[0].string.text; 1640 1641 if ((media_attr = ippFindAttribute(val->collection, "media-source", 1642 IPP_TAG_ZERO)) != NULL && 1643 (media_attr->value_tag == IPP_TAG_NAME || 1644 media_attr->value_tag == IPP_TAG_NAMELANG || 1645 media_attr->value_tag == IPP_TAG_KEYWORD)) 1646 mdb.source = media_attr->values[0].string.text; 1647 1648 if ((media_attr = ippFindAttribute(val->collection, "media-type", 1649 IPP_TAG_ZERO)) != NULL && 1650 (media_attr->value_tag == IPP_TAG_NAME || 1651 media_attr->value_tag == IPP_TAG_NAMELANG || 1652 media_attr->value_tag == IPP_TAG_KEYWORD)) 1653 mdb.type = media_attr->values[0].string.text; 1654 1655 if ((media_attr = ippFindAttribute(val->collection, "media-bottom-margin", 1656 IPP_TAG_INTEGER)) != NULL) 1657 mdb.bottom = media_attr->values[0].integer; 1658 1659 if ((media_attr = ippFindAttribute(val->collection, "media-left-margin", 1660 IPP_TAG_INTEGER)) != NULL) 1661 mdb.left = media_attr->values[0].integer; 1662 1663 if ((media_attr = ippFindAttribute(val->collection, "media-right-margin", 1664 IPP_TAG_INTEGER)) != NULL) 1665 mdb.right = media_attr->values[0].integer; 1666 1667 if ((media_attr = ippFindAttribute(val->collection, "media-top-margin", 1668 IPP_TAG_INTEGER)) != NULL) 1669 mdb.top = media_attr->values[0].integer; 1670 1671 cupsArrayAdd(db, &mdb); 1672 } 1673 1674 if (custom) 1675 { 1676 if ((media_attr = ippFindAttribute(custom->collection, 1677 "media-bottom-margin", 1678 IPP_TAG_INTEGER)) != NULL) 1679 { 1680 dinfo->min_size.top = 1681 dinfo->max_size.top = media_attr->values[0].integer; 1682 } 1683 1684 if ((media_attr = ippFindAttribute(custom->collection, 1685 "media-left-margin", 1686 IPP_TAG_INTEGER)) != NULL) 1687 { 1688 dinfo->min_size.left = 1689 dinfo->max_size.left = media_attr->values[0].integer; 1690 } 1691 1692 if ((media_attr = ippFindAttribute(custom->collection, 1693 "media-right-margin", 1694 IPP_TAG_INTEGER)) != NULL) 1695 { 1696 dinfo->min_size.right = 1697 dinfo->max_size.right = media_attr->values[0].integer; 1698 } 1699 1700 if ((media_attr = ippFindAttribute(custom->collection, 1701 "media-top-margin", 1702 IPP_TAG_INTEGER)) != NULL) 1703 { 1704 dinfo->min_size.top = 1705 dinfo->max_size.top = media_attr->values[0].integer; 1706 } 1707 } 1708 } 1709 else if (media_attr && 1710 (media_attr->value_tag == IPP_TAG_NAME || 1711 media_attr->value_tag == IPP_TAG_NAMELANG || 1712 media_attr->value_tag == IPP_TAG_KEYWORD)) 1713 { 1714 memset(&mdb, 0, sizeof(mdb)); 1715 1716 mdb.left = 1717 mdb.right = 635; /* Default 1/4" side margins */ 1718 mdb.top = 1719 mdb.bottom = 1270; /* Default 1/2" top/bottom margins */ 1720 1721 for (i = media_attr->num_values, val = media_attr->values; 1722 i > 0; 1723 i --, val ++) 1724 { 1725 if ((pwg = pwgMediaForPWG(val->string.text)) == NULL) 1726 if ((pwg = pwgMediaForLegacy(val->string.text)) == NULL) 1727 { 1728 DEBUG_printf(("3cups_create_media_db: Ignoring unknown size '%s'.", 1729 val->string.text)); 1730 continue; 1731 } 1732 1733 mdb.width = pwg->width; 1734 mdb.length = pwg->length; 1735 1736 if (flags != CUPS_MEDIA_FLAGS_READY && 1737 !strncmp(val->string.text, "custom_min_", 11)) 1738 { 1739 mdb.size_name = NULL; 1740 dinfo->min_size = mdb; 1741 } 1742 else if (flags != CUPS_MEDIA_FLAGS_READY && 1743 !strncmp(val->string.text, "custom_max_", 11)) 1744 { 1745 mdb.size_name = NULL; 1746 dinfo->max_size = mdb; 1747 } 1748 else 1749 { 1750 mdb.size_name = val->string.text; 1751 1752 cupsArrayAdd(db, &mdb); 1753 } 1754 } 1755 } 1756 } 1757 1758 1759 /* 1760 * 'cups_free_media_cb()' - Free a media entry. 1761 */ 1762 1763 static void 1764 cups_free_media_db( 1765 _cups_media_db_t *mdb) /* I - Media entry to free */ 1766 { 1767 if (mdb->color) 1768 _cupsStrFree(mdb->color); 1769 if (mdb->key) 1770 _cupsStrFree(mdb->key); 1771 if (mdb->info) 1772 _cupsStrFree(mdb->info); 1773 if (mdb->size_name) 1774 _cupsStrFree(mdb->size_name); 1775 if (mdb->source) 1776 _cupsStrFree(mdb->source); 1777 if (mdb->type) 1778 _cupsStrFree(mdb->type); 1779 1780 free(mdb); 1781 } 1782 1783 1784 /* 1785 * 'cups_get_media_db()' - Lookup the media entry for a given size. 1786 */ 1787 1788 static int /* O - 1 on match, 0 on failure */ 1789 cups_get_media_db(http_t *http, /* I - Connection to destination */ 1790 cups_dinfo_t *dinfo, /* I - Destination information */ 1791 pwg_media_t *pwg, /* I - PWG media info */ 1792 unsigned flags, /* I - Media matching flags */ 1793 cups_size_t *size) /* O - Media size/margin/name info */ 1794 { 1795 cups_array_t *db; /* Which media database to query */ 1796 _cups_media_db_t *mdb, /* Current media database entry */ 1797 *best = NULL, /* Best matching entry */ 1798 key; /* Search key */ 1799 1800 1801 /* 1802 * Create the media database as needed... 1803 */ 1804 1805 if (flags & CUPS_MEDIA_FLAGS_READY) 1806 { 1807 cups_update_ready(http, dinfo); 1808 db = dinfo->ready_db; 1809 } 1810 else 1811 { 1812 if (!dinfo->media_db) 1813 cups_create_media_db(dinfo, CUPS_MEDIA_FLAGS_DEFAULT); 1814 1815 db = dinfo->media_db; 1816 } 1817 1818 /* 1819 * Find a match... 1820 */ 1821 1822 memset(&key, 0, sizeof(key)); 1823 key.width = pwg->width; 1824 key.length = pwg->length; 1825 1826 if ((mdb = cupsArrayFind(db, &key)) != NULL) 1827 { 1828 /* 1829 * Found an exact match, let's figure out the best margins for the flags 1830 * supplied... 1831 */ 1832 1833 best = mdb; 1834 1835 if (flags & CUPS_MEDIA_FLAGS_BORDERLESS) 1836 { 1837 /* 1838 * Look for the smallest margins... 1839 */ 1840 1841 if (best->left != 0 || best->right != 0 || best->top != 0 || best->bottom != 0) 1842 { 1843 for (mdb = (_cups_media_db_t *)cupsArrayNext(db); 1844 mdb && !cups_compare_media_db(mdb, &key); 1845 mdb = (_cups_media_db_t *)cupsArrayNext(db)) 1846 { 1847 if (mdb->left <= best->left && mdb->right <= best->right && 1848 mdb->top <= best->top && mdb->bottom <= best->bottom) 1849 { 1850 best = mdb; 1851 if (mdb->left == 0 && mdb->right == 0 && mdb->bottom == 0 && 1852 mdb->top == 0) 1853 break; 1854 } 1855 } 1856 } 1857 1858 /* 1859 * If we need an exact match, return no-match if the size is not 1860 * borderless. 1861 */ 1862 1863 if ((flags & CUPS_MEDIA_FLAGS_EXACT) && 1864 (best->left || best->right || best->top || best->bottom)) 1865 return (0); 1866 } 1867 else if (flags & CUPS_MEDIA_FLAGS_DUPLEX) 1868 { 1869 /* 1870 * Look for the largest margins... 1871 */ 1872 1873 for (mdb = (_cups_media_db_t *)cupsArrayNext(db); 1874 mdb && !cups_compare_media_db(mdb, &key); 1875 mdb = (_cups_media_db_t *)cupsArrayNext(db)) 1876 { 1877 if (mdb->left >= best->left && mdb->right >= best->right && 1878 mdb->top >= best->top && mdb->bottom >= best->bottom && 1879 (mdb->bottom != best->bottom || mdb->left != best->left || mdb->right != best->right || mdb->top != best->top)) 1880 best = mdb; 1881 } 1882 } 1883 else 1884 { 1885 /* 1886 * Look for the smallest non-zero margins... 1887 */ 1888 1889 for (mdb = (_cups_media_db_t *)cupsArrayNext(db); 1890 mdb && !cups_compare_media_db(mdb, &key); 1891 mdb = (_cups_media_db_t *)cupsArrayNext(db)) 1892 { 1893 if (((mdb->left > 0 && mdb->left <= best->left) || best->left == 0) && 1894 ((mdb->right > 0 && mdb->right <= best->right) || best->right == 0) && 1895 ((mdb->top > 0 && mdb->top <= best->top) || best->top == 0) && 1896 ((mdb->bottom > 0 && mdb->bottom <= best->bottom) || best->bottom == 0) && 1897 (mdb->bottom != best->bottom || mdb->left != best->left || mdb->right != best->right || mdb->top != best->top)) 1898 best = mdb; 1899 } 1900 } 1901 } 1902 else if (flags & CUPS_MEDIA_FLAGS_EXACT) 1903 { 1904 /* 1905 * See if we can do this as a custom size... 1906 */ 1907 1908 if (pwg->width < dinfo->min_size.width || 1909 pwg->width > dinfo->max_size.width || 1910 pwg->length < dinfo->min_size.length || 1911 pwg->length > dinfo->max_size.length) 1912 return (0); /* Out of range */ 1913 1914 if ((flags & CUPS_MEDIA_FLAGS_BORDERLESS) && 1915 (dinfo->min_size.left > 0 || dinfo->min_size.right > 0 || 1916 dinfo->min_size.top > 0 || dinfo->min_size.bottom > 0)) 1917 return (0); /* Not borderless */ 1918 1919 key.size_name = (char *)pwg->pwg; 1920 key.bottom = dinfo->min_size.bottom; 1921 key.left = dinfo->min_size.left; 1922 key.right = dinfo->min_size.right; 1923 key.top = dinfo->min_size.top; 1924 1925 best = &key; 1926 } 1927 else if (pwg->width >= dinfo->min_size.width && 1928 pwg->width <= dinfo->max_size.width && 1929 pwg->length >= dinfo->min_size.length && 1930 pwg->length <= dinfo->max_size.length) 1931 { 1932 /* 1933 * Map to custom size... 1934 */ 1935 1936 key.size_name = (char *)pwg->pwg; 1937 key.bottom = dinfo->min_size.bottom; 1938 key.left = dinfo->min_size.left; 1939 key.right = dinfo->min_size.right; 1940 key.top = dinfo->min_size.top; 1941 1942 best = &key; 1943 } 1944 else 1945 { 1946 /* 1947 * Find a close size... 1948 */ 1949 1950 for (mdb = (_cups_media_db_t *)cupsArrayFirst(db); 1951 mdb; 1952 mdb = (_cups_media_db_t *)cupsArrayNext(db)) 1953 if (cups_is_close_media_db(mdb, &key)) 1954 break; 1955 1956 if (!mdb) 1957 return (0); 1958 1959 best = mdb; 1960 1961 if (flags & CUPS_MEDIA_FLAGS_BORDERLESS) 1962 { 1963 /* 1964 * Look for the smallest margins... 1965 */ 1966 1967 if (best->left != 0 || best->right != 0 || best->top != 0 || 1968 best->bottom != 0) 1969 { 1970 for (mdb = (_cups_media_db_t *)cupsArrayNext(db); 1971 mdb && cups_is_close_media_db(mdb, &key); 1972 mdb = (_cups_media_db_t *)cupsArrayNext(db)) 1973 { 1974 if (mdb->left <= best->left && mdb->right <= best->right && 1975 mdb->top <= best->top && mdb->bottom <= best->bottom && 1976 (mdb->bottom != best->bottom || mdb->left != best->left || mdb->right != best->right || mdb->top != best->top)) 1977 { 1978 best = mdb; 1979 if (mdb->left == 0 && mdb->right == 0 && mdb->bottom == 0 && 1980 mdb->top == 0) 1981 break; 1982 } 1983 } 1984 } 1985 } 1986 else if (flags & CUPS_MEDIA_FLAGS_DUPLEX) 1987 { 1988 /* 1989 * Look for the largest margins... 1990 */ 1991 1992 for (mdb = (_cups_media_db_t *)cupsArrayNext(db); 1993 mdb && cups_is_close_media_db(mdb, &key); 1994 mdb = (_cups_media_db_t *)cupsArrayNext(db)) 1995 { 1996 if (mdb->left >= best->left && mdb->right >= best->right && 1997 mdb->top >= best->top && mdb->bottom >= best->bottom && 1998 (mdb->bottom != best->bottom || mdb->left != best->left || mdb->right != best->right || mdb->top != best->top)) 1999 best = mdb; 2000 } 2001 } 2002 else 2003 { 2004 /* 2005 * Look for the smallest non-zero margins... 2006 */ 2007 2008 for (mdb = (_cups_media_db_t *)cupsArrayNext(db); 2009 mdb && cups_is_close_media_db(mdb, &key); 2010 mdb = (_cups_media_db_t *)cupsArrayNext(db)) 2011 { 2012 if (((mdb->left > 0 && mdb->left <= best->left) || best->left == 0) && 2013 ((mdb->right > 0 && mdb->right <= best->right) || 2014 best->right == 0) && 2015 ((mdb->top > 0 && mdb->top <= best->top) || best->top == 0) && 2016 ((mdb->bottom > 0 && mdb->bottom <= best->bottom) || 2017 best->bottom == 0) && 2018 (mdb->bottom != best->bottom || mdb->left != best->left || mdb->right != best->right || mdb->top != best->top)) 2019 best = mdb; 2020 } 2021 } 2022 } 2023 2024 if (best) 2025 { 2026 /* 2027 * Return the matching size... 2028 */ 2029 2030 if (best->size_name) 2031 strlcpy(size->media, best->size_name, sizeof(size->media)); 2032 else if (best->key) 2033 strlcpy(size->media, best->key, sizeof(size->media)); 2034 else 2035 strlcpy(size->media, pwg->pwg, sizeof(size->media)); 2036 2037 size->width = best->width; 2038 size->length = best->length; 2039 size->bottom = best->bottom; 2040 size->left = best->left; 2041 size->right = best->right; 2042 size->top = best->top; 2043 2044 return (1); 2045 } 2046 2047 return (0); 2048 } 2049 2050 2051 /* 2052 * 'cups_is_close_media_db()' - Compare two media entries to see if they are 2053 * close to the same size. 2054 * 2055 * Currently we use 5 points (from PostScript) as the matching range... 2056 */ 2057 2058 static int /* O - 1 if the sizes are close */ 2059 cups_is_close_media_db( 2060 _cups_media_db_t *a, /* I - First media entries */ 2061 _cups_media_db_t *b) /* I - Second media entries */ 2062 { 2063 int dwidth, /* Difference in width */ 2064 dlength; /* Difference in length */ 2065 2066 2067 dwidth = a->width - b->width; 2068 dlength = a->length - b->length; 2069 2070 return (dwidth >= -176 && dwidth <= 176 && 2071 dlength >= -176 && dlength <= 176); 2072 } 2073 2074 2075 /* 2076 * 'cups_test_constraints()' - Test constraints. 2077 * 2078 * TODO: STR #4096 - Need to properly support media-col contraints... 2079 */ 2080 2081 static cups_array_t * /* O - Active constraints */ 2082 cups_test_constraints( 2083 cups_dinfo_t *dinfo, /* I - Destination information */ 2084 const char *new_option, /* I - Newly selected option */ 2085 const char *new_value, /* I - Newly selected value */ 2086 int num_options, /* I - Number of options */ 2087 cups_option_t *options, /* I - Options */ 2088 int *num_conflicts, /* O - Number of conflicting options */ 2089 cups_option_t **conflicts) /* O - Conflicting options */ 2090 { 2091 int i, /* Looping var */ 2092 match; /* Value matches? */ 2093 int num_matching; /* Number of matching options */ 2094 cups_option_t *matching; /* Matching options */ 2095 _cups_dconstres_t *c; /* Current constraint */ 2096 cups_array_t *active = NULL; /* Active constraints */ 2097 ipp_attribute_t *attr; /* Current attribute */ 2098 _ipp_value_t *attrval; /* Current attribute value */ 2099 const char *value; /* Current value */ 2100 char temp[1024]; /* Temporary string */ 2101 int int_value; /* Integer value */ 2102 int xres_value, /* Horizontal resolution */ 2103 yres_value; /* Vertical resolution */ 2104 ipp_res_t units_value; /* Resolution units */ 2105 2106 2107 for (c = (_cups_dconstres_t *)cupsArrayFirst(dinfo->constraints); 2108 c; 2109 c = (_cups_dconstres_t *)cupsArrayNext(dinfo->constraints)) 2110 { 2111 num_matching = 0; 2112 matching = NULL; 2113 2114 for (attr = ippFirstAttribute(c->collection); 2115 attr; 2116 attr = ippNextAttribute(c->collection)) 2117 { 2118 if (attr->value_tag == IPP_TAG_BEGIN_COLLECTION) 2119 break; /* TODO: STR #4096 */ 2120 2121 /* 2122 * Get the value for the current attribute in the constraint... 2123 */ 2124 2125 if (new_option && new_value && !strcmp(attr->name, new_option)) 2126 value = new_value; 2127 else if ((value = cupsGetOption(attr->name, num_options, 2128 options)) == NULL) 2129 value = cupsGetOption(attr->name, dinfo->num_defaults, dinfo->defaults); 2130 2131 if (!value) 2132 { 2133 /* 2134 * Not set so this constraint does not apply... 2135 */ 2136 2137 break; 2138 } 2139 2140 match = 0; 2141 2142 switch (attr->value_tag) 2143 { 2144 case IPP_TAG_INTEGER : 2145 case IPP_TAG_ENUM : 2146 int_value = atoi(value); 2147 2148 for (i = attr->num_values, attrval = attr->values; 2149 i > 0; 2150 i --, attrval ++) 2151 { 2152 if (attrval->integer == int_value) 2153 { 2154 match = 1; 2155 break; 2156 } 2157 } 2158 break; 2159 2160 case IPP_TAG_BOOLEAN : 2161 int_value = !strcmp(value, "true"); 2162 2163 for (i = attr->num_values, attrval = attr->values; 2164 i > 0; 2165 i --, attrval ++) 2166 { 2167 if (attrval->boolean == int_value) 2168 { 2169 match = 1; 2170 break; 2171 } 2172 } 2173 break; 2174 2175 case IPP_TAG_RANGE : 2176 int_value = atoi(value); 2177 2178 for (i = attr->num_values, attrval = attr->values; 2179 i > 0; 2180 i --, attrval ++) 2181 { 2182 if (int_value >= attrval->range.lower && 2183 int_value <= attrval->range.upper) 2184 { 2185 match = 1; 2186 break; 2187 } 2188 } 2189 break; 2190 2191 case IPP_TAG_RESOLUTION : 2192 if (sscanf(value, "%dx%d%15s", &xres_value, &yres_value, temp) != 3) 2193 { 2194 if (sscanf(value, "%d%15s", &xres_value, temp) != 2) 2195 break; 2196 2197 yres_value = xres_value; 2198 } 2199 2200 if (!strcmp(temp, "dpi")) 2201 units_value = IPP_RES_PER_INCH; 2202 else if (!strcmp(temp, "dpc") || !strcmp(temp, "dpcm")) 2203 units_value = IPP_RES_PER_CM; 2204 else 2205 break; 2206 2207 for (i = attr->num_values, attrval = attr->values; 2208 i > 0; 2209 i --, attrval ++) 2210 { 2211 if (attrval->resolution.xres == xres_value && 2212 attrval->resolution.yres == yres_value && 2213 attrval->resolution.units == units_value) 2214 { 2215 match = 1; 2216 break; 2217 } 2218 } 2219 break; 2220 2221 case IPP_TAG_TEXT : 2222 case IPP_TAG_NAME : 2223 case IPP_TAG_KEYWORD : 2224 case IPP_TAG_CHARSET : 2225 case IPP_TAG_URI : 2226 case IPP_TAG_URISCHEME : 2227 case IPP_TAG_MIMETYPE : 2228 case IPP_TAG_LANGUAGE : 2229 case IPP_TAG_TEXTLANG : 2230 case IPP_TAG_NAMELANG : 2231 for (i = attr->num_values, attrval = attr->values; 2232 i > 0; 2233 i --, attrval ++) 2234 { 2235 if (!strcmp(attrval->string.text, value)) 2236 { 2237 match = 1; 2238 break; 2239 } 2240 } 2241 break; 2242 2243 default : 2244 break; 2245 } 2246 2247 if (!match) 2248 break; 2249 2250 num_matching = cupsAddOption(attr->name, value, num_matching, &matching); 2251 } 2252 2253 if (!attr) 2254 { 2255 if (!active) 2256 active = cupsArrayNew(NULL, NULL); 2257 2258 cupsArrayAdd(active, c); 2259 2260 if (num_conflicts && conflicts) 2261 { 2262 cups_option_t *moption; /* Matching option */ 2263 2264 for (i = num_matching, moption = matching; i > 0; i --, moption ++) 2265 *num_conflicts = cupsAddOption(moption->name, moption->value, 2266 *num_conflicts, conflicts); 2267 } 2268 } 2269 2270 cupsFreeOptions(num_matching, matching); 2271 } 2272 2273 return (active); 2274 } 2275 2276 2277 /* 2278 * 'cups_update_ready()' - Update xxx-ready attributes for the printer. 2279 */ 2280 2281 static void 2282 cups_update_ready(http_t *http, /* I - Connection to destination */ 2283 cups_dinfo_t *dinfo) /* I - Destination information */ 2284 { 2285 ipp_t *request; /* Get-Printer-Attributes request */ 2286 static const char * const pattrs[] = /* Printer attributes we want */ 2287 { 2288 "finishings-col-ready", 2289 "finishings-ready", 2290 "job-finishings-col-ready", 2291 "job-finishings-ready", 2292 "media-col-ready", 2293 "media-ready" 2294 }; 2295 2296 2297 /* 2298 * Don't update more than once every 30 seconds... 2299 */ 2300 2301 if ((time(NULL) - dinfo->ready_time) < _CUPS_MEDIA_READY_TTL) 2302 return; 2303 2304 /* 2305 * Free any previous results... 2306 */ 2307 2308 if (dinfo->cached_flags & CUPS_MEDIA_FLAGS_READY) 2309 { 2310 cupsArrayDelete(dinfo->cached_db); 2311 dinfo->cached_db = NULL; 2312 dinfo->cached_flags = CUPS_MEDIA_FLAGS_DEFAULT; 2313 } 2314 2315 ippDelete(dinfo->ready_attrs); 2316 dinfo->ready_attrs = NULL; 2317 2318 cupsArrayDelete(dinfo->ready_db); 2319 dinfo->ready_db = NULL; 2320 2321 /* 2322 * Query the xxx-ready values... 2323 */ 2324 2325 request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES); 2326 ippSetVersion(request, dinfo->version / 10, dinfo->version % 10); 2327 2328 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, 2329 dinfo->uri); 2330 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", 2331 NULL, cupsUser()); 2332 ippAddStrings(request, IPP_TAG_OPERATION, IPP_CONST_TAG(IPP_TAG_KEYWORD), "requested-attributes", (int)(sizeof(pattrs) / sizeof(pattrs[0])), NULL, pattrs); 2333 2334 dinfo->ready_attrs = cupsDoRequest(http, request, dinfo->resource); 2335 2336 /* 2337 * Update the ready media database... 2338 */ 2339 2340 cups_create_media_db(dinfo, CUPS_MEDIA_FLAGS_READY); 2341 2342 /* 2343 * Update last lookup time and return... 2344 */ 2345 2346 dinfo->ready_time = time(NULL); 2347 } 2348