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