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