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