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