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