]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/dest-options.c
More work on temporary queues - implement support for them in the cupsDest APIs,
[thirdparty/cups.git] / cups / dest-options.c
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 * file is 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/OS X 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/OS X 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/OS X 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))", http, dest,
576 dest ? dest->name : ""));
577
578 /*
579 * Range check input...
580 */
581
582 if (!http || !dest)
583 return (NULL);
584
585 /*
586 * Get the printer URI and resource path...
587 */
588
589 if ((uri = _cupsGetDestResource(dest, resource, sizeof(resource))) == NULL)
590 return (NULL);
591
592 /*
593 * Get the supported attributes...
594 */
595
596 delay = 1;
597 prev_delay = 1;
598 tries = 0;
599 version = 20;
600
601 do
602 {
603 /*
604 * Send a Get-Printer-Attributes request...
605 */
606
607 request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
608 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
609 uri);
610 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
611 "requesting-user-name", NULL, cupsUser());
612 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
613 "requested-attributes",
614 (int)(sizeof(requested_attrs) / sizeof(requested_attrs[0])),
615 NULL, requested_attrs);
616 response = cupsDoRequest(http, request, resource);
617 status = cupsLastError();
618
619 if (status > IPP_STATUS_OK_IGNORED_OR_SUBSTITUTED)
620 {
621 DEBUG_printf(("cupsCopyDestSupported: Get-Printer-Attributes for '%s' "
622 "returned %s (%s)", dest->name, ippErrorString(status),
623 cupsLastErrorString()));
624
625 ippDelete(response);
626 response = NULL;
627
628 if (status == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED && version > 11)
629 version = 11;
630 else if (status == IPP_STATUS_ERROR_BUSY)
631 {
632 sleep((unsigned)delay);
633
634 delay = _cupsNextDelay(delay, &prev_delay);
635 }
636 else
637 return (NULL);
638 }
639
640 tries ++;
641 }
642 while (!response && tries < 10);
643
644 if (!response)
645 return (NULL);
646
647 /*
648 * Allocate a cups_dinfo_t structure and return it...
649 */
650
651 if ((dinfo = calloc(1, sizeof(cups_dinfo_t))) == NULL)
652 {
653 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
654 ippDelete(response);
655 return (NULL);
656 }
657
658 dinfo->version = version;
659 dinfo->uri = uri;
660 dinfo->resource = _cupsStrAlloc(resource);
661 dinfo->attrs = response;
662
663 return (dinfo);
664 }
665
666
667 /*
668 * 'cupsFindDestDefault()' - Find the default value(s) for the given option.
669 *
670 * The returned value is an IPP attribute. Use the @code ippGetBoolean@,
671 * @code ippGetCollection@, @code ippGetCount@, @code ippGetDate@,
672 * @code ippGetInteger@, @code ippGetOctetString@, @code ippGetRange@,
673 * @code ippGetResolution@, @code ippGetString@, and @code ippGetValueTag@
674 * functions to inspect the default value(s) as needed.
675 *
676 * @since CUPS 1.7/OS X 10.9@
677 */
678
679 ipp_attribute_t * /* O - Default attribute or @code NULL@ for none */
680 cupsFindDestDefault(
681 http_t *http, /* I - Connection to destination */
682 cups_dest_t *dest, /* I - Destination */
683 cups_dinfo_t *dinfo, /* I - Destination information */
684 const char *option) /* I - Option/attribute name */
685 {
686 char name[IPP_MAX_NAME]; /* Attribute name */
687
688
689 /*
690 * Range check input...
691 */
692
693 if (!http || !dest || !dinfo || !option)
694 {
695 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
696 return (NULL);
697 }
698
699 /*
700 * Find and return the attribute...
701 */
702
703 snprintf(name, sizeof(name), "%s-default", option);
704 return (ippFindAttribute(dinfo->attrs, name, IPP_TAG_ZERO));
705 }
706
707
708 /*
709 * 'cupsFindDestReady()' - Find the default value(s) for the given option.
710 *
711 * The returned value is an IPP attribute. Use the @code ippGetBoolean@,
712 * @code ippGetCollection@, @code ippGetCount@, @code ippGetDate@,
713 * @code ippGetInteger@, @code ippGetOctetString@, @code ippGetRange@,
714 * @code ippGetResolution@, @code ippGetString@, and @code ippGetValueTag@
715 * functions to inspect the default value(s) as needed.
716 *
717 * @since CUPS 1.7/OS X 10.9@
718 */
719
720 ipp_attribute_t * /* O - Default attribute or @code NULL@ for none */
721 cupsFindDestReady(
722 http_t *http, /* I - Connection to destination */
723 cups_dest_t *dest, /* I - Destination */
724 cups_dinfo_t *dinfo, /* I - Destination information */
725 const char *option) /* I - Option/attribute name */
726 {
727 char name[IPP_MAX_NAME]; /* Attribute name */
728
729
730 /*
731 * Range check input...
732 */
733
734 if (!http || !dest || !dinfo || !option)
735 {
736 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
737 return (NULL);
738 }
739
740 /*
741 * Find and return the attribute...
742 */
743
744 cups_update_ready(http, dinfo);
745
746 snprintf(name, sizeof(name), "%s-ready", option);
747 return (ippFindAttribute(dinfo->ready_attrs, name, IPP_TAG_ZERO));
748 }
749
750
751 /*
752 * 'cupsFindDestSupported()' - Find the default value(s) for the given option.
753 *
754 * The returned value is an IPP attribute. Use the @code ippGetBoolean@,
755 * @code ippGetCollection@, @code ippGetCount@, @code ippGetDate@,
756 * @code ippGetInteger@, @code ippGetOctetString@, @code ippGetRange@,
757 * @code ippGetResolution@, @code ippGetString@, and @code ippGetValueTag@
758 * functions to inspect the default value(s) as needed.
759 *
760 * @since CUPS 1.7/OS X 10.9@
761 */
762
763 ipp_attribute_t * /* O - Default attribute or @code NULL@ for none */
764 cupsFindDestSupported(
765 http_t *http, /* I - Connection to destination */
766 cups_dest_t *dest, /* I - Destination */
767 cups_dinfo_t *dinfo, /* I - Destination information */
768 const char *option) /* I - Option/attribute name */
769 {
770 char name[IPP_MAX_NAME]; /* Attribute name */
771
772
773 /*
774 * Range check input...
775 */
776
777 if (!http || !dest || !dinfo || !option)
778 {
779 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
780 return (NULL);
781 }
782
783 /*
784 * Find and return the attribute...
785 */
786
787 snprintf(name, sizeof(name), "%s-supported", option);
788 return (ippFindAttribute(dinfo->attrs, name, IPP_TAG_ZERO));
789 }
790
791
792 /*
793 * 'cupsFreeDestInfo()' - Free destination information obtained using
794 * @link cupsCopyDestInfo@.
795 */
796
797 void
798 cupsFreeDestInfo(cups_dinfo_t *dinfo) /* I - Destination information */
799 {
800 /*
801 * Range check input...
802 */
803
804 if (!dinfo)
805 return;
806
807 /*
808 * Free memory and return...
809 */
810
811 _cupsStrFree(dinfo->resource);
812
813 cupsArrayDelete(dinfo->constraints);
814 cupsArrayDelete(dinfo->resolvers);
815
816 cupsArrayDelete(dinfo->localizations);
817
818 cupsArrayDelete(dinfo->media_db);
819
820 cupsArrayDelete(dinfo->cached_db);
821
822 ippDelete(dinfo->ready_attrs);
823 cupsArrayDelete(dinfo->ready_db);
824
825 ippDelete(dinfo->attrs);
826
827 free(dinfo);
828 }
829
830
831 /*
832 * 'cupsGetDestMediaByIndex()' - Get a media name, dimension, and margins for a
833 * specific size.
834 *
835 * The @code flags@ parameter determines which set of media are indexed. For
836 * example, passing @code CUPS_MEDIA_FLAGS_BORDERLESS@ will get the Nth
837 * borderless size supported by the printer.
838 *
839 * @since CUPS 1.7/OS X 10.9@
840 */
841
842 int /* O - 1 on success, 0 on failure */
843 cupsGetDestMediaByIndex(
844 http_t *http, /* I - Connection to destination */
845 cups_dest_t *dest, /* I - Destination */
846 cups_dinfo_t *dinfo, /* I - Destination information */
847 int n, /* I - Media size number (0-based) */
848 unsigned flags, /* I - Media flags */
849 cups_size_t *size) /* O - Media size information */
850 {
851 _cups_media_db_t *nsize; /* Size for N */
852 pwg_media_t *pwg; /* PWG media name for size */
853
854
855 /*
856 * Range check input...
857 */
858
859 if (size)
860 memset(size, 0, sizeof(cups_size_t));
861
862 if (!http || !dest || !dinfo || n < 0 || !size)
863 {
864 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
865 return (0);
866 }
867
868 /*
869 * Load media list as needed...
870 */
871
872 if (flags & CUPS_MEDIA_FLAGS_READY)
873 cups_update_ready(http, dinfo);
874
875 if (!dinfo->cached_db || dinfo->cached_flags != flags)
876 cups_create_cached(http, dinfo, flags);
877
878 /*
879 * Copy the size over and return...
880 */
881
882 if ((nsize = (_cups_media_db_t *)cupsArrayIndex(dinfo->cached_db, n)) == NULL)
883 {
884 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
885 return (0);
886 }
887
888 if (nsize->size_name)
889 strlcpy(size->media, nsize->size_name, sizeof(size->media));
890 else if (nsize->key)
891 strlcpy(size->media, nsize->key, sizeof(size->media));
892 else if ((pwg = pwgMediaForSize(nsize->width, nsize->length)) != NULL)
893 strlcpy(size->media, pwg->pwg, sizeof(size->media));
894 else
895 {
896 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
897 return (0);
898 }
899
900 size->width = nsize->width;
901 size->length = nsize->length;
902 size->bottom = nsize->bottom;
903 size->left = nsize->left;
904 size->right = nsize->right;
905 size->top = nsize->top;
906
907 return (1);
908 }
909
910
911 /*
912 * 'cupsGetDestMediaByName()' - Get media names, dimensions, and margins.
913 *
914 * The "media" string is a PWG media name. "Flags" provides some matching
915 * guidance (multiple flags can be combined):
916 *
917 * CUPS_MEDIA_FLAGS_DEFAULT = find the closest size supported by the printer,
918 * CUPS_MEDIA_FLAGS_BORDERLESS = find a borderless size,
919 * CUPS_MEDIA_FLAGS_DUPLEX = find a size compatible with 2-sided printing,
920 * CUPS_MEDIA_FLAGS_EXACT = find an exact match for the size, and
921 * CUPS_MEDIA_FLAGS_READY = if the printer supports media sensing, find the
922 * size amongst the "ready" media.
923 *
924 * The matching result (if any) is returned in the "cups_size_t" structure.
925 *
926 * Returns 1 when there is a match and 0 if there is not a match.
927 *
928 * @since CUPS 1.6/OS X 10.8@
929 */
930
931 int /* O - 1 on match, 0 on failure */
932 cupsGetDestMediaByName(
933 http_t *http, /* I - Connection to destination */
934 cups_dest_t *dest, /* I - Destination */
935 cups_dinfo_t *dinfo, /* I - Destination information */
936 const char *media, /* I - Media name */
937 unsigned flags, /* I - Media matching flags */
938 cups_size_t *size) /* O - Media size information */
939 {
940 pwg_media_t *pwg; /* PWG media info */
941
942
943 /*
944 * Range check input...
945 */
946
947 if (size)
948 memset(size, 0, sizeof(cups_size_t));
949
950 if (!http || !dest || !dinfo || !media || !size)
951 {
952 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
953 return (0);
954 }
955
956 /*
957 * Lookup the media size name...
958 */
959
960 if ((pwg = pwgMediaForPWG(media)) == NULL)
961 if ((pwg = pwgMediaForLegacy(media)) == NULL)
962 {
963 DEBUG_printf(("1cupsGetDestMediaByName: Unknown size '%s'.", media));
964 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unknown media size name."), 1);
965 return (0);
966 }
967
968 /*
969 * Lookup the size...
970 */
971
972 return (cups_get_media_db(http, dinfo, pwg, flags, size));
973 }
974
975
976 /*
977 * 'cupsGetDestMediaBySize()' - Get media names, dimensions, and margins.
978 *
979 * "Width" and "length" are the dimensions in hundredths of millimeters.
980 * "Flags" provides some matching guidance (multiple flags can be combined):
981 *
982 * CUPS_MEDIA_FLAGS_DEFAULT = find the closest size supported by the printer,
983 * CUPS_MEDIA_FLAGS_BORDERLESS = find a borderless size,
984 * CUPS_MEDIA_FLAGS_DUPLEX = find a size compatible with 2-sided printing,
985 * CUPS_MEDIA_FLAGS_EXACT = find an exact match for the size, and
986 * CUPS_MEDIA_FLAGS_READY = if the printer supports media sensing, find the
987 * size amongst the "ready" media.
988 *
989 * The matching result (if any) is returned in the "cups_size_t" structure.
990 *
991 * Returns 1 when there is a match and 0 if there is not a match.
992 *
993 * @since CUPS 1.6/OS X 10.8@
994 */
995
996 int /* O - 1 on match, 0 on failure */
997 cupsGetDestMediaBySize(
998 http_t *http, /* I - Connection to destination */
999 cups_dest_t *dest, /* I - Destination */
1000 cups_dinfo_t *dinfo, /* I - Destination information */
1001 int width, /* I - Media width in hundredths of
1002 * of millimeters */
1003 int length, /* I - Media length in hundredths of
1004 * of millimeters */
1005 unsigned flags, /* I - Media matching flags */
1006 cups_size_t *size) /* O - Media size information */
1007 {
1008 pwg_media_t *pwg; /* PWG media info */
1009
1010
1011 /*
1012 * Range check input...
1013 */
1014
1015 if (size)
1016 memset(size, 0, sizeof(cups_size_t));
1017
1018 if (!http || !dest || !dinfo || width <= 0 || length <= 0 || !size)
1019 {
1020 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
1021 return (0);
1022 }
1023
1024 /*
1025 * Lookup the media size name...
1026 */
1027
1028 if ((pwg = pwgMediaForSize(width, length)) == NULL)
1029 {
1030 DEBUG_printf(("1cupsGetDestMediaBySize: Invalid size %dx%d.", width,
1031 length));
1032 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Invalid media size."), 1);
1033 return (0);
1034 }
1035
1036 /*
1037 * Lookup the size...
1038 */
1039
1040 return (cups_get_media_db(http, dinfo, pwg, flags, size));
1041 }
1042
1043
1044 /*
1045 * 'cupsGetDestMediaCount()' - Get the number of sizes supported by a
1046 * destination.
1047 *
1048 * The @code flags@ parameter determines the set of media sizes that are
1049 * counted. For example, passing @code CUPS_MEDIA_FLAGS_BORDERLESS@ will return
1050 * the number of borderless sizes.
1051 *
1052 * @since CUPS 1.7/OS X 10.9@
1053 */
1054
1055 int /* O - Number of sizes */
1056 cupsGetDestMediaCount(
1057 http_t *http, /* I - Connection to destination */
1058 cups_dest_t *dest, /* I - Destination */
1059 cups_dinfo_t *dinfo, /* I - Destination information */
1060 unsigned flags) /* I - Media flags */
1061 {
1062 /*
1063 * Range check input...
1064 */
1065
1066 if (!http || !dest || !dinfo)
1067 {
1068 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
1069 return (0);
1070 }
1071
1072 /*
1073 * Load media list as needed...
1074 */
1075
1076 if (flags & CUPS_MEDIA_FLAGS_READY)
1077 cups_update_ready(http, dinfo);
1078
1079 if (!dinfo->cached_db || dinfo->cached_flags != flags)
1080 cups_create_cached(http, dinfo, flags);
1081
1082 return (cupsArrayCount(dinfo->cached_db));
1083 }
1084
1085
1086 /*
1087 * 'cupsGetDestMediaDefault()' - Get the default size for a destination.
1088 *
1089 * The @code flags@ parameter determines which default size is returned. For
1090 * example, passing @code CUPS_MEDIA_FLAGS_BORDERLESS@ will return the default
1091 * borderless size, typically US Letter or A4, but sometimes 4x6 photo media.
1092 *
1093 * @since CUPS 1.7/OS X 10.9@
1094 */
1095
1096 int /* O - 1 on success, 0 on failure */
1097 cupsGetDestMediaDefault(
1098 http_t *http, /* I - Connection to destination */
1099 cups_dest_t *dest, /* I - Destination */
1100 cups_dinfo_t *dinfo, /* I - Destination information */
1101 unsigned flags, /* I - Media flags */
1102 cups_size_t *size) /* O - Media size information */
1103 {
1104 const char *media; /* Default media size */
1105
1106
1107 /*
1108 * Range check input...
1109 */
1110
1111 if (size)
1112 memset(size, 0, sizeof(cups_size_t));
1113
1114 if (!http || !dest || !dinfo || !size)
1115 {
1116 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
1117 return (0);
1118 }
1119
1120 /*
1121 * Get the default media size, if any...
1122 */
1123
1124 if ((media = cupsGetOption("media", dest->num_options,
1125 dest->options)) == NULL)
1126 media = "na_letter_8.5x11in";
1127
1128 if (cupsGetDestMediaByName(http, dest, dinfo, media, flags, size))
1129 return (1);
1130
1131 if (strcmp(media, "na_letter_8.5x11in") &&
1132 cupsGetDestMediaByName(http, dest, dinfo, "iso_a4_210x297mm", flags,
1133 size))
1134 return (1);
1135
1136 if (strcmp(media, "iso_a4_210x297mm") &&
1137 cupsGetDestMediaByName(http, dest, dinfo, "na_letter_8.5x11in", flags,
1138 size))
1139 return (1);
1140
1141 if ((flags & CUPS_MEDIA_FLAGS_BORDERLESS) &&
1142 cupsGetDestMediaByName(http, dest, dinfo, "na_index_4x6in", flags, size))
1143 return (1);
1144
1145 /*
1146 * Fall back to the first matching media size...
1147 */
1148
1149 return (cupsGetDestMediaByIndex(http, dest, dinfo, 0, flags, size));
1150 }
1151
1152
1153 /*
1154 * 'cups_add_dconstres()' - Add a constraint or resolver to an array.
1155 */
1156
1157 static void
1158 cups_add_dconstres(
1159 cups_array_t *a, /* I - Array */
1160 ipp_t *collection) /* I - Collection value */
1161 {
1162 ipp_attribute_t *attr; /* Attribute */
1163 _cups_dconstres_t *temp; /* Current constraint/resolver */
1164
1165
1166 if ((attr = ippFindAttribute(collection, "resolver-name",
1167 IPP_TAG_NAME)) == NULL)
1168 return;
1169
1170 if ((temp = calloc(1, sizeof(_cups_dconstres_t))) == NULL)
1171 return;
1172
1173 temp->name = attr->values[0].string.text;
1174 temp->collection = collection;
1175
1176 cupsArrayAdd(a, temp);
1177 }
1178
1179
1180 /*
1181 * 'cups_compare_dconstres()' - Compare to resolver entries.
1182 */
1183
1184 static int /* O - Result of comparison */
1185 cups_compare_dconstres(
1186 _cups_dconstres_t *a, /* I - First resolver */
1187 _cups_dconstres_t *b) /* I - Second resolver */
1188 {
1189 return (strcmp(a->name, b->name));
1190 }
1191
1192
1193 /*
1194 * 'cups_compare_media_db()' - Compare two media entries.
1195 */
1196
1197 static int /* O - Result of comparison */
1198 cups_compare_media_db(
1199 _cups_media_db_t *a, /* I - First media entries */
1200 _cups_media_db_t *b) /* I - Second media entries */
1201 {
1202 int result; /* Result of comparison */
1203
1204
1205 if ((result = a->width - b->width) == 0)
1206 result = a->length - b->length;
1207
1208 return (result);
1209 }
1210
1211
1212 /*
1213 * 'cups_copy_media_db()' - Copy a media entry.
1214 */
1215
1216 static _cups_media_db_t * /* O - New media entry */
1217 cups_copy_media_db(
1218 _cups_media_db_t *mdb) /* I - Media entry to copy */
1219 {
1220 _cups_media_db_t *temp; /* New media entry */
1221
1222
1223 if ((temp = calloc(1, sizeof(_cups_media_db_t))) == NULL)
1224 return (NULL);
1225
1226 if (mdb->color)
1227 temp->color = _cupsStrAlloc(mdb->color);
1228 if (mdb->key)
1229 temp->key = _cupsStrAlloc(mdb->key);
1230 if (mdb->info)
1231 temp->info = _cupsStrAlloc(mdb->info);
1232 if (mdb->size_name)
1233 temp->size_name = _cupsStrAlloc(mdb->size_name);
1234 if (mdb->source)
1235 temp->source = _cupsStrAlloc(mdb->source);
1236 if (mdb->type)
1237 temp->type = _cupsStrAlloc(mdb->type);
1238
1239 temp->width = mdb->width;
1240 temp->length = mdb->length;
1241 temp->bottom = mdb->bottom;
1242 temp->left = mdb->left;
1243 temp->right = mdb->right;
1244 temp->top = mdb->top;
1245
1246 return (temp);
1247 }
1248
1249
1250 /*
1251 * 'cups_create_cached()' - Create the media selection cache.
1252 */
1253
1254 static void
1255 cups_create_cached(http_t *http, /* I - Connection to destination */
1256 cups_dinfo_t *dinfo, /* I - Destination information */
1257 unsigned flags) /* I - Media selection flags */
1258 {
1259 cups_array_t *db; /* Media database array to use */
1260 _cups_media_db_t *mdb, /* Media database entry */
1261 *first; /* First entry this size */
1262
1263
1264 DEBUG_printf(("3cups_create_cached(http=%p, dinfo=%p, flags=%u)", http, dinfo, flags));
1265
1266 if (dinfo->cached_db)
1267 cupsArrayDelete(dinfo->cached_db);
1268
1269 dinfo->cached_db = cupsArrayNew(NULL, NULL);
1270 dinfo->cached_flags = flags;
1271
1272 if (flags & CUPS_MEDIA_FLAGS_READY)
1273 {
1274 DEBUG_puts("4cups_create_cached: ready media");
1275
1276 cups_update_ready(http, dinfo);
1277 db = dinfo->ready_db;
1278 }
1279 else
1280 {
1281 DEBUG_puts("4cups_create_cached: supported media");
1282
1283 if (!dinfo->media_db)
1284 cups_create_media_db(dinfo, CUPS_MEDIA_FLAGS_DEFAULT);
1285
1286 db = dinfo->media_db;
1287 }
1288
1289 for (mdb = (_cups_media_db_t *)cupsArrayFirst(db), first = mdb;
1290 mdb;
1291 mdb = (_cups_media_db_t *)cupsArrayNext(db))
1292 {
1293 DEBUG_printf(("4cups_create_cached: %p key=\"%s\", type=\"%s\", %dx%d, B%d L%d R%d T%d", mdb, mdb->key, mdb->type, mdb->width, mdb->length, mdb->bottom, mdb->left, mdb->right, mdb->top));
1294
1295 if (flags & CUPS_MEDIA_FLAGS_BORDERLESS)
1296 {
1297 if (!mdb->left && !mdb->right && !mdb->top && !mdb->bottom)
1298 {
1299 DEBUG_printf(("4cups_create_cached: add %p", mdb));
1300 cupsArrayAdd(dinfo->cached_db, mdb);
1301 }
1302 }
1303 else if (flags & CUPS_MEDIA_FLAGS_DUPLEX)
1304 {
1305 if (first->width != mdb->width || first->length != mdb->length)
1306 {
1307 DEBUG_printf(("4cups_create_cached: add %p", first));
1308 cupsArrayAdd(dinfo->cached_db, first);
1309 first = mdb;
1310 }
1311 else if (mdb->left >= first->left && mdb->right >= first->right && mdb->top >= first->top && mdb->bottom >= first->bottom &&
1312 (mdb->left != first->left || mdb->right != first->right || mdb->top != first->top || mdb->bottom != first->bottom))
1313 first = mdb;
1314 }
1315 else
1316 {
1317 DEBUG_printf(("4cups_create_cached: add %p", mdb));
1318 cupsArrayAdd(dinfo->cached_db, mdb);
1319 }
1320 }
1321
1322 if (flags & CUPS_MEDIA_FLAGS_DUPLEX)
1323 {
1324 DEBUG_printf(("4cups_create_cached: add %p", first));
1325 cupsArrayAdd(dinfo->cached_db, first);
1326 }
1327 }
1328
1329
1330 /*
1331 * 'cups_create_constraints()' - Create the constraints and resolvers arrays.
1332 */
1333
1334 static void
1335 cups_create_constraints(
1336 cups_dinfo_t *dinfo) /* I - Destination information */
1337 {
1338 int i; /* Looping var */
1339 ipp_attribute_t *attr; /* Attribute */
1340 _ipp_value_t *val; /* Current value */
1341
1342
1343 dinfo->constraints = cupsArrayNew3(NULL, NULL, NULL, 0, NULL,
1344 (cups_afree_func_t)free);
1345 dinfo->resolvers = cupsArrayNew3((cups_array_func_t)cups_compare_dconstres,
1346 NULL, NULL, 0, NULL,
1347 (cups_afree_func_t)free);
1348
1349 if ((attr = ippFindAttribute(dinfo->attrs, "job-constraints-supported",
1350 IPP_TAG_BEGIN_COLLECTION)) != NULL)
1351 {
1352 for (i = attr->num_values, val = attr->values; i > 0; i --, val ++)
1353 cups_add_dconstres(dinfo->constraints, val->collection);
1354 }
1355
1356 if ((attr = ippFindAttribute(dinfo->attrs, "job-resolvers-supported",
1357 IPP_TAG_BEGIN_COLLECTION)) != NULL)
1358 {
1359 for (i = attr->num_values, val = attr->values; i > 0; i --, val ++)
1360 cups_add_dconstres(dinfo->resolvers, val->collection);
1361 }
1362 }
1363
1364
1365 /*
1366 * 'cups_create_defaults()' - Create the -default option array.
1367 *
1368 * TODO: Need to support collection defaults...
1369 */
1370
1371 static void
1372 cups_create_defaults(
1373 cups_dinfo_t *dinfo) /* I - Destination information */
1374 {
1375 ipp_attribute_t *attr; /* Current attribute */
1376 char name[IPP_MAX_NAME + 1],
1377 /* Current name */
1378 *nameptr, /* Pointer into current name */
1379 value[2048]; /* Current value */
1380
1381
1382 /*
1383 * Iterate through the printer attributes looking for xxx-default and adding
1384 * xxx=value to the defaults option array.
1385 */
1386
1387 for (attr = ippFirstAttribute(dinfo->attrs);
1388 attr;
1389 attr = ippNextAttribute(dinfo->attrs))
1390 {
1391 if (!attr->name || attr->group_tag != IPP_TAG_PRINTER)
1392 continue;
1393
1394 if (attr->value_tag == IPP_TAG_BEGIN_COLLECTION)
1395 continue; /* TODO: STR #4096 */
1396
1397 if ((nameptr = attr->name + strlen(attr->name) - 8) <= attr->name ||
1398 strcmp(nameptr, "-default"))
1399 continue;
1400
1401 strlcpy(name, attr->name, sizeof(name));
1402 if ((nameptr = name + strlen(name) - 8) <= name ||
1403 strcmp(nameptr, "-default"))
1404 continue;
1405
1406 *nameptr = '\0';
1407
1408 if (ippAttributeString(attr, value, sizeof(value)) >= sizeof(value))
1409 continue;
1410
1411 dinfo->num_defaults = cupsAddOption(name, value, dinfo->num_defaults,
1412 &dinfo->defaults);
1413 }
1414 }
1415
1416
1417 /*
1418 * 'cups_create_media_db()' - Create the media database.
1419 */
1420
1421 static void
1422 cups_create_media_db(
1423 cups_dinfo_t *dinfo, /* I - Destination information */
1424 unsigned flags) /* I - Media flags */
1425 {
1426 int i; /* Looping var */
1427 _ipp_value_t *val; /* Current value */
1428 ipp_attribute_t *media_col_db, /* media-col-database */
1429 *media_attr, /* media-xxx */
1430 *x_dimension, /* x-dimension */
1431 *y_dimension; /* y-dimension */
1432 pwg_media_t *pwg; /* PWG media info */
1433 cups_array_t *db; /* New media database array */
1434 _cups_media_db_t mdb; /* Media entry */
1435
1436
1437 db = cupsArrayNew3((cups_array_func_t)cups_compare_media_db,
1438 NULL, NULL, 0,
1439 (cups_acopy_func_t)cups_copy_media_db,
1440 (cups_afree_func_t)cups_free_media_db);
1441
1442 if (flags == CUPS_MEDIA_FLAGS_READY)
1443 {
1444 dinfo->ready_db = db;
1445
1446 media_col_db = ippFindAttribute(dinfo->ready_attrs, "media-col-ready",
1447 IPP_TAG_BEGIN_COLLECTION);
1448 media_attr = ippFindAttribute(dinfo->ready_attrs, "media-ready",
1449 IPP_TAG_ZERO);
1450 }
1451 else
1452 {
1453 dinfo->media_db = db;
1454 dinfo->min_size.width = INT_MAX;
1455 dinfo->min_size.length = INT_MAX;
1456 dinfo->max_size.width = 0;
1457 dinfo->max_size.length = 0;
1458
1459 media_col_db = ippFindAttribute(dinfo->attrs, "media-col-database",
1460 IPP_TAG_BEGIN_COLLECTION);
1461 media_attr = ippFindAttribute(dinfo->attrs, "media-supported",
1462 IPP_TAG_ZERO);
1463 }
1464
1465 if (media_col_db)
1466 {
1467 _ipp_value_t *custom = NULL; /* Custom size range value */
1468
1469 for (i = media_col_db->num_values, val = media_col_db->values;
1470 i > 0;
1471 i --, val ++)
1472 {
1473 memset(&mdb, 0, sizeof(mdb));
1474
1475 if ((media_attr = ippFindAttribute(val->collection, "media-size",
1476 IPP_TAG_BEGIN_COLLECTION)) != NULL)
1477 {
1478 ipp_t *media_size = media_attr->values[0].collection;
1479 /* media-size collection value */
1480
1481 if ((x_dimension = ippFindAttribute(media_size, "x-dimension",
1482 IPP_TAG_INTEGER)) != NULL &&
1483 (y_dimension = ippFindAttribute(media_size, "y-dimension",
1484 IPP_TAG_INTEGER)) != NULL)
1485 {
1486 /*
1487 * Fixed size...
1488 */
1489
1490 mdb.width = x_dimension->values[0].integer;
1491 mdb.length = y_dimension->values[0].integer;
1492 }
1493 else if ((x_dimension = ippFindAttribute(media_size, "x-dimension",
1494 IPP_TAG_INTEGER)) != NULL &&
1495 (y_dimension = ippFindAttribute(media_size, "y-dimension",
1496 IPP_TAG_RANGE)) != NULL)
1497 {
1498 /*
1499 * Roll limits...
1500 */
1501
1502 mdb.width = x_dimension->values[0].integer;
1503 mdb.length = y_dimension->values[0].range.upper;
1504 }
1505 else if (flags != CUPS_MEDIA_FLAGS_READY &&
1506 (x_dimension = ippFindAttribute(media_size, "x-dimension",
1507 IPP_TAG_RANGE)) != NULL &&
1508 (y_dimension = ippFindAttribute(media_size, "y-dimension",
1509 IPP_TAG_RANGE)) != NULL)
1510 {
1511 /*
1512 * Custom size range; save this as the custom size value with default
1513 * margins, then continue; we'll capture the real margins below...
1514 */
1515
1516 custom = val;
1517
1518 dinfo->min_size.width = x_dimension->values[0].range.lower;
1519 dinfo->min_size.length = y_dimension->values[0].range.lower;
1520 dinfo->min_size.left =
1521 dinfo->min_size.right = 635; /* Default 1/4" side margins */
1522 dinfo->min_size.top =
1523 dinfo->min_size.bottom = 1270; /* Default 1/2" top/bottom margins */
1524
1525 dinfo->max_size.width = x_dimension->values[0].range.upper;
1526 dinfo->max_size.length = y_dimension->values[0].range.upper;
1527 dinfo->max_size.left =
1528 dinfo->max_size.right = 635; /* Default 1/4" side margins */
1529 dinfo->max_size.top =
1530 dinfo->max_size.bottom = 1270; /* Default 1/2" top/bottom margins */
1531 continue;
1532 }
1533 }
1534
1535 if ((media_attr = ippFindAttribute(val->collection, "media-color",
1536 IPP_TAG_ZERO)) != NULL &&
1537 (media_attr->value_tag == IPP_TAG_NAME ||
1538 media_attr->value_tag == IPP_TAG_NAMELANG ||
1539 media_attr->value_tag == IPP_TAG_KEYWORD))
1540 mdb.color = media_attr->values[0].string.text;
1541
1542 if ((media_attr = ippFindAttribute(val->collection, "media-info",
1543 IPP_TAG_TEXT)) != NULL)
1544 mdb.info = media_attr->values[0].string.text;
1545
1546 if ((media_attr = ippFindAttribute(val->collection, "media-key",
1547 IPP_TAG_ZERO)) != NULL &&
1548 (media_attr->value_tag == IPP_TAG_NAME ||
1549 media_attr->value_tag == IPP_TAG_NAMELANG ||
1550 media_attr->value_tag == IPP_TAG_KEYWORD))
1551 mdb.key = media_attr->values[0].string.text;
1552
1553 if ((media_attr = ippFindAttribute(val->collection, "media-size-name",
1554 IPP_TAG_ZERO)) != NULL &&
1555 (media_attr->value_tag == IPP_TAG_NAME ||
1556 media_attr->value_tag == IPP_TAG_NAMELANG ||
1557 media_attr->value_tag == IPP_TAG_KEYWORD))
1558 mdb.size_name = media_attr->values[0].string.text;
1559
1560 if ((media_attr = ippFindAttribute(val->collection, "media-source",
1561 IPP_TAG_ZERO)) != NULL &&
1562 (media_attr->value_tag == IPP_TAG_NAME ||
1563 media_attr->value_tag == IPP_TAG_NAMELANG ||
1564 media_attr->value_tag == IPP_TAG_KEYWORD))
1565 mdb.source = media_attr->values[0].string.text;
1566
1567 if ((media_attr = ippFindAttribute(val->collection, "media-type",
1568 IPP_TAG_ZERO)) != NULL &&
1569 (media_attr->value_tag == IPP_TAG_NAME ||
1570 media_attr->value_tag == IPP_TAG_NAMELANG ||
1571 media_attr->value_tag == IPP_TAG_KEYWORD))
1572 mdb.type = media_attr->values[0].string.text;
1573
1574 if ((media_attr = ippFindAttribute(val->collection, "media-bottom-margin",
1575 IPP_TAG_INTEGER)) != NULL)
1576 mdb.bottom = media_attr->values[0].integer;
1577
1578 if ((media_attr = ippFindAttribute(val->collection, "media-left-margin",
1579 IPP_TAG_INTEGER)) != NULL)
1580 mdb.left = media_attr->values[0].integer;
1581
1582 if ((media_attr = ippFindAttribute(val->collection, "media-right-margin",
1583 IPP_TAG_INTEGER)) != NULL)
1584 mdb.right = media_attr->values[0].integer;
1585
1586 if ((media_attr = ippFindAttribute(val->collection, "media-top-margin",
1587 IPP_TAG_INTEGER)) != NULL)
1588 mdb.top = media_attr->values[0].integer;
1589
1590 cupsArrayAdd(db, &mdb);
1591 }
1592
1593 if (custom)
1594 {
1595 if ((media_attr = ippFindAttribute(custom->collection,
1596 "media-bottom-margin",
1597 IPP_TAG_INTEGER)) != NULL)
1598 {
1599 dinfo->min_size.top =
1600 dinfo->max_size.top = media_attr->values[0].integer;
1601 }
1602
1603 if ((media_attr = ippFindAttribute(custom->collection,
1604 "media-left-margin",
1605 IPP_TAG_INTEGER)) != NULL)
1606 {
1607 dinfo->min_size.left =
1608 dinfo->max_size.left = media_attr->values[0].integer;
1609 }
1610
1611 if ((media_attr = ippFindAttribute(custom->collection,
1612 "media-right-margin",
1613 IPP_TAG_INTEGER)) != NULL)
1614 {
1615 dinfo->min_size.right =
1616 dinfo->max_size.right = media_attr->values[0].integer;
1617 }
1618
1619 if ((media_attr = ippFindAttribute(custom->collection,
1620 "media-top-margin",
1621 IPP_TAG_INTEGER)) != NULL)
1622 {
1623 dinfo->min_size.top =
1624 dinfo->max_size.top = media_attr->values[0].integer;
1625 }
1626 }
1627 }
1628 else if (media_attr &&
1629 (media_attr->value_tag == IPP_TAG_NAME ||
1630 media_attr->value_tag == IPP_TAG_NAMELANG ||
1631 media_attr->value_tag == IPP_TAG_KEYWORD))
1632 {
1633 memset(&mdb, 0, sizeof(mdb));
1634
1635 mdb.left =
1636 mdb.right = 635; /* Default 1/4" side margins */
1637 mdb.top =
1638 mdb.bottom = 1270; /* Default 1/2" top/bottom margins */
1639
1640 for (i = media_attr->num_values, val = media_attr->values;
1641 i > 0;
1642 i --, val ++)
1643 {
1644 if ((pwg = pwgMediaForPWG(val->string.text)) == NULL)
1645 if ((pwg = pwgMediaForLegacy(val->string.text)) == NULL)
1646 {
1647 DEBUG_printf(("3cups_create_media_db: Ignoring unknown size '%s'.",
1648 val->string.text));
1649 continue;
1650 }
1651
1652 mdb.width = pwg->width;
1653 mdb.length = pwg->length;
1654
1655 if (flags != CUPS_MEDIA_FLAGS_READY &&
1656 !strncmp(val->string.text, "custom_min_", 11))
1657 {
1658 mdb.size_name = NULL;
1659 dinfo->min_size = mdb;
1660 }
1661 else if (flags != CUPS_MEDIA_FLAGS_READY &&
1662 !strncmp(val->string.text, "custom_max_", 11))
1663 {
1664 mdb.size_name = NULL;
1665 dinfo->max_size = mdb;
1666 }
1667 else
1668 {
1669 mdb.size_name = val->string.text;
1670
1671 cupsArrayAdd(db, &mdb);
1672 }
1673 }
1674 }
1675 }
1676
1677
1678 /*
1679 * 'cups_free_media_cb()' - Free a media entry.
1680 */
1681
1682 static void
1683 cups_free_media_db(
1684 _cups_media_db_t *mdb) /* I - Media entry to free */
1685 {
1686 if (mdb->color)
1687 _cupsStrFree(mdb->color);
1688 if (mdb->key)
1689 _cupsStrFree(mdb->key);
1690 if (mdb->info)
1691 _cupsStrFree(mdb->info);
1692 if (mdb->size_name)
1693 _cupsStrFree(mdb->size_name);
1694 if (mdb->source)
1695 _cupsStrFree(mdb->source);
1696 if (mdb->type)
1697 _cupsStrFree(mdb->type);
1698
1699 free(mdb);
1700 }
1701
1702
1703 /*
1704 * 'cups_get_media_db()' - Lookup the media entry for a given size.
1705 */
1706
1707 static int /* O - 1 on match, 0 on failure */
1708 cups_get_media_db(http_t *http, /* I - Connection to destination */
1709 cups_dinfo_t *dinfo, /* I - Destination information */
1710 pwg_media_t *pwg, /* I - PWG media info */
1711 unsigned flags, /* I - Media matching flags */
1712 cups_size_t *size) /* O - Media size/margin/name info */
1713 {
1714 cups_array_t *db; /* Which media database to query */
1715 _cups_media_db_t *mdb, /* Current media database entry */
1716 *best = NULL, /* Best matching entry */
1717 key; /* Search key */
1718
1719
1720 /*
1721 * Create the media database as needed...
1722 */
1723
1724 if (flags & CUPS_MEDIA_FLAGS_READY)
1725 {
1726 cups_update_ready(http, dinfo);
1727 db = dinfo->ready_db;
1728 }
1729 else
1730 {
1731 if (!dinfo->media_db)
1732 cups_create_media_db(dinfo, CUPS_MEDIA_FLAGS_DEFAULT);
1733
1734 db = dinfo->media_db;
1735 }
1736
1737 /*
1738 * Find a match...
1739 */
1740
1741 memset(&key, 0, sizeof(key));
1742 key.width = pwg->width;
1743 key.length = pwg->length;
1744
1745 if ((mdb = cupsArrayFind(db, &key)) != NULL)
1746 {
1747 /*
1748 * Found an exact match, let's figure out the best margins for the flags
1749 * supplied...
1750 */
1751
1752 best = mdb;
1753
1754 if (flags & CUPS_MEDIA_FLAGS_BORDERLESS)
1755 {
1756 /*
1757 * Look for the smallest margins...
1758 */
1759
1760 if (best->left != 0 || best->right != 0 || best->top != 0 || best->bottom != 0)
1761 {
1762 for (mdb = (_cups_media_db_t *)cupsArrayNext(db);
1763 mdb && !cups_compare_media_db(mdb, &key);
1764 mdb = (_cups_media_db_t *)cupsArrayNext(db))
1765 {
1766 if (mdb->left <= best->left && mdb->right <= best->right &&
1767 mdb->top <= best->top && mdb->bottom <= best->bottom)
1768 {
1769 best = mdb;
1770 if (mdb->left == 0 && mdb->right == 0 && mdb->bottom == 0 &&
1771 mdb->top == 0)
1772 break;
1773 }
1774 }
1775 }
1776
1777 /*
1778 * If we need an exact match, return no-match if the size is not
1779 * borderless.
1780 */
1781
1782 if ((flags & CUPS_MEDIA_FLAGS_EXACT) &&
1783 (best->left || best->right || best->top || best->bottom))
1784 return (0);
1785 }
1786 else if (flags & CUPS_MEDIA_FLAGS_DUPLEX)
1787 {
1788 /*
1789 * Look for the largest margins...
1790 */
1791
1792 for (mdb = (_cups_media_db_t *)cupsArrayNext(db);
1793 mdb && !cups_compare_media_db(mdb, &key);
1794 mdb = (_cups_media_db_t *)cupsArrayNext(db))
1795 {
1796 if (mdb->left >= best->left && mdb->right >= best->right &&
1797 mdb->top >= best->top && mdb->bottom >= best->bottom &&
1798 (mdb->bottom != best->bottom || mdb->left != best->left || mdb->right != best->right || mdb->top != best->top))
1799 best = mdb;
1800 }
1801 }
1802 else
1803 {
1804 /*
1805 * Look for the smallest non-zero margins...
1806 */
1807
1808 for (mdb = (_cups_media_db_t *)cupsArrayNext(db);
1809 mdb && !cups_compare_media_db(mdb, &key);
1810 mdb = (_cups_media_db_t *)cupsArrayNext(db))
1811 {
1812 if (((mdb->left > 0 && mdb->left <= best->left) || best->left == 0) &&
1813 ((mdb->right > 0 && mdb->right <= best->right) || best->right == 0) &&
1814 ((mdb->top > 0 && mdb->top <= best->top) || best->top == 0) &&
1815 ((mdb->bottom > 0 && mdb->bottom <= best->bottom) || best->bottom == 0) &&
1816 (mdb->bottom != best->bottom || mdb->left != best->left || mdb->right != best->right || mdb->top != best->top))
1817 best = mdb;
1818 }
1819 }
1820 }
1821 else if (flags & CUPS_MEDIA_FLAGS_EXACT)
1822 {
1823 /*
1824 * See if we can do this as a custom size...
1825 */
1826
1827 if (pwg->width < dinfo->min_size.width ||
1828 pwg->width > dinfo->max_size.width ||
1829 pwg->length < dinfo->min_size.length ||
1830 pwg->length > dinfo->max_size.length)
1831 return (0); /* Out of range */
1832
1833 if ((flags & CUPS_MEDIA_FLAGS_BORDERLESS) &&
1834 (dinfo->min_size.left > 0 || dinfo->min_size.right > 0 ||
1835 dinfo->min_size.top > 0 || dinfo->min_size.bottom > 0))
1836 return (0); /* Not borderless */
1837
1838 key.size_name = (char *)pwg->pwg;
1839 key.bottom = dinfo->min_size.bottom;
1840 key.left = dinfo->min_size.left;
1841 key.right = dinfo->min_size.right;
1842 key.top = dinfo->min_size.top;
1843
1844 best = &key;
1845 }
1846 else if (pwg->width >= dinfo->min_size.width &&
1847 pwg->width <= dinfo->max_size.width &&
1848 pwg->length >= dinfo->min_size.length &&
1849 pwg->length <= dinfo->max_size.length)
1850 {
1851 /*
1852 * Map to custom size...
1853 */
1854
1855 key.size_name = (char *)pwg->pwg;
1856 key.bottom = dinfo->min_size.bottom;
1857 key.left = dinfo->min_size.left;
1858 key.right = dinfo->min_size.right;
1859 key.top = dinfo->min_size.top;
1860
1861 best = &key;
1862 }
1863 else
1864 {
1865 /*
1866 * Find a close size...
1867 */
1868
1869 for (mdb = (_cups_media_db_t *)cupsArrayFirst(db);
1870 mdb;
1871 mdb = (_cups_media_db_t *)cupsArrayNext(db))
1872 if (cups_is_close_media_db(mdb, &key))
1873 break;
1874
1875 if (!mdb)
1876 return (0);
1877
1878 best = mdb;
1879
1880 if (flags & CUPS_MEDIA_FLAGS_BORDERLESS)
1881 {
1882 /*
1883 * Look for the smallest margins...
1884 */
1885
1886 if (best->left != 0 || best->right != 0 || best->top != 0 ||
1887 best->bottom != 0)
1888 {
1889 for (mdb = (_cups_media_db_t *)cupsArrayNext(db);
1890 mdb && cups_is_close_media_db(mdb, &key);
1891 mdb = (_cups_media_db_t *)cupsArrayNext(db))
1892 {
1893 if (mdb->left <= best->left && mdb->right <= best->right &&
1894 mdb->top <= best->top && mdb->bottom <= best->bottom &&
1895 (mdb->bottom != best->bottom || mdb->left != best->left || mdb->right != best->right || mdb->top != best->top))
1896 {
1897 best = mdb;
1898 if (mdb->left == 0 && mdb->right == 0 && mdb->bottom == 0 &&
1899 mdb->top == 0)
1900 break;
1901 }
1902 }
1903 }
1904 }
1905 else if (flags & CUPS_MEDIA_FLAGS_DUPLEX)
1906 {
1907 /*
1908 * Look for the largest margins...
1909 */
1910
1911 for (mdb = (_cups_media_db_t *)cupsArrayNext(db);
1912 mdb && cups_is_close_media_db(mdb, &key);
1913 mdb = (_cups_media_db_t *)cupsArrayNext(db))
1914 {
1915 if (mdb->left >= best->left && mdb->right >= best->right &&
1916 mdb->top >= best->top && mdb->bottom >= best->bottom &&
1917 (mdb->bottom != best->bottom || mdb->left != best->left || mdb->right != best->right || mdb->top != best->top))
1918 best = mdb;
1919 }
1920 }
1921 else
1922 {
1923 /*
1924 * Look for the smallest non-zero margins...
1925 */
1926
1927 for (mdb = (_cups_media_db_t *)cupsArrayNext(db);
1928 mdb && cups_is_close_media_db(mdb, &key);
1929 mdb = (_cups_media_db_t *)cupsArrayNext(db))
1930 {
1931 if (((mdb->left > 0 && mdb->left <= best->left) || best->left == 0) &&
1932 ((mdb->right > 0 && mdb->right <= best->right) ||
1933 best->right == 0) &&
1934 ((mdb->top > 0 && mdb->top <= best->top) || best->top == 0) &&
1935 ((mdb->bottom > 0 && mdb->bottom <= best->bottom) ||
1936 best->bottom == 0) &&
1937 (mdb->bottom != best->bottom || mdb->left != best->left || mdb->right != best->right || mdb->top != best->top))
1938 best = mdb;
1939 }
1940 }
1941 }
1942
1943 if (best)
1944 {
1945 /*
1946 * Return the matching size...
1947 */
1948
1949 if (best->size_name)
1950 strlcpy(size->media, best->size_name, sizeof(size->media));
1951 else if (best->key)
1952 strlcpy(size->media, best->key, sizeof(size->media));
1953 else
1954 strlcpy(size->media, pwg->pwg, sizeof(size->media));
1955
1956 size->width = best->width;
1957 size->length = best->length;
1958 size->bottom = best->bottom;
1959 size->left = best->left;
1960 size->right = best->right;
1961 size->top = best->top;
1962
1963 return (1);
1964 }
1965
1966 return (0);
1967 }
1968
1969
1970 /*
1971 * 'cups_is_close_media_db()' - Compare two media entries to see if they are
1972 * close to the same size.
1973 *
1974 * Currently we use 5 points (from PostScript) as the matching range...
1975 */
1976
1977 static int /* O - 1 if the sizes are close */
1978 cups_is_close_media_db(
1979 _cups_media_db_t *a, /* I - First media entries */
1980 _cups_media_db_t *b) /* I - Second media entries */
1981 {
1982 int dwidth, /* Difference in width */
1983 dlength; /* Difference in length */
1984
1985
1986 dwidth = a->width - b->width;
1987 dlength = a->length - b->length;
1988
1989 return (dwidth >= -176 && dwidth <= 176 &&
1990 dlength >= -176 && dlength <= 176);
1991 }
1992
1993
1994 /*
1995 * 'cups_test_constraints()' - Test constraints.
1996 *
1997 * TODO: STR #4096 - Need to properly support media-col contraints...
1998 */
1999
2000 static cups_array_t * /* O - Active constraints */
2001 cups_test_constraints(
2002 cups_dinfo_t *dinfo, /* I - Destination information */
2003 const char *new_option, /* I - Newly selected option */
2004 const char *new_value, /* I - Newly selected value */
2005 int num_options, /* I - Number of options */
2006 cups_option_t *options, /* I - Options */
2007 int *num_conflicts, /* O - Number of conflicting options */
2008 cups_option_t **conflicts) /* O - Conflicting options */
2009 {
2010 int i, /* Looping var */
2011 match; /* Value matches? */
2012 int num_matching; /* Number of matching options */
2013 cups_option_t *matching; /* Matching options */
2014 _cups_dconstres_t *c; /* Current constraint */
2015 cups_array_t *active = NULL; /* Active constraints */
2016 ipp_attribute_t *attr; /* Current attribute */
2017 _ipp_value_t *attrval; /* Current attribute value */
2018 const char *value; /* Current value */
2019 char temp[1024]; /* Temporary string */
2020 int int_value; /* Integer value */
2021 int xres_value, /* Horizontal resolution */
2022 yres_value; /* Vertical resolution */
2023 ipp_res_t units_value; /* Resolution units */
2024
2025
2026 for (c = (_cups_dconstres_t *)cupsArrayFirst(dinfo->constraints);
2027 c;
2028 c = (_cups_dconstres_t *)cupsArrayNext(dinfo->constraints))
2029 {
2030 num_matching = 0;
2031 matching = NULL;
2032
2033 for (attr = ippFirstAttribute(c->collection);
2034 attr;
2035 attr = ippNextAttribute(c->collection))
2036 {
2037 if (attr->value_tag == IPP_TAG_BEGIN_COLLECTION)
2038 break; /* TODO: STR #4096 */
2039
2040 /*
2041 * Get the value for the current attribute in the constraint...
2042 */
2043
2044 if (new_option && new_value && !strcmp(attr->name, new_option))
2045 value = new_value;
2046 else if ((value = cupsGetOption(attr->name, num_options,
2047 options)) == NULL)
2048 value = cupsGetOption(attr->name, dinfo->num_defaults, dinfo->defaults);
2049
2050 if (!value)
2051 {
2052 /*
2053 * Not set so this constraint does not apply...
2054 */
2055
2056 break;
2057 }
2058
2059 match = 0;
2060
2061 switch (attr->value_tag)
2062 {
2063 case IPP_TAG_INTEGER :
2064 case IPP_TAG_ENUM :
2065 int_value = atoi(value);
2066
2067 for (i = attr->num_values, attrval = attr->values;
2068 i > 0;
2069 i --, attrval ++)
2070 {
2071 if (attrval->integer == int_value)
2072 {
2073 match = 1;
2074 break;
2075 }
2076 }
2077 break;
2078
2079 case IPP_TAG_BOOLEAN :
2080 int_value = !strcmp(value, "true");
2081
2082 for (i = attr->num_values, attrval = attr->values;
2083 i > 0;
2084 i --, attrval ++)
2085 {
2086 if (attrval->boolean == int_value)
2087 {
2088 match = 1;
2089 break;
2090 }
2091 }
2092 break;
2093
2094 case IPP_TAG_RANGE :
2095 int_value = atoi(value);
2096
2097 for (i = attr->num_values, attrval = attr->values;
2098 i > 0;
2099 i --, attrval ++)
2100 {
2101 if (int_value >= attrval->range.lower &&
2102 int_value <= attrval->range.upper)
2103 {
2104 match = 1;
2105 break;
2106 }
2107 }
2108 break;
2109
2110 case IPP_TAG_RESOLUTION :
2111 if (sscanf(value, "%dx%d%15s", &xres_value, &yres_value, temp) != 3)
2112 {
2113 if (sscanf(value, "%d%15s", &xres_value, temp) != 2)
2114 break;
2115
2116 yres_value = xres_value;
2117 }
2118
2119 if (!strcmp(temp, "dpi"))
2120 units_value = IPP_RES_PER_INCH;
2121 else if (!strcmp(temp, "dpc") || !strcmp(temp, "dpcm"))
2122 units_value = IPP_RES_PER_CM;
2123 else
2124 break;
2125
2126 for (i = attr->num_values, attrval = attr->values;
2127 i > 0;
2128 i --, attrval ++)
2129 {
2130 if (attrval->resolution.xres == xres_value &&
2131 attrval->resolution.yres == yres_value &&
2132 attrval->resolution.units == units_value)
2133 {
2134 match = 1;
2135 break;
2136 }
2137 }
2138 break;
2139
2140 case IPP_TAG_TEXT :
2141 case IPP_TAG_NAME :
2142 case IPP_TAG_KEYWORD :
2143 case IPP_TAG_CHARSET :
2144 case IPP_TAG_URI :
2145 case IPP_TAG_URISCHEME :
2146 case IPP_TAG_MIMETYPE :
2147 case IPP_TAG_LANGUAGE :
2148 case IPP_TAG_TEXTLANG :
2149 case IPP_TAG_NAMELANG :
2150 for (i = attr->num_values, attrval = attr->values;
2151 i > 0;
2152 i --, attrval ++)
2153 {
2154 if (!strcmp(attrval->string.text, value))
2155 {
2156 match = 1;
2157 break;
2158 }
2159 }
2160 break;
2161
2162 default :
2163 break;
2164 }
2165
2166 if (!match)
2167 break;
2168
2169 num_matching = cupsAddOption(attr->name, value, num_matching, &matching);
2170 }
2171
2172 if (!attr)
2173 {
2174 if (!active)
2175 active = cupsArrayNew(NULL, NULL);
2176
2177 cupsArrayAdd(active, c);
2178
2179 if (num_conflicts && conflicts)
2180 {
2181 cups_option_t *moption; /* Matching option */
2182
2183 for (i = num_matching, moption = matching; i > 0; i --, moption ++)
2184 *num_conflicts = cupsAddOption(moption->name, moption->value,
2185 *num_conflicts, conflicts);
2186 }
2187 }
2188
2189 cupsFreeOptions(num_matching, matching);
2190 }
2191
2192 return (active);
2193 }
2194
2195
2196 /*
2197 * 'cups_update_ready()' - Update xxx-ready attributes for the printer.
2198 */
2199
2200 static void
2201 cups_update_ready(http_t *http, /* I - Connection to destination */
2202 cups_dinfo_t *dinfo) /* I - Destination information */
2203 {
2204 ipp_t *request; /* Get-Printer-Attributes request */
2205 static const char * const pattrs[] = /* Printer attributes we want */
2206 {
2207 "finishings-col-ready",
2208 "finishings-ready",
2209 "job-finishings-col-ready",
2210 "job-finishings-ready",
2211 "media-col-ready",
2212 "media-ready"
2213 };
2214
2215
2216 /*
2217 * Don't update more than once every 30 seconds...
2218 */
2219
2220 if ((time(NULL) - dinfo->ready_time) < _CUPS_MEDIA_READY_TTL)
2221 return;
2222
2223 /*
2224 * Free any previous results...
2225 */
2226
2227 if (dinfo->cached_flags & CUPS_MEDIA_FLAGS_READY)
2228 {
2229 cupsArrayDelete(dinfo->cached_db);
2230 dinfo->cached_db = NULL;
2231 dinfo->cached_flags = CUPS_MEDIA_FLAGS_DEFAULT;
2232 }
2233
2234 ippDelete(dinfo->ready_attrs);
2235 dinfo->ready_attrs = NULL;
2236
2237 cupsArrayDelete(dinfo->ready_db);
2238 dinfo->ready_db = NULL;
2239
2240 /*
2241 * Query the xxx-ready values...
2242 */
2243
2244 request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
2245 ippSetVersion(request, dinfo->version / 10, dinfo->version % 10);
2246
2247 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
2248 dinfo->uri);
2249 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
2250 NULL, cupsUser());
2251 ippAddStrings(request, IPP_TAG_OPERATION, IPP_CONST_TAG(IPP_TAG_KEYWORD), "requested-attributes", (int)(sizeof(pattrs) / sizeof(pattrs[0])), NULL, pattrs);
2252
2253 dinfo->ready_attrs = cupsDoRequest(http, request, dinfo->resource);
2254
2255 /*
2256 * Update the ready media database...
2257 */
2258
2259 cups_create_media_db(dinfo, CUPS_MEDIA_FLAGS_READY);
2260
2261 /*
2262 * Update last lookup time and return...
2263 */
2264
2265 dinfo->ready_time = time(NULL);
2266 }
2267
2268
2269 /*
2270 * End of "$Id$".
2271 */