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