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