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