]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/dest-options.c
Import CUPS v1.7.1
[thirdparty/cups.git] / cups / dest-options.c
1 /*
2 * "$Id: dest-options.c 11085 2013-07-03 13:53:05Z msweet $"
3 *
4 * Destination option/media support for CUPS.
5 *
6 * Copyright 2012-2013 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 * Contents:
17 *
18 * cupsCheckDestSupported() - Check that the option and value are supported
19 * by the destination.
20 * cupsCopyDestConflicts() - Get conflicts and resolutions for a new
21 * option/value pair.
22 * cupsCopyDestInfo() - Get the supported values/capabilities for the
23 * destination.
24 * cupsFindDestDefault() - Find the default value(s) for the given
25 * option.
26 * cupsFindDestReady() - Find the default value(s) for the given
27 * option.
28 * cupsFindDestSupported() - Find the default value(s) for the given
29 * option.
30 * cupsFreeDestInfo() - Free destination information obtained using
31 * @link cupsCopyDestInfo@.
32 * cupsGetDestMediaByIndex() - Get a media name, dimension, and margins for a
33 * specific size.
34 * cupsGetDestMediaByName() - Get media names, dimensions, and margins.
35 * cupsGetDestMediaBySize() - Get media names, dimensions, and margins.
36 * cupsGetDestMediaCount() - Get the number of sizes supported by a
37 * destination.
38 * cupsGetDestMediaDefault() - Get the default size for a destination.
39 * cups_add_dconstres() - Add a constraint or resolver to an array.
40 * cups_compare_dconstres() - Compare to resolver entries.
41 * cups_compare_media_db() - Compare two media entries.
42 * cups_copy_media_db() - Copy a media entry.
43 * cups_create_cached() - Create the media selection cache.
44 * cups_create_constraints() - Create the constraints and resolvers arrays.
45 * cups_create_defaults() - Create the -default option array.
46 * cups_create_media_db() - Create the media database.
47 * cups_free_media_cb() - Free a media entry.
48 * cups_get_media_db() - Lookup the media entry for a given size.
49 * cups_is_close_media_db() - Compare two media entries to see if they are
50 * close to the same size.
51 * cups_test_constraints() - Test constraints.
52 * cups_update_ready() - Update xxx-ready attributes for the printer.
53 */
54
55 /*
56 * Include necessary headers...
57 */
58
59 #include "cups-private.h"
60
61
62 /*
63 * Local constants...
64 */
65
66 #define _CUPS_MEDIA_READY_TTL 30 /* Life of xxx-ready values */
67
68
69 /*
70 * Local functions...
71 */
72
73 static void cups_add_dconstres(cups_array_t *a, ipp_t *collection);
74 static int cups_compare_dconstres(_cups_dconstres_t *a,
75 _cups_dconstres_t *b);
76 static int cups_compare_media_db(_cups_media_db_t *a,
77 _cups_media_db_t *b);
78 static _cups_media_db_t *cups_copy_media_db(_cups_media_db_t *mdb);
79 static void cups_create_cached(http_t *http, cups_dinfo_t *dinfo,
80 unsigned flags);
81 static void cups_create_constraints(cups_dinfo_t *dinfo);
82 static void cups_create_defaults(cups_dinfo_t *dinfo);
83 static void cups_create_media_db(cups_dinfo_t *dinfo,
84 unsigned flags);
85 static void cups_free_media_db(_cups_media_db_t *mdb);
86 static int cups_get_media_db(http_t *http, cups_dinfo_t *dinfo,
87 pwg_media_t *pwg, unsigned flags,
88 cups_size_t *size);
89 static int cups_is_close_media_db(_cups_media_db_t *a,
90 _cups_media_db_t *b);
91 static cups_array_t *cups_test_constraints(cups_dinfo_t *dinfo,
92 const char *new_option,
93 const char *new_value,
94 int num_options,
95 cups_option_t *options,
96 int *num_conflicts,
97 cups_option_t **conflicts);
98 static void cups_update_ready(http_t *http, cups_dinfo_t *dinfo);
99
100
101 /*
102 * 'cupsCheckDestSupported()' - Check that the option and value are supported
103 * by the destination.
104 *
105 * Returns 1 if supported, 0 otherwise.
106 *
107 * @since CUPS 1.6/OS X 10.8@
108 */
109
110 int /* O - 1 if supported, 0 otherwise */
111 cupsCheckDestSupported(
112 http_t *http, /* I - Connection to destination */
113 cups_dest_t *dest, /* I - Destination */
114 cups_dinfo_t *dinfo, /* I - Destination information */
115 const char *option, /* I - Option */
116 const char *value) /* I - Value */
117 {
118 int i; /* Looping var */
119 char temp[1024]; /* Temporary string */
120 int int_value; /* Integer value */
121 int xres_value, /* Horizontal resolution */
122 yres_value; /* Vertical resolution */
123 ipp_res_t units_value; /* Resolution units */
124 ipp_attribute_t *attr; /* Attribute */
125 _ipp_value_t *attrval; /* Current attribute value */
126
127
128 /*
129 * Range check input...
130 */
131
132 if (!http || !dest || !dinfo || !option || !value)
133 return (0);
134
135 /*
136 * Lookup the attribute...
137 */
138
139 if (strstr(option, "-supported"))
140 attr = ippFindAttribute(dinfo->attrs, option, IPP_TAG_ZERO);
141 else
142 {
143 snprintf(temp, sizeof(temp), "%s-supported", option);
144 attr = ippFindAttribute(dinfo->attrs, temp, IPP_TAG_ZERO);
145 }
146
147 if (!attr)
148 return (0);
149
150 /*
151 * Compare values...
152 */
153
154 if (!strcmp(option, "media") && !strncmp(value, "custom_", 7))
155 {
156 /*
157 * Check range of custom media sizes...
158 */
159
160 pwg_media_t *pwg; /* Current PWG media size info */
161 int min_width, /* Minimum width */
162 min_length, /* Minimum length */
163 max_width, /* Maximum width */
164 max_length; /* Maximum length */
165
166 /*
167 * Get the minimum and maximum size...
168 */
169
170 min_width = min_length = INT_MAX;
171 max_width = max_length = 0;
172
173 for (i = attr->num_values, attrval = attr->values;
174 i > 0;
175 i --, attrval ++)
176 {
177 if (!strncmp(attrval->string.text, "custom_min_", 11) &&
178 (pwg = pwgMediaForPWG(attrval->string.text)) != NULL)
179 {
180 min_width = pwg->width;
181 min_length = pwg->length;
182 }
183 else if (!strncmp(attrval->string.text, "custom_max_", 11) &&
184 (pwg = pwgMediaForPWG(attrval->string.text)) != NULL)
185 {
186 max_width = pwg->width;
187 max_length = pwg->length;
188 }
189 }
190
191 /*
192 * Check the range...
193 */
194
195 if (min_width < INT_MAX && max_width > 0 &&
196 (pwg = pwgMediaForPWG(value)) != NULL &&
197 pwg->width >= min_width && pwg->width <= max_width &&
198 pwg->length >= min_length && pwg->length <= max_length)
199 return (1);
200 }
201 else
202 {
203 /*
204 * Check literal values...
205 */
206
207 switch (attr->value_tag)
208 {
209 case IPP_TAG_INTEGER :
210 case IPP_TAG_ENUM :
211 int_value = atoi(value);
212
213 for (i = 0; i < attr->num_values; i ++)
214 if (attr->values[i].integer == int_value)
215 return (1);
216 break;
217
218 case IPP_TAG_BOOLEAN :
219 return (attr->values[0].boolean);
220
221 case IPP_TAG_RANGE :
222 int_value = atoi(value);
223
224 for (i = 0; i < attr->num_values; i ++)
225 if (int_value >= attr->values[i].range.lower &&
226 int_value <= attr->values[i].range.upper)
227 return (1);
228 break;
229
230 case IPP_TAG_RESOLUTION :
231 if (sscanf(value, "%dx%d%15s", &xres_value, &yres_value, temp) != 3)
232 {
233 if (sscanf(value, "%d%15s", &xres_value, temp) != 2)
234 return (0);
235
236 yres_value = xres_value;
237 }
238
239 if (!strcmp(temp, "dpi"))
240 units_value = IPP_RES_PER_INCH;
241 else if (!strcmp(temp, "dpc") || !strcmp(temp, "dpcm"))
242 units_value = IPP_RES_PER_CM;
243 else
244 return (0);
245
246 for (i = attr->num_values, attrval = attr->values;
247 i > 0;
248 i --, attrval ++)
249 {
250 if (attrval->resolution.xres == xres_value &&
251 attrval->resolution.yres == yres_value &&
252 attrval->resolution.units == units_value)
253 return (1);
254 }
255 break;
256
257 case IPP_TAG_TEXT :
258 case IPP_TAG_NAME :
259 case IPP_TAG_KEYWORD :
260 case IPP_TAG_CHARSET :
261 case IPP_TAG_URI :
262 case IPP_TAG_URISCHEME :
263 case IPP_TAG_MIMETYPE :
264 case IPP_TAG_LANGUAGE :
265 case IPP_TAG_TEXTLANG :
266 case IPP_TAG_NAMELANG :
267 for (i = 0; i < attr->num_values; i ++)
268 if (!strcmp(attr->values[i].string.text, value))
269 return (1);
270 break;
271
272 default :
273 break;
274 }
275 }
276
277 /*
278 * If we get there the option+value is not supported...
279 */
280
281 return (0);
282 }
283
284
285 /*
286 * 'cupsCopyDestConflicts()' - Get conflicts and resolutions for a new
287 * option/value pair.
288 *
289 * "num_options" and "options" represent the currently selected options by the
290 * user. "new_option" and "new_value" are the setting the user has just
291 * changed.
292 *
293 * Returns 1 if there is a conflict, 0 if there are no conflicts, and -1 if
294 * there was an unrecoverable error such as a resolver loop.
295 *
296 * If "num_conflicts" and "conflicts" are not @code NULL@, they are set to
297 * contain the list of conflicting option/value pairs. Similarly, if
298 * "num_resolved" and "resolved" are not @code NULL@ they will be set to the
299 * list of changes needed to resolve the conflict.
300 *
301 * If cupsCopyDestConflicts returns 1 but "num_resolved" and "resolved" are set
302 * to 0 and @code NULL@, respectively, then the conflict cannot be resolved.
303 *
304 * @since CUPS 1.6/OS X 10.8@
305 */
306
307 int /* O - 1 if there is a conflict, 0 if none, -1 on error */
308 cupsCopyDestConflicts(
309 http_t *http, /* I - Connection to destination */
310 cups_dest_t *dest, /* I - Destination */
311 cups_dinfo_t *dinfo, /* I - Destination information */
312 int num_options, /* I - Number of current options */
313 cups_option_t *options, /* I - Current options */
314 const char *new_option, /* I - New option */
315 const char *new_value, /* I - New value */
316 int *num_conflicts, /* O - Number of conflicting options */
317 cups_option_t **conflicts, /* O - Conflicting options */
318 int *num_resolved, /* O - Number of options to resolve */
319 cups_option_t **resolved) /* O - Resolved options */
320 {
321 int i, /* Looping var */
322 have_conflicts = 0, /* Do we have conflicts? */
323 changed, /* Did we change something? */
324 tries, /* Number of tries for resolution */
325 num_myconf = 0, /* My number of conflicting options */
326 num_myres = 0; /* My number of resolved options */
327 cups_option_t *myconf = NULL, /* My conflicting options */
328 *myres = NULL, /* My resolved options */
329 *myoption, /* My current option */
330 *option; /* Current option */
331 cups_array_t *active, /* Active conflicts */
332 *pass = NULL, /* Resolvers for this pass */
333 *resolvers = NULL, /* Resolvers we have used */
334 *test; /* Test array for conflicts */
335 _cups_dconstres_t *c, /* Current constraint */
336 *r; /* Current resolver */
337 ipp_attribute_t *attr; /* Current attribute */
338 char value[2048]; /* Current attribute value as string */
339 const char *myvalue; /* Current value of an option */
340
341
342 /*
343 * Clear returned values...
344 */
345
346 if (num_conflicts)
347 *num_conflicts = 0;
348
349 if (conflicts)
350 *conflicts = NULL;
351
352 if (num_resolved)
353 *num_resolved = 0;
354
355 if (resolved)
356 *resolved = NULL;
357
358 /*
359 * Range check input...
360 */
361
362 if (!http || !dest || !dinfo ||
363 (num_conflicts != NULL) != (conflicts != NULL) ||
364 (num_resolved != NULL) != (resolved != NULL))
365 return (0);
366
367 /*
368 * Load constraints as needed...
369 */
370
371 if (!dinfo->constraints)
372 cups_create_constraints(dinfo);
373
374 if (cupsArrayCount(dinfo->constraints) == 0)
375 return (0);
376
377 if (!dinfo->num_defaults)
378 cups_create_defaults(dinfo);
379
380 /*
381 * If we are resolving, create a shadow array...
382 */
383
384 if (num_resolved)
385 {
386 for (i = num_options, option = options; i > 0; i --, option ++)
387 num_myres = cupsAddOption(option->name, option->value, num_myres, &myres);
388
389 if (new_option && new_value)
390 num_myres = cupsAddOption(new_option, new_value, num_myres, &myres);
391 }
392 else
393 {
394 num_myres = num_options;
395 myres = options;
396 }
397
398 /*
399 * Check for any conflicts...
400 */
401
402 if (num_resolved)
403 pass = cupsArrayNew((cups_array_func_t)cups_compare_dconstres, NULL);
404
405 for (tries = 0; tries < 100; tries ++)
406 {
407 /*
408 * Check for any conflicts...
409 */
410
411 if (num_conflicts || num_resolved)
412 {
413 cupsFreeOptions(num_myconf, myconf);
414
415 num_myconf = 0;
416 myconf = NULL;
417 active = cups_test_constraints(dinfo, new_option, new_value,
418 num_myres, myres, &num_myconf,
419 &myconf);
420 }
421 else
422 active = cups_test_constraints(dinfo, new_option, new_value, num_myres,
423 myres, NULL, NULL);
424
425 have_conflicts = (active != NULL);
426
427 if (!active || !num_resolved)
428 break; /* All done */
429
430 /*
431 * Scan the constraints that were triggered to apply resolvers...
432 */
433
434 if (!resolvers)
435 resolvers = cupsArrayNew((cups_array_func_t)cups_compare_dconstres, NULL);
436
437 for (c = (_cups_dconstres_t *)cupsArrayFirst(active), changed = 0;
438 c;
439 c = (_cups_dconstres_t *)cupsArrayNext(active))
440 {
441 if (cupsArrayFind(pass, c))
442 continue; /* Already applied this resolver... */
443
444 if (cupsArrayFind(resolvers, c))
445 {
446 DEBUG_printf(("1cupsCopyDestConflicts: Resolver loop with %s.",
447 c->name));
448 have_conflicts = -1;
449 goto cleanup;
450 }
451
452 if ((r = cupsArrayFind(dinfo->resolvers, c)) == NULL)
453 {
454 DEBUG_printf(("1cupsCopyDestConflicts: Resolver %s not found.",
455 c->name));
456 have_conflicts = -1;
457 goto cleanup;
458 }
459
460 /*
461 * Add the options from the resolver...
462 */
463
464 cupsArrayAdd(pass, r);
465 cupsArrayAdd(resolvers, r);
466
467 for (attr = ippFirstAttribute(r->collection);
468 attr;
469 attr = ippNextAttribute(r->collection))
470 {
471 if (new_option && !strcmp(attr->name, new_option))
472 continue; /* Ignore this if we just changed it */
473
474 if (ippAttributeString(attr, value, sizeof(value)) >= sizeof(value))
475 continue; /* Ignore if the value is too long */
476
477 if ((test = cups_test_constraints(dinfo, attr->name, value, num_myres,
478 myres, NULL, NULL)) == NULL)
479 {
480 /*
481 * That worked, flag it...
482 */
483
484 changed = 1;
485 }
486 else
487 cupsArrayDelete(test);
488
489 /*
490 * Add the option/value from the resolver regardless of whether it
491 * worked; this makes sure that we can cascade several changes to
492 * make things resolve...
493 */
494
495 num_myres = cupsAddOption(attr->name, value, num_myres, &myres);
496 }
497 }
498
499 if (!changed)
500 {
501 DEBUG_puts("1cupsCopyDestConflicts: Unable to resolve constraints.");
502 have_conflicts = -1;
503 goto cleanup;
504 }
505
506 cupsArrayClear(pass);
507
508 cupsArrayDelete(active);
509 active = NULL;
510 }
511
512 if (tries >= 100)
513 {
514 DEBUG_puts("1cupsCopyDestConflicts: Unable to resolve after 100 tries.");
515 have_conflicts = -1;
516 goto cleanup;
517 }
518
519 /*
520 * Copy resolved options as needed...
521 */
522
523 if (num_resolved)
524 {
525 for (i = num_myres, myoption = myres; i > 0; i --, myoption ++)
526 {
527 if ((myvalue = cupsGetOption(myoption->name, num_options,
528 options)) == NULL ||
529 strcmp(myvalue, myoption->value))
530 {
531 if (new_option && !strcmp(new_option, myoption->name) &&
532 new_value && !strcmp(new_value, myoption->value))
533 continue;
534
535 *num_resolved = cupsAddOption(myoption->name, myoption->value,
536 *num_resolved, resolved);
537 }
538 }
539 }
540
541 /*
542 * Clean up...
543 */
544
545 cleanup:
546
547 cupsArrayDelete(active);
548 cupsArrayDelete(pass);
549 cupsArrayDelete(resolvers);
550
551 if (num_resolved)
552 {
553 /*
554 * Free shadow copy of options...
555 */
556
557 cupsFreeOptions(num_myres, myres);
558 }
559
560 if (num_conflicts)
561 {
562 /*
563 * Return conflicting options to caller...
564 */
565
566 *num_conflicts = num_myconf;
567 *conflicts = myconf;
568 }
569 else
570 {
571 /*
572 * Free conflicting options...
573 */
574
575 cupsFreeOptions(num_myconf, myconf);
576 }
577
578 return (have_conflicts);
579 }
580
581
582 /*
583 * 'cupsCopyDestInfo()' - Get the supported values/capabilities for the
584 * destination.
585 *
586 * The caller is responsible for calling @link cupsFreeDestInfo@ on the return
587 * value. @code NULL@ is returned on error.
588 *
589 * @since CUPS 1.6/OS X 10.8@
590 */
591
592 cups_dinfo_t * /* O - Destination information */
593 cupsCopyDestInfo(
594 http_t *http, /* I - Connection to destination */
595 cups_dest_t *dest) /* I - Destination */
596 {
597 cups_dinfo_t *dinfo; /* Destination information */
598 ipp_t *request, /* Get-Printer-Attributes request */
599 *response; /* Supported attributes */
600 int tries, /* Number of tries so far */
601 delay, /* Current retry delay */
602 prev_delay; /* Next retry delay */
603 const char *uri; /* Printer URI */
604 char resource[1024]; /* Resource path */
605 int version; /* IPP version */
606 ipp_status_t status; /* Status of request */
607 static const char * const requested_attrs[] =
608 { /* Requested attributes */
609 "job-template",
610 "media-col-database",
611 "printer-description"
612 };
613
614
615 DEBUG_printf(("cupsCopyDestSupported(http=%p, dest=%p(%s))", http, dest,
616 dest ? dest->name : ""));
617
618 /*
619 * Range check input...
620 */
621
622 if (!http || !dest)
623 return (NULL);
624
625 /*
626 * Get the printer URI and resource path...
627 */
628
629 if ((uri = _cupsGetDestResource(dest, resource, sizeof(resource))) == NULL)
630 return (NULL);
631
632 /*
633 * Get the supported attributes...
634 */
635
636 delay = 1;
637 prev_delay = 1;
638 tries = 0;
639 version = 20;
640
641 do
642 {
643 /*
644 * Send a Get-Printer-Attributes request...
645 */
646
647 request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
648 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
649 uri);
650 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
651 "requesting-user-name", NULL, cupsUser());
652 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
653 "requested-attributes",
654 (int)(sizeof(requested_attrs) / sizeof(requested_attrs[0])),
655 NULL, requested_attrs);
656 response = cupsDoRequest(http, request, resource);
657 status = cupsLastError();
658
659 if (status > IPP_STATUS_OK_IGNORED_OR_SUBSTITUTED)
660 {
661 DEBUG_printf(("cupsCopyDestSupported: Get-Printer-Attributes for '%s' "
662 "returned %s (%s)", dest->name, ippErrorString(status),
663 cupsLastErrorString()));
664
665 ippDelete(response);
666 response = NULL;
667
668 if (status == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED && version > 11)
669 version = 11;
670 else if (status == IPP_STATUS_ERROR_BUSY)
671 {
672 sleep(delay);
673
674 delay = _cupsNextDelay(delay, &prev_delay);
675 }
676 else
677 return (NULL);
678 }
679
680 tries ++;
681 }
682 while (!response && tries < 10);
683
684 if (!response)
685 return (NULL);
686
687 /*
688 * Allocate a cups_dinfo_t structure and return it...
689 */
690
691 if ((dinfo = calloc(1, sizeof(cups_dinfo_t))) == NULL)
692 {
693 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
694 ippDelete(response);
695 return (NULL);
696 }
697
698 dinfo->version = version;
699 dinfo->uri = uri;
700 dinfo->resource = _cupsStrAlloc(resource);
701 dinfo->attrs = response;
702
703 return (dinfo);
704 }
705
706
707 /*
708 * 'cupsFindDestDefault()' - Find the default value(s) for the given option.
709 *
710 * The returned value is an IPP attribute. Use the @code ippGetBoolean@,
711 * @code ippGetCollection@, @code ippGetCount@, @code ippGetDate@,
712 * @code ippGetInteger@, @code ippGetOctetString@, @code ippGetRange@,
713 * @code ippGetResolution@, @code ippGetString@, and @code ippGetValueTag@
714 * functions to inspect the default value(s) as needed.
715 *
716 * @since CUPS 1.7/OS X 10.9@
717 */
718
719 ipp_attribute_t * /* O - Default attribute or @code NULL@ for none */
720 cupsFindDestDefault(
721 http_t *http, /* I - Connection to destination */
722 cups_dest_t *dest, /* I - Destination */
723 cups_dinfo_t *dinfo, /* I - Destination information */
724 const char *option) /* I - Option/attribute name */
725 {
726 char name[IPP_MAX_NAME]; /* Attribute name */
727
728
729 /*
730 * Range check input...
731 */
732
733 if (!http || !dest || !dinfo || !option)
734 {
735 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
736 return (NULL);
737 }
738
739 /*
740 * Find and return the attribute...
741 */
742
743 snprintf(name, sizeof(name), "%s-default", option);
744 return (ippFindAttribute(dinfo->attrs, name, IPP_TAG_ZERO));
745 }
746
747 /*
748 * 'cupsFindDestReady()' - Find the default value(s) for the given option.
749 *
750 * The returned value is an IPP attribute. Use the @code ippGetBoolean@,
751 * @code ippGetCollection@, @code ippGetCount@, @code ippGetDate@,
752 * @code ippGetInteger@, @code ippGetOctetString@, @code ippGetRange@,
753 * @code ippGetResolution@, @code ippGetString@, and @code ippGetValueTag@
754 * functions to inspect the default value(s) as needed.
755 *
756 * @since CUPS 1.7/OS X 10.9@
757 */
758
759 ipp_attribute_t * /* O - Default attribute or @code NULL@ for none */
760 cupsFindDestReady(
761 http_t *http, /* I - Connection to destination */
762 cups_dest_t *dest, /* I - Destination */
763 cups_dinfo_t *dinfo, /* I - Destination information */
764 const char *option) /* I - Option/attribute name */
765 {
766 char name[IPP_MAX_NAME]; /* Attribute name */
767
768
769 /*
770 * Range check input...
771 */
772
773 if (!http || !dest || !dinfo || !option)
774 {
775 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
776 return (NULL);
777 }
778
779 /*
780 * Find and return the attribute...
781 */
782
783 cups_update_ready(http, dinfo);
784
785 snprintf(name, sizeof(name), "%s-ready", option);
786 return (ippFindAttribute(dinfo->ready_attrs, name, IPP_TAG_ZERO));
787 }
788
789 /*
790 * 'cupsFindDestSupported()' - Find the default value(s) for the given option.
791 *
792 * The returned value is an IPP attribute. Use the @code ippGetBoolean@,
793 * @code ippGetCollection@, @code ippGetCount@, @code ippGetDate@,
794 * @code ippGetInteger@, @code ippGetOctetString@, @code ippGetRange@,
795 * @code ippGetResolution@, @code ippGetString@, and @code ippGetValueTag@
796 * functions to inspect the default value(s) as needed.
797 *
798 * @since CUPS 1.7/OS X 10.9@
799 */
800
801 ipp_attribute_t * /* O - Default attribute or @code NULL@ for none */
802 cupsFindDestSupported(
803 http_t *http, /* I - Connection to destination */
804 cups_dest_t *dest, /* I - Destination */
805 cups_dinfo_t *dinfo, /* I - Destination information */
806 const char *option) /* I - Option/attribute name */
807 {
808 char name[IPP_MAX_NAME]; /* Attribute name */
809
810
811 /*
812 * Range check input...
813 */
814
815 if (!http || !dest || !dinfo || !option)
816 {
817 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
818 return (NULL);
819 }
820
821 /*
822 * Find and return the attribute...
823 */
824
825 snprintf(name, sizeof(name), "%s-supported", option);
826 return (ippFindAttribute(dinfo->attrs, name, IPP_TAG_ZERO));
827 }
828
829
830 /*
831 * 'cupsFreeDestInfo()' - Free destination information obtained using
832 * @link cupsCopyDestInfo@.
833 */
834
835 void
836 cupsFreeDestInfo(cups_dinfo_t *dinfo) /* I - Destination information */
837 {
838 /*
839 * Range check input...
840 */
841
842 if (!dinfo)
843 return;
844
845 /*
846 * Free memory and return...
847 */
848
849 _cupsStrFree(dinfo->resource);
850
851 cupsArrayDelete(dinfo->constraints);
852 cupsArrayDelete(dinfo->resolvers);
853
854 cupsArrayDelete(dinfo->localizations);
855
856 cupsArrayDelete(dinfo->media_db);
857
858 cupsArrayDelete(dinfo->cached_db);
859
860 ippDelete(dinfo->ready_attrs);
861 cupsArrayDelete(dinfo->ready_db);
862
863 ippDelete(dinfo->attrs);
864
865 free(dinfo);
866 }
867
868
869 /*
870 * 'cupsGetDestMediaByIndex()' - Get a media name, dimension, and margins for a
871 * specific size.
872 *
873 * The @code flags@ parameter determines which set of media are indexed. For
874 * example, passing @code CUPS_MEDIA_FLAGS_BORDERLESS@ will get the Nth
875 * borderless size supported by the printer.
876 *
877 * @since CUPS 1.7/OS X 10.9@
878 */
879
880 int /* O - 1 on success, 0 on failure */
881 cupsGetDestMediaByIndex(
882 http_t *http, /* I - Connection to destination */
883 cups_dest_t *dest, /* I - Destination */
884 cups_dinfo_t *dinfo, /* I - Destination information */
885 int n, /* I - Media size number (0-based) */
886 unsigned flags, /* I - Media flags */
887 cups_size_t *size) /* O - Media size information */
888 {
889 cups_size_t *nsize; /* Size for N */
890
891
892 /*
893 * Range check input...
894 */
895
896 if (size)
897 memset(size, 0, sizeof(cups_size_t));
898
899 if (!http || !dest || !dinfo || n < 0 || !size)
900 {
901 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
902 return (0);
903 }
904
905 /*
906 * Load media list as needed...
907 */
908
909 if (flags & CUPS_MEDIA_FLAGS_READY)
910 cups_update_ready(http, dinfo);
911
912 if (!dinfo->cached_db || dinfo->cached_flags != flags)
913 cups_create_cached(http, dinfo, flags);
914
915 /*
916 * Copy the size over and return...
917 */
918
919 if ((nsize = (cups_size_t *)cupsArrayIndex(dinfo->cached_db, n)) == NULL)
920 {
921 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
922 return (0);
923 }
924
925 memcpy(size, nsize, sizeof(cups_size_t));
926
927 return (1);
928 }
929
930
931 /*
932 * 'cupsGetDestMediaByName()' - Get media names, dimensions, and margins.
933 *
934 * The "media" string is a PWG media name. "Flags" provides some matching
935 * guidance (multiple flags can be combined):
936 *
937 * CUPS_MEDIA_FLAGS_DEFAULT = find the closest size supported by the printer,
938 * CUPS_MEDIA_FLAGS_BORDERLESS = find a borderless size,
939 * CUPS_MEDIA_FLAGS_DUPLEX = find a size compatible with 2-sided printing,
940 * CUPS_MEDIA_FLAGS_EXACT = find an exact match for the size, and
941 * CUPS_MEDIA_FLAGS_READY = if the printer supports media sensing, find the
942 * size amongst the "ready" media.
943 *
944 * The matching result (if any) is returned in the "cups_size_t" structure.
945 *
946 * Returns 1 when there is a match and 0 if there is not a match.
947 *
948 * @since CUPS 1.6/OS X 10.8@
949 */
950
951 int /* O - 1 on match, 0 on failure */
952 cupsGetDestMediaByName(
953 http_t *http, /* I - Connection to destination */
954 cups_dest_t *dest, /* I - Destination */
955 cups_dinfo_t *dinfo, /* I - Destination information */
956 const char *media, /* I - Media name */
957 unsigned flags, /* I - Media matching flags */
958 cups_size_t *size) /* O - Media size information */
959 {
960 pwg_media_t *pwg; /* PWG media info */
961
962
963 /*
964 * Range check input...
965 */
966
967 if (size)
968 memset(size, 0, sizeof(cups_size_t));
969
970 if (!http || !dest || !dinfo || !media || !size)
971 {
972 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
973 return (0);
974 }
975
976 /*
977 * Lookup the media size name...
978 */
979
980 if ((pwg = pwgMediaForPWG(media)) == NULL)
981 if ((pwg = pwgMediaForLegacy(media)) == NULL)
982 {
983 DEBUG_printf(("1cupsGetDestMediaByName: Unknown size '%s'.", media));
984 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unknown media size name."), 1);
985 return (0);
986 }
987
988 /*
989 * Lookup the size...
990 */
991
992 return (cups_get_media_db(http, dinfo, pwg, flags, size));
993 }
994
995
996 /*
997 * 'cupsGetDestMediaBySize()' - Get media names, dimensions, and margins.
998 *
999 * "Width" and "length" are the dimensions in hundredths of millimeters.
1000 * "Flags" provides some matching guidance (multiple flags can be combined):
1001 *
1002 * CUPS_MEDIA_FLAGS_DEFAULT = find the closest size supported by the printer,
1003 * CUPS_MEDIA_FLAGS_BORDERLESS = find a borderless size,
1004 * CUPS_MEDIA_FLAGS_DUPLEX = find a size compatible with 2-sided printing,
1005 * CUPS_MEDIA_FLAGS_EXACT = find an exact match for the size, and
1006 * CUPS_MEDIA_FLAGS_READY = if the printer supports media sensing, find the
1007 * size amongst the "ready" media.
1008 *
1009 * The matching result (if any) is returned in the "cups_size_t" structure.
1010 *
1011 * Returns 1 when there is a match and 0 if there is not a match.
1012 *
1013 * @since CUPS 1.6/OS X 10.8@
1014 */
1015
1016 int /* O - 1 on match, 0 on failure */
1017 cupsGetDestMediaBySize(
1018 http_t *http, /* I - Connection to destination */
1019 cups_dest_t *dest, /* I - Destination */
1020 cups_dinfo_t *dinfo, /* I - Destination information */
1021 int width, /* I - Media width in hundredths of
1022 * of millimeters */
1023 int length, /* I - Media length in hundredths of
1024 * of millimeters */
1025 unsigned flags, /* I - Media matching flags */
1026 cups_size_t *size) /* O - Media size information */
1027 {
1028 pwg_media_t *pwg; /* PWG media info */
1029
1030
1031 /*
1032 * Range check input...
1033 */
1034
1035 if (size)
1036 memset(size, 0, sizeof(cups_size_t));
1037
1038 if (!http || !dest || !dinfo || width <= 0 || length <= 0 || !size)
1039 {
1040 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
1041 return (0);
1042 }
1043
1044 /*
1045 * Lookup the media size name...
1046 */
1047
1048 if ((pwg = pwgMediaForSize(width, length)) == NULL)
1049 {
1050 DEBUG_printf(("1cupsGetDestMediaBySize: Invalid size %dx%d.", width,
1051 length));
1052 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Invalid media size."), 1);
1053 return (0);
1054 }
1055
1056 /*
1057 * Lookup the size...
1058 */
1059
1060 return (cups_get_media_db(http, dinfo, pwg, flags, size));
1061 }
1062
1063
1064 /*
1065 * 'cupsGetDestMediaCount()' - Get the number of sizes supported by a
1066 * destination.
1067 *
1068 * The @code flags@ parameter determines the set of media sizes that are
1069 * counted. For example, passing @code CUPS_MEDIA_FLAGS_BORDERLESS@ will return
1070 * the number of borderless sizes.
1071 *
1072 * @since CUPS 1.7/OS X 10.9@
1073 */
1074
1075 int /* O - Number of sizes */
1076 cupsGetDestMediaCount(
1077 http_t *http, /* I - Connection to destination */
1078 cups_dest_t *dest, /* I - Destination */
1079 cups_dinfo_t *dinfo, /* I - Destination information */
1080 unsigned flags) /* I - Media flags */
1081 {
1082 /*
1083 * Range check input...
1084 */
1085
1086 if (!http || !dest || !dinfo)
1087 {
1088 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
1089 return (0);
1090 }
1091
1092 /*
1093 * Load media list as needed...
1094 */
1095
1096 if (flags & CUPS_MEDIA_FLAGS_READY)
1097 cups_update_ready(http, dinfo);
1098
1099 if (!dinfo->cached_db || dinfo->cached_flags != flags)
1100 cups_create_cached(http, dinfo, flags);
1101
1102 return (cupsArrayCount(dinfo->cached_db));
1103 }
1104
1105
1106 /*
1107 * 'cupsGetDestMediaDefault()' - Get the default size for a destination.
1108 *
1109 * The @code flags@ parameter determines which default size is returned. For
1110 * example, passing @code CUPS_MEDIA_FLAGS_BORDERLESS@ will return the default
1111 * borderless size, typically US Letter or A4, but sometimes 4x6 photo media.
1112 *
1113 * @since CUPS 1.7/OS X 10.9@
1114 */
1115
1116 int /* O - 1 on success, 0 on failure */
1117 cupsGetDestMediaDefault(
1118 http_t *http, /* I - Connection to destination */
1119 cups_dest_t *dest, /* I - Destination */
1120 cups_dinfo_t *dinfo, /* I - Destination information */
1121 unsigned flags, /* I - Media flags */
1122 cups_size_t *size) /* O - Media size information */
1123 {
1124 const char *media; /* Default media size */
1125
1126
1127 /*
1128 * Range check input...
1129 */
1130
1131 if (size)
1132 memset(size, 0, sizeof(cups_size_t));
1133
1134 if (!http || !dest || !dinfo || !size)
1135 {
1136 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
1137 return (0);
1138 }
1139
1140 /*
1141 * Get the default media size, if any...
1142 */
1143
1144 if ((media = cupsGetOption("media", dest->num_options,
1145 dest->options)) == NULL)
1146 media = "na_letter_8.5x11in";
1147
1148 if (cupsGetDestMediaByName(http, dest, dinfo, media, flags, size))
1149 return (1);
1150
1151 if (strcmp(media, "na_letter_8.5x11in") &&
1152 cupsGetDestMediaByName(http, dest, dinfo, "iso_a4_210x297mm", flags,
1153 size))
1154 return (1);
1155
1156 if (strcmp(media, "iso_a4_210x297mm") &&
1157 cupsGetDestMediaByName(http, dest, dinfo, "na_letter_8.5x11in", flags,
1158 size))
1159 return (1);
1160
1161 if ((flags & CUPS_MEDIA_FLAGS_BORDERLESS) &&
1162 cupsGetDestMediaByName(http, dest, dinfo, "na_index_4x6in", flags, size))
1163 return (1);
1164
1165 /*
1166 * Fall back to the first matching media size...
1167 */
1168
1169 return (cupsGetDestMediaByIndex(http, dest, dinfo, flags, 0, size));
1170 }
1171
1172
1173 /*
1174 * 'cups_add_dconstres()' - Add a constraint or resolver to an array.
1175 */
1176
1177 static void
1178 cups_add_dconstres(
1179 cups_array_t *a, /* I - Array */
1180 ipp_t *collection) /* I - Collection value */
1181 {
1182 ipp_attribute_t *attr; /* Attribute */
1183 _cups_dconstres_t *temp; /* Current constraint/resolver */
1184
1185
1186 if ((attr = ippFindAttribute(collection, "resolver-name",
1187 IPP_TAG_NAME)) == NULL)
1188 return;
1189
1190 if ((temp = calloc(1, sizeof(_cups_dconstres_t))) == NULL)
1191 return;
1192
1193 temp->name = attr->values[0].string.text;
1194 temp->collection = collection;
1195
1196 cupsArrayAdd(a, temp);
1197 }
1198
1199
1200 /*
1201 * 'cups_compare_dconstres()' - Compare to resolver entries.
1202 */
1203
1204 static int /* O - Result of comparison */
1205 cups_compare_dconstres(
1206 _cups_dconstres_t *a, /* I - First resolver */
1207 _cups_dconstres_t *b) /* I - Second resolver */
1208 {
1209 return (strcmp(a->name, b->name));
1210 }
1211
1212
1213 /*
1214 * 'cups_compare_media_db()' - Compare two media entries.
1215 */
1216
1217 static int /* O - Result of comparison */
1218 cups_compare_media_db(
1219 _cups_media_db_t *a, /* I - First media entries */
1220 _cups_media_db_t *b) /* I - Second media entries */
1221 {
1222 int result; /* Result of comparison */
1223
1224
1225 if ((result = a->width - b->width) == 0)
1226 result = a->length - b->length;
1227
1228 return (result);
1229 }
1230
1231
1232 /*
1233 * 'cups_copy_media_db()' - Copy a media entry.
1234 */
1235
1236 static _cups_media_db_t * /* O - New media entry */
1237 cups_copy_media_db(
1238 _cups_media_db_t *mdb) /* I - Media entry to copy */
1239 {
1240 _cups_media_db_t *temp; /* New media entry */
1241
1242
1243 if ((temp = calloc(1, sizeof(_cups_media_db_t))) == NULL)
1244 return (NULL);
1245
1246 if (mdb->color)
1247 temp->color = _cupsStrAlloc(mdb->color);
1248 if (mdb->key)
1249 temp->key = _cupsStrAlloc(mdb->key);
1250 if (mdb->info)
1251 temp->info = _cupsStrAlloc(mdb->info);
1252 if (mdb->size_name)
1253 temp->size_name = _cupsStrAlloc(mdb->size_name);
1254 if (mdb->source)
1255 temp->source = _cupsStrAlloc(mdb->source);
1256 if (mdb->type)
1257 temp->type = _cupsStrAlloc(mdb->type);
1258
1259 temp->width = mdb->width;
1260 temp->length = mdb->length;
1261 temp->bottom = mdb->bottom;
1262 temp->left = mdb->left;
1263 temp->right = mdb->right;
1264 temp->top = mdb->top;
1265
1266 return (temp);
1267 }
1268
1269
1270 /*
1271 * 'cups_create_cached()' - Create the media selection cache.
1272 */
1273
1274 static void
1275 cups_create_cached(http_t *http, /* I - Connection to destination */
1276 cups_dinfo_t *dinfo, /* I - Destination information */
1277 unsigned flags) /* I - Media selection flags */
1278 {
1279 cups_array_t *db; /* Media database array to use */
1280 _cups_media_db_t *mdb, /* Media database entry */
1281 *first; /* First entry this size */
1282
1283
1284 if (dinfo->cached_db)
1285 cupsArrayDelete(dinfo->cached_db);
1286
1287 dinfo->cached_db = cupsArrayNew(NULL, NULL);
1288 dinfo->cached_flags = flags;
1289
1290 if (flags & CUPS_MEDIA_FLAGS_READY)
1291 {
1292 cups_update_ready(http, dinfo);
1293 db = dinfo->ready_db;
1294 }
1295 else
1296 {
1297 if (!dinfo->media_db)
1298 cups_create_media_db(dinfo, CUPS_MEDIA_FLAGS_DEFAULT);
1299
1300 db = dinfo->media_db;
1301 }
1302
1303 for (mdb = (_cups_media_db_t *)cupsArrayFirst(db), first = mdb;
1304 mdb;
1305 mdb = (_cups_media_db_t *)cupsArrayNext(db))
1306 {
1307 if (flags & CUPS_MEDIA_FLAGS_BORDERLESS)
1308 {
1309 if (!mdb->left && !mdb->right && !mdb->top && !mdb->bottom)
1310 cupsArrayAdd(dinfo->cached_db, mdb);
1311 }
1312 else if (flags & CUPS_MEDIA_FLAGS_DUPLEX)
1313 {
1314 if (first->width != mdb->width || first->length != mdb->length)
1315 {
1316 cupsArrayAdd(dinfo->cached_db, first);
1317 first = mdb;
1318 }
1319 else if (mdb->left >= first->left && mdb->right >= first->right &&
1320 mdb->top >= first->top && mdb->bottom >= first->bottom)
1321 first = mdb;
1322 }
1323 }
1324
1325 if (flags & CUPS_MEDIA_FLAGS_DUPLEX)
1326 cupsArrayAdd(dinfo->cached_db, first);
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 ||
1761 best->bottom != 0)
1762 {
1763 for (mdb = (_cups_media_db_t *)cupsArrayNext(db);
1764 mdb && !cups_compare_media_db(mdb, &key);
1765 mdb = (_cups_media_db_t *)cupsArrayNext(db))
1766 {
1767 if (mdb->left <= best->left && mdb->right <= best->right &&
1768 mdb->top <= best->top && mdb->bottom <= best->bottom)
1769 {
1770 best = mdb;
1771 if (mdb->left == 0 && mdb->right == 0 && mdb->bottom == 0 &&
1772 mdb->top == 0)
1773 break;
1774 }
1775 }
1776 }
1777
1778 /*
1779 * If we need an exact match, return no-match if the size is not
1780 * borderless.
1781 */
1782
1783 if ((flags & CUPS_MEDIA_FLAGS_EXACT) &&
1784 (best->left || best->right || best->top || best->bottom))
1785 return (0);
1786 }
1787 else if (flags & CUPS_MEDIA_FLAGS_DUPLEX)
1788 {
1789 /*
1790 * Look for the largest margins...
1791 */
1792
1793 for (mdb = (_cups_media_db_t *)cupsArrayNext(db);
1794 mdb && !cups_compare_media_db(mdb, &key);
1795 mdb = (_cups_media_db_t *)cupsArrayNext(db))
1796 {
1797 if (mdb->left >= best->left && mdb->right >= best->right &&
1798 mdb->top >= best->top && mdb->bottom >= best->bottom)
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) ||
1814 best->right == 0) &&
1815 ((mdb->top > 0 && mdb->top <= best->top) || best->top == 0) &&
1816 ((mdb->bottom > 0 && mdb->bottom <= best->bottom) ||
1817 best->bottom == 0))
1818 best = mdb;
1819 }
1820 }
1821 }
1822 else if (flags & CUPS_MEDIA_FLAGS_EXACT)
1823 {
1824 /*
1825 * See if we can do this as a custom size...
1826 */
1827
1828 if (pwg->width < dinfo->min_size.width ||
1829 pwg->width > dinfo->max_size.width ||
1830 pwg->length < dinfo->min_size.length ||
1831 pwg->length > dinfo->max_size.length)
1832 return (0); /* Out of range */
1833
1834 if ((flags & CUPS_MEDIA_FLAGS_BORDERLESS) &&
1835 (dinfo->min_size.left > 0 || dinfo->min_size.right > 0 ||
1836 dinfo->min_size.top > 0 || dinfo->min_size.bottom > 0))
1837 return (0); /* Not borderless */
1838
1839 key.size_name = (char *)pwg->pwg;
1840 key.bottom = dinfo->min_size.bottom;
1841 key.left = dinfo->min_size.left;
1842 key.right = dinfo->min_size.right;
1843 key.top = dinfo->min_size.top;
1844
1845 best = &key;
1846 }
1847 else if (pwg->width >= dinfo->min_size.width &&
1848 pwg->width <= dinfo->max_size.width &&
1849 pwg->length >= dinfo->min_size.length &&
1850 pwg->length <= dinfo->max_size.length)
1851 {
1852 /*
1853 * Map to custom size...
1854 */
1855
1856 key.size_name = (char *)pwg->pwg;
1857 key.bottom = dinfo->min_size.bottom;
1858 key.left = dinfo->min_size.left;
1859 key.right = dinfo->min_size.right;
1860 key.top = dinfo->min_size.top;
1861
1862 best = &key;
1863 }
1864 else
1865 {
1866 /*
1867 * Find a close size...
1868 */
1869
1870 for (mdb = (_cups_media_db_t *)cupsArrayFirst(db);
1871 mdb;
1872 mdb = (_cups_media_db_t *)cupsArrayNext(db))
1873 if (cups_is_close_media_db(mdb, &key))
1874 break;
1875
1876 if (!mdb)
1877 return (0);
1878
1879 best = mdb;
1880
1881 if (flags & CUPS_MEDIA_FLAGS_BORDERLESS)
1882 {
1883 /*
1884 * Look for the smallest margins...
1885 */
1886
1887 if (best->left != 0 || best->right != 0 || best->top != 0 ||
1888 best->bottom != 0)
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 <= best->left && mdb->right <= best->right &&
1895 mdb->top <= best->top && mdb->bottom <= best->bottom)
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 best = mdb;
1918 }
1919 }
1920 else
1921 {
1922 /*
1923 * Look for the smallest non-zero margins...
1924 */
1925
1926 for (mdb = (_cups_media_db_t *)cupsArrayNext(db);
1927 mdb && cups_is_close_media_db(mdb, &key);
1928 mdb = (_cups_media_db_t *)cupsArrayNext(db))
1929 {
1930 if (((mdb->left > 0 && mdb->left <= best->left) || best->left == 0) &&
1931 ((mdb->right > 0 && mdb->right <= best->right) ||
1932 best->right == 0) &&
1933 ((mdb->top > 0 && mdb->top <= best->top) || best->top == 0) &&
1934 ((mdb->bottom > 0 && mdb->bottom <= best->bottom) ||
1935 best->bottom == 0))
1936 best = mdb;
1937 }
1938 }
1939 }
1940
1941 if (best)
1942 {
1943 /*
1944 * Return the matching size...
1945 */
1946
1947 if (best->size_name)
1948 strlcpy(size->media, best->size_name, sizeof(size->media));
1949 else if (best->key)
1950 strlcpy(size->media, best->key, sizeof(size->media));
1951 else
1952 strlcpy(size->media, pwg->pwg, sizeof(size->media));
1953
1954 size->width = best->width;
1955 size->length = best->length;
1956 size->bottom = best->bottom;
1957 size->left = best->left;
1958 size->right = best->right;
1959 size->top = best->top;
1960
1961 return (1);
1962 }
1963
1964 return (0);
1965 }
1966
1967
1968 /*
1969 * 'cups_is_close_media_db()' - Compare two media entries to see if they are
1970 * close to the same size.
1971 *
1972 * Currently we use 5 points (from PostScript) as the matching range...
1973 */
1974
1975 static int /* O - 1 if the sizes are close */
1976 cups_is_close_media_db(
1977 _cups_media_db_t *a, /* I - First media entries */
1978 _cups_media_db_t *b) /* I - Second media entries */
1979 {
1980 int dwidth, /* Difference in width */
1981 dlength; /* Difference in length */
1982
1983
1984 dwidth = a->width - b->width;
1985 dlength = a->length - b->length;
1986
1987 return (dwidth >= -176 && dwidth <= 176 &&
1988 dlength >= -176 && dlength <= 176);
1989 }
1990
1991
1992 /*
1993 * 'cups_test_constraints()' - Test constraints.
1994 *
1995 * TODO: STR #4096 - Need to properly support media-col contraints...
1996 */
1997
1998 static cups_array_t * /* O - Active constraints */
1999 cups_test_constraints(
2000 cups_dinfo_t *dinfo, /* I - Destination information */
2001 const char *new_option, /* I - Newly selected option */
2002 const char *new_value, /* I - Newly selected value */
2003 int num_options, /* I - Number of options */
2004 cups_option_t *options, /* I - Options */
2005 int *num_conflicts, /* O - Number of conflicting options */
2006 cups_option_t **conflicts) /* O - Conflicting options */
2007 {
2008 int i, /* Looping var */
2009 match; /* Value matches? */
2010 int num_matching; /* Number of matching options */
2011 cups_option_t *matching; /* Matching options */
2012 _cups_dconstres_t *c; /* Current constraint */
2013 cups_array_t *active = NULL; /* Active constraints */
2014 ipp_attribute_t *attr; /* Current attribute */
2015 _ipp_value_t *attrval; /* Current attribute value */
2016 const char *value; /* Current value */
2017 char temp[1024]; /* Temporary string */
2018 int int_value; /* Integer value */
2019 int xres_value, /* Horizontal resolution */
2020 yres_value; /* Vertical resolution */
2021 ipp_res_t units_value; /* Resolution units */
2022
2023
2024 for (c = (_cups_dconstres_t *)cupsArrayFirst(dinfo->constraints);
2025 c;
2026 c = (_cups_dconstres_t *)cupsArrayNext(dinfo->constraints))
2027 {
2028 num_matching = 0;
2029 matching = NULL;
2030
2031 for (attr = ippFirstAttribute(c->collection);
2032 attr;
2033 attr = ippNextAttribute(c->collection))
2034 {
2035 if (attr->value_tag == IPP_TAG_BEGIN_COLLECTION)
2036 break; /* TODO: STR #4096 */
2037
2038 /*
2039 * Get the value for the current attribute in the constraint...
2040 */
2041
2042 if (new_option && new_value && !strcmp(attr->name, new_option))
2043 value = new_value;
2044 else if ((value = cupsGetOption(attr->name, num_options,
2045 options)) == NULL)
2046 value = cupsGetOption(attr->name, dinfo->num_defaults, dinfo->defaults);
2047
2048 if (!value)
2049 {
2050 /*
2051 * Not set so this constraint does not apply...
2052 */
2053
2054 break;
2055 }
2056
2057 match = 0;
2058
2059 switch (attr->value_tag)
2060 {
2061 case IPP_TAG_INTEGER :
2062 case IPP_TAG_ENUM :
2063 int_value = atoi(value);
2064
2065 for (i = attr->num_values, attrval = attr->values;
2066 i > 0;
2067 i --, attrval ++)
2068 {
2069 if (attrval->integer == int_value)
2070 {
2071 match = 1;
2072 break;
2073 }
2074 }
2075 break;
2076
2077 case IPP_TAG_BOOLEAN :
2078 int_value = !strcmp(value, "true");
2079
2080 for (i = attr->num_values, attrval = attr->values;
2081 i > 0;
2082 i --, attrval ++)
2083 {
2084 if (attrval->boolean == int_value)
2085 {
2086 match = 1;
2087 break;
2088 }
2089 }
2090 break;
2091
2092 case IPP_TAG_RANGE :
2093 int_value = atoi(value);
2094
2095 for (i = attr->num_values, attrval = attr->values;
2096 i > 0;
2097 i --, attrval ++)
2098 {
2099 if (int_value >= attrval->range.lower &&
2100 int_value <= attrval->range.upper)
2101 {
2102 match = 1;
2103 break;
2104 }
2105 }
2106 break;
2107
2108 case IPP_TAG_RESOLUTION :
2109 if (sscanf(value, "%dx%d%15s", &xres_value, &yres_value, temp) != 3)
2110 {
2111 if (sscanf(value, "%d%15s", &xres_value, temp) != 2)
2112 break;
2113
2114 yres_value = xres_value;
2115 }
2116
2117 if (!strcmp(temp, "dpi"))
2118 units_value = IPP_RES_PER_INCH;
2119 else if (!strcmp(temp, "dpc") || !strcmp(temp, "dpcm"))
2120 units_value = IPP_RES_PER_CM;
2121 else
2122 break;
2123
2124 for (i = attr->num_values, attrval = attr->values;
2125 i > 0;
2126 i --, attrval ++)
2127 {
2128 if (attrval->resolution.xres == xres_value &&
2129 attrval->resolution.yres == yres_value &&
2130 attrval->resolution.units == units_value)
2131 {
2132 match = 1;
2133 break;
2134 }
2135 }
2136 break;
2137
2138 case IPP_TAG_TEXT :
2139 case IPP_TAG_NAME :
2140 case IPP_TAG_KEYWORD :
2141 case IPP_TAG_CHARSET :
2142 case IPP_TAG_URI :
2143 case IPP_TAG_URISCHEME :
2144 case IPP_TAG_MIMETYPE :
2145 case IPP_TAG_LANGUAGE :
2146 case IPP_TAG_TEXTLANG :
2147 case IPP_TAG_NAMELANG :
2148 for (i = attr->num_values, attrval = attr->values;
2149 i > 0;
2150 i --, attrval ++)
2151 {
2152 if (!strcmp(attrval->string.text, value))
2153 {
2154 match = 1;
2155 break;
2156 }
2157 }
2158 break;
2159
2160 default :
2161 break;
2162 }
2163
2164 if (!match)
2165 break;
2166
2167 num_matching = cupsAddOption(attr->name, value, num_matching, &matching);
2168 }
2169
2170 if (!attr)
2171 {
2172 if (!active)
2173 active = cupsArrayNew(NULL, NULL);
2174
2175 cupsArrayAdd(active, c);
2176
2177 if (num_conflicts && conflicts)
2178 {
2179 cups_option_t *moption; /* Matching option */
2180
2181 for (i = num_matching, moption = matching; i > 0; i --, moption ++)
2182 *num_conflicts = cupsAddOption(moption->name, moption->value,
2183 *num_conflicts, conflicts);
2184 }
2185 }
2186
2187 cupsFreeOptions(num_matching, matching);
2188 }
2189
2190 return (active);
2191 }
2192
2193
2194 /*
2195 * 'cups_update_ready()' - Update xxx-ready attributes for the printer.
2196 */
2197
2198 static void
2199 cups_update_ready(http_t *http, /* I - Connection to destination */
2200 cups_dinfo_t *dinfo) /* I - Destination information */
2201 {
2202 ipp_t *request; /* Get-Printer-Attributes request */
2203 static const char * const pattrs[] = /* Printer attributes we want */
2204 {
2205 "finishings-col-ready",
2206 "finishings-ready",
2207 "job-finishings-col-ready",
2208 "job-finishings-ready",
2209 "media-col-ready",
2210 "media-ready"
2211 };
2212
2213
2214 /*
2215 * Don't update more than once every 30 seconds...
2216 */
2217
2218 if ((time(NULL) - dinfo->ready_time) < _CUPS_MEDIA_READY_TTL)
2219 return;
2220
2221 /*
2222 * Free any previous results...
2223 */
2224
2225 if (dinfo->cached_flags & CUPS_MEDIA_FLAGS_READY)
2226 {
2227 cupsArrayDelete(dinfo->cached_db);
2228 dinfo->cached_db = NULL;
2229 dinfo->cached_flags = CUPS_MEDIA_FLAGS_DEFAULT;
2230 }
2231
2232 ippDelete(dinfo->ready_attrs);
2233 dinfo->ready_attrs = NULL;
2234
2235 cupsArrayDelete(dinfo->ready_db);
2236 dinfo->ready_db = NULL;
2237
2238 /*
2239 * Query the xxx-ready values...
2240 */
2241
2242 request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
2243 ippSetVersion(request, dinfo->version / 10, dinfo->version % 10);
2244
2245 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
2246 dinfo->uri);
2247 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
2248 NULL, cupsUser());
2249 ippAddStrings(request, IPP_TAG_OPERATION,
2250 IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST, "requested-attributes",
2251 (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: dest-options.c 11085 2013-07-03 13:53:05Z msweet $".
2271 */