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