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