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