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