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