• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Internet Printing Protocol functions for CUPS.
3  *
4  * Copyright © 2022-2025 by OpenPrinting.
5  * Copyright © 2007-2021 by Apple Inc.
6  * Copyright © 1997-2007 by Easy Software Products, all rights reserved.
7  *
8  * Licensed under Apache License v2.0.  See the file "LICENSE" for more
9  * information.
10  */
11 
12 /*
13  * Include necessary headers...
14  */
15 
16 #include "cups-private.h"
17 #include "debug-internal.h"
18 #include <regex.h>
19 #ifdef _WIN32
20 #  include <io.h>
21 #endif /* _WIN32 */
22 
23 
24 /*
25  * Local functions...
26  */
27 
28 static ipp_attribute_t	*ipp_add_attr(ipp_t *ipp, const char *name,
29 			              ipp_tag_t  group_tag, ipp_tag_t value_tag,
30 			              int num_values);
31 static void		ipp_free_values(ipp_attribute_t *attr, int element,
32 			                int count);
33 static char		*ipp_get_code(const char *locale, char *buffer, size_t bufsize) _CUPS_NONNULL(1,2);
34 static char		*ipp_lang_code(const char *locale, char *buffer, size_t bufsize) _CUPS_NONNULL(1,2);
35 static size_t		ipp_length(ipp_t *ipp, int collection);
36 static ssize_t		ipp_read_file(int *fd, ipp_uchar_t *buffer, size_t length);
37 static ssize_t		ipp_read_http(http_t *http, ipp_uchar_t *buffer, size_t length);
38 static ipp_state_t	ipp_read_io(void *src, ipp_iocb_t cb, int blocking, ipp_t *parent, ipp_t *ipp, int depth);
39 static void		ipp_set_error(ipp_status_t status, const char *format,
40 			              ...);
41 static _ipp_value_t	*ipp_set_value(ipp_t *ipp, ipp_attribute_t **attr,
42 			               int element);
43 static ssize_t		ipp_write_file(int *fd, ipp_uchar_t *buffer,
44 			               size_t length);
45 
46 
47 /*
48  * '_cupsBufferGet()' - Get a read/write buffer.
49  */
50 
51 char *					/* O - Buffer */
_cupsBufferGet(size_t size)52 _cupsBufferGet(size_t size)		/* I - Size required */
53 {
54   _cups_buffer_t	*buffer;	/* Current buffer */
55   _cups_globals_t	*cg = _cupsGlobals();
56 					/* Global data */
57 
58 
59   for (buffer = cg->cups_buffers; buffer; buffer = buffer->next)
60     if (!buffer->used && buffer->size >= size)
61       break;
62 
63   if (!buffer)
64   {
65     if ((buffer = malloc(sizeof(_cups_buffer_t) + size - 1)) == NULL)
66       return (NULL);
67 
68     buffer->next     = cg->cups_buffers;
69     buffer->size     = size;
70     cg->cups_buffers = buffer;
71   }
72 
73   buffer->used = 1;
74 
75   return (buffer->d);
76 }
77 
78 
79 /*
80  * '_cupsBufferRelease()' - Release a read/write buffer.
81  */
82 
83 void
_cupsBufferRelease(char * b)84 _cupsBufferRelease(char *b)		/* I - Buffer to release */
85 {
86   _cups_buffer_t	*buffer;	/* Buffer */
87 
88 
89  /*
90   * Mark this buffer as unused...
91   */
92 
93   buffer       = (_cups_buffer_t *)(b - offsetof(_cups_buffer_t, d));
94   buffer->used = 0;
95 }
96 
97 
98 /*
99  * 'ippAddBoolean()' - Add a boolean attribute to an IPP message.
100  *
101  * The @code ipp@ parameter refers to an IPP message previously created using
102  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
103  *
104  * The @code group@ parameter specifies the IPP attribute group tag: none
105  * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@),
106  * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation
107  * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription
108  * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
109  */
110 
111 ipp_attribute_t *			/* O - New attribute */
ippAddBoolean(ipp_t * ipp,ipp_tag_t group,const char * name,char value)112 ippAddBoolean(ipp_t      *ipp,		/* I - IPP message */
113               ipp_tag_t  group,		/* I - IPP group */
114               const char *name,		/* I - Name of attribute */
115               char       value)		/* I - Value of attribute */
116 {
117   ipp_attribute_t	*attr;		/* New attribute */
118 
119 
120   DEBUG_printf(("ippAddBoolean(ipp=%p, group=%02x(%s), name=\"%s\", value=%d)", (void *)ipp, group, ippTagString(group), name, value));
121 
122  /*
123   * Range check input...
124   */
125 
126   if (!ipp || !name || group < IPP_TAG_ZERO ||
127       group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE)
128     return (NULL);
129 
130  /*
131   * Create the attribute...
132   */
133 
134   if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_BOOLEAN, 1)) == NULL)
135     return (NULL);
136 
137   attr->values[0].boolean = value;
138 
139   return (attr);
140 }
141 
142 
143 /*
144  * 'ippAddBooleans()' - Add an array of boolean values.
145  *
146  * The @code ipp@ parameter refers to an IPP message previously created using
147  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
148  *
149  * The @code group@ parameter specifies the IPP attribute group tag: none
150  * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@),
151  * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation
152  * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription
153  * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
154  */
155 
156 ipp_attribute_t *			/* O - New attribute */
ippAddBooleans(ipp_t * ipp,ipp_tag_t group,const char * name,int num_values,const char * values)157 ippAddBooleans(ipp_t      *ipp,		/* I - IPP message */
158                ipp_tag_t  group,	/* I - IPP group */
159 	       const char *name,	/* I - Name of attribute */
160 	       int        num_values,	/* I - Number of values */
161 	       const char *values)	/* I - Values */
162 {
163   int			i;		/* Looping var */
164   ipp_attribute_t	*attr;		/* New attribute */
165   _ipp_value_t		*value;		/* Current value */
166 
167 
168   DEBUG_printf(("ippAddBooleans(ipp=%p, group=%02x(%s), name=\"%s\", num_values=%d, values=%p)", (void *)ipp, group, ippTagString(group), name, num_values, (void *)values));
169 
170  /*
171   * Range check input...
172   */
173 
174   if (!ipp || !name || group < IPP_TAG_ZERO ||
175       group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
176       num_values < 1)
177     return (NULL);
178 
179  /*
180   * Create the attribute...
181   */
182 
183   if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_BOOLEAN, num_values)) == NULL)
184     return (NULL);
185 
186   if (values)
187   {
188     for (i = num_values, value = attr->values;
189 	 i > 0;
190 	 i --, value ++)
191       value->boolean = *values++;
192   }
193 
194   return (attr);
195 }
196 
197 
198 /*
199  * 'ippAddCollection()' - Add a collection value.
200  *
201  * The @code ipp@ parameter refers to an IPP message previously created using
202  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
203  *
204  * The @code group@ parameter specifies the IPP attribute group tag: none
205  * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@),
206  * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation
207  * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription
208  * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
209  *
210  * @since CUPS 1.1.19/macOS 10.3@
211  */
212 
213 ipp_attribute_t *			/* O - New attribute */
ippAddCollection(ipp_t * ipp,ipp_tag_t group,const char * name,ipp_t * value)214 ippAddCollection(ipp_t      *ipp,	/* I - IPP message */
215                  ipp_tag_t  group,	/* I - IPP group */
216 		 const char *name,	/* I - Name of attribute */
217 		 ipp_t      *value)	/* I - Value */
218 {
219   ipp_attribute_t	*attr;		/* New attribute */
220 
221 
222   DEBUG_printf(("ippAddCollection(ipp=%p, group=%02x(%s), name=\"%s\", value=%p)", (void *)ipp, group, ippTagString(group), name, (void *)value));
223 
224  /*
225   * Range check input...
226   */
227 
228   if (!ipp || !name || group < IPP_TAG_ZERO ||
229       group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE)
230     return (NULL);
231 
232  /*
233   * Create the attribute...
234   */
235 
236   if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_BEGIN_COLLECTION, 1)) == NULL)
237     return (NULL);
238 
239   attr->values[0].collection = value;
240 
241   if (value)
242     value->use ++;
243 
244   return (attr);
245 }
246 
247 
248 /*
249  * 'ippAddCollections()' - Add an array of collection values.
250  *
251  * The @code ipp@ parameter refers to an IPP message previously created using
252  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
253  *
254  * The @code group@ parameter specifies the IPP attribute group tag: none
255  * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@),
256  * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation
257  * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription
258  * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
259  *
260  * @since CUPS 1.1.19/macOS 10.3@
261  */
262 
263 ipp_attribute_t *			/* O - New attribute */
ippAddCollections(ipp_t * ipp,ipp_tag_t group,const char * name,int num_values,const ipp_t ** values)264 ippAddCollections(
265     ipp_t       *ipp,			/* I - IPP message */
266     ipp_tag_t   group,			/* I - IPP group */
267     const char  *name,			/* I - Name of attribute */
268     int         num_values,		/* I - Number of values */
269     const ipp_t **values)		/* I - Values */
270 {
271   int			i;		/* Looping var */
272   ipp_attribute_t	*attr;		/* New attribute */
273   _ipp_value_t		*value;		/* Current value */
274 
275 
276   DEBUG_printf(("ippAddCollections(ipp=%p, group=%02x(%s), name=\"%s\", num_values=%d, values=%p)", (void *)ipp, group, ippTagString(group), name, num_values, (void *)values));
277 
278  /*
279   * Range check input...
280   */
281 
282   if (!ipp || !name || group < IPP_TAG_ZERO ||
283       group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
284       num_values < 1)
285     return (NULL);
286 
287  /*
288   * Create the attribute...
289   */
290 
291   if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_BEGIN_COLLECTION,
292                            num_values)) == NULL)
293     return (NULL);
294 
295   if (values)
296   {
297     for (i = num_values, value = attr->values;
298 	 i > 0;
299 	 i --, value ++)
300     {
301       value->collection = (ipp_t *)*values++;
302       value->collection->use ++;
303     }
304   }
305 
306   return (attr);
307 }
308 
309 
310 /*
311  * 'ippAddDate()' - Add a dateTime attribute to an IPP message.
312  *
313  * The @code ipp@ parameter refers to an IPP message previously created using
314  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
315  *
316  * The @code group@ parameter specifies the IPP attribute group tag: none
317  * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@),
318  * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation
319  * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription
320  * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
321  */
322 
323 ipp_attribute_t *			/* O - New attribute */
ippAddDate(ipp_t * ipp,ipp_tag_t group,const char * name,const ipp_uchar_t * value)324 ippAddDate(ipp_t             *ipp,	/* I - IPP message */
325            ipp_tag_t         group,	/* I - IPP group */
326 	   const char        *name,	/* I - Name of attribute */
327 	   const ipp_uchar_t *value)	/* I - Value */
328 {
329   ipp_attribute_t	*attr;		/* New attribute */
330 
331 
332   DEBUG_printf(("ippAddDate(ipp=%p, group=%02x(%s), name=\"%s\", value=%p)", (void *)ipp, group, ippTagString(group), name, (void *)value));
333 
334  /*
335   * Range check input...
336   */
337 
338   if (!ipp || !name || !value || group < IPP_TAG_ZERO ||
339       group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE)
340     return (NULL);
341 
342  /*
343   * Create the attribute...
344   */
345 
346   if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_DATE, 1)) == NULL)
347     return (NULL);
348 
349   memcpy(attr->values[0].date, value, 11);
350 
351   return (attr);
352 }
353 
354 
355 /*
356  * 'ippAddInteger()' - Add a integer attribute to an IPP message.
357  *
358  * The @code ipp@ parameter refers to an IPP message previously created using
359  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
360  *
361  * The @code group@ parameter specifies the IPP attribute group tag: none
362  * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@),
363  * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation
364  * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription
365  * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
366  *
367  * Supported values include enum (@code IPP_TAG_ENUM@) and integer
368  * (@code IPP_TAG_INTEGER@).
369  */
370 
371 ipp_attribute_t *			/* O - New attribute */
ippAddInteger(ipp_t * ipp,ipp_tag_t group,ipp_tag_t value_tag,const char * name,int value)372 ippAddInteger(ipp_t      *ipp,		/* I - IPP message */
373               ipp_tag_t  group,		/* I - IPP group */
374 	      ipp_tag_t  value_tag,	/* I - Type of attribute */
375               const char *name,		/* I - Name of attribute */
376               int        value)		/* I - Value of attribute */
377 {
378   ipp_attribute_t	*attr;		/* New attribute */
379 
380 
381   DEBUG_printf(("ippAddInteger(ipp=%p, group=%02x(%s), type=%02x(%s), name=\"%s\", value=%d)", (void *)ipp, group, ippTagString(group), value_tag, ippTagString(value_tag), name, value));
382 
383   value_tag &= IPP_TAG_CUPS_MASK;
384 
385  /*
386   * Special-case for legacy usage: map out-of-band attributes to new ippAddOutOfBand
387   * function...
388   */
389 
390   if (value_tag >= IPP_TAG_UNSUPPORTED_VALUE && value_tag <= IPP_TAG_ADMINDEFINE)
391     return (ippAddOutOfBand(ipp, group, value_tag, name));
392 
393  /*
394   * Range check input...
395   */
396 
397 #if 0
398   if (!ipp || !name || group < IPP_TAG_ZERO ||
399       group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
400       (value_tag != IPP_TAG_INTEGER && value_tag != IPP_TAG_ENUM))
401     return (NULL);
402 #else
403   if (!ipp || !name || group < IPP_TAG_ZERO ||
404       group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE)
405     return (NULL);
406 #endif /* 0 */
407 
408  /*
409   * Create the attribute...
410   */
411 
412   if ((attr = ipp_add_attr(ipp, name, group, value_tag, 1)) == NULL)
413     return (NULL);
414 
415   attr->values[0].integer = value;
416 
417   return (attr);
418 }
419 
420 
421 /*
422  * 'ippAddIntegers()' - Add an array of integer values.
423  *
424  * The @code ipp@ parameter refers to an IPP message previously created using
425  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
426  *
427  * The @code group@ parameter specifies the IPP attribute group tag: none
428  * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@),
429  * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation
430  * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription
431  * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
432  *
433  * Supported values include enum (@code IPP_TAG_ENUM@) and integer
434  * (@code IPP_TAG_INTEGER@).
435  */
436 
437 ipp_attribute_t *			/* O - New attribute */
ippAddIntegers(ipp_t * ipp,ipp_tag_t group,ipp_tag_t value_tag,const char * name,int num_values,const int * values)438 ippAddIntegers(ipp_t      *ipp,		/* I - IPP message */
439                ipp_tag_t  group,	/* I - IPP group */
440 	       ipp_tag_t  value_tag,	/* I - Type of attribute */
441 	       const char *name,	/* I - Name of attribute */
442 	       int        num_values,	/* I - Number of values */
443 	       const int  *values)	/* I - Values */
444 {
445   int			i;		/* Looping var */
446   ipp_attribute_t	*attr;		/* New attribute */
447   _ipp_value_t		*value;		/* Current value */
448 
449 
450   DEBUG_printf(("ippAddIntegers(ipp=%p, group=%02x(%s), type=%02x(%s), name=\"%s\", num_values=%d, values=%p)", (void *)ipp, group, ippTagString(group), value_tag, ippTagString(value_tag), name, num_values, (void *)values));
451 
452   value_tag &= IPP_TAG_CUPS_MASK;
453 
454  /*
455   * Range check input...
456   */
457 
458 #if 0
459   if (!ipp || !name || group < IPP_TAG_ZERO ||
460       group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
461       (value_tag != IPP_TAG_INTEGER && value_tag != IPP_TAG_ENUM) ||
462       num_values < 1)
463     return (NULL);
464 #else
465   if (!ipp || !name || group < IPP_TAG_ZERO ||
466       group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
467       num_values < 1)
468     return (NULL);
469 #endif /* 0 */
470 
471  /*
472   * Create the attribute...
473   */
474 
475   if ((attr = ipp_add_attr(ipp, name, group, value_tag, num_values)) == NULL)
476     return (NULL);
477 
478   if (values)
479   {
480     for (i = num_values, value = attr->values;
481 	 i > 0;
482 	 i --, value ++)
483       value->integer = *values++;
484   }
485 
486   return (attr);
487 }
488 
489 
490 /*
491  * 'ippAddOctetString()' - Add an octetString value to an IPP message.
492  *
493  * The @code ipp@ parameter refers to an IPP message previously created using
494  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
495  *
496  * The @code group@ parameter specifies the IPP attribute group tag: none
497  * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@),
498  * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation
499  * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription
500  * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
501  *
502  * @since CUPS 1.2/macOS 10.5@
503  */
504 
505 ipp_attribute_t	*			/* O - New attribute */
ippAddOctetString(ipp_t * ipp,ipp_tag_t group,const char * name,const void * data,int datalen)506 ippAddOctetString(ipp_t      *ipp,	/* I - IPP message */
507                   ipp_tag_t  group,	/* I - IPP group */
508                   const char *name,	/* I - Name of attribute */
509                   const void *data,	/* I - octetString data */
510 		  int        datalen)	/* I - Length of data in bytes */
511 {
512   ipp_attribute_t	*attr;		/* New attribute */
513 
514 
515   if (!ipp || !name || group < IPP_TAG_ZERO ||
516       group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
517       datalen < 0 || datalen > IPP_MAX_LENGTH)
518     return (NULL);
519 
520   if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_STRING, 1)) == NULL)
521     return (NULL);
522 
523  /*
524   * Initialize the attribute data...
525   */
526 
527   attr->values[0].unknown.length = datalen;
528 
529   if (data)
530   {
531     if ((attr->values[0].unknown.data = malloc((size_t)datalen)) == NULL)
532     {
533       ippDeleteAttribute(ipp, attr);
534       return (NULL);
535     }
536 
537     memcpy(attr->values[0].unknown.data, data, (size_t)datalen);
538   }
539 
540  /*
541   * Return the new attribute...
542   */
543 
544   return (attr);
545 }
546 
547 
548 /*
549  * 'ippAddOutOfBand()' - Add an out-of-band value to an IPP message.
550  *
551  * The @code ipp@ parameter refers to an IPP message previously created using
552  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
553  *
554  * The @code group@ parameter specifies the IPP attribute group tag: none
555  * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@),
556  * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation
557  * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription
558  * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
559  *
560  * Supported out-of-band values include unsupported-value
561  * (@code IPP_TAG_UNSUPPORTED_VALUE@), default (@code IPP_TAG_DEFAULT@), unknown
562  * (@code IPP_TAG_UNKNOWN@), no-value (@code IPP_TAG_NOVALUE@), not-settable
563  * (@code IPP_TAG_NOTSETTABLE@), delete-attribute (@code IPP_TAG_DELETEATTR@), and
564  * admin-define (@code IPP_TAG_ADMINDEFINE@).
565  *
566  * @since CUPS 1.6/macOS 10.8@
567  */
568 
569 ipp_attribute_t	*			/* O - New attribute */
ippAddOutOfBand(ipp_t * ipp,ipp_tag_t group,ipp_tag_t value_tag,const char * name)570 ippAddOutOfBand(ipp_t      *ipp,	/* I - IPP message */
571                 ipp_tag_t  group,	/* I - IPP group */
572                 ipp_tag_t  value_tag,	/* I - Type of attribute */
573 		const char *name)	/* I - Name of attribute */
574 {
575   DEBUG_printf(("ippAddOutOfBand(ipp=%p, group=%02x(%s), value_tag=%02x(%s), name=\"%s\")", (void *)ipp, group, ippTagString(group), value_tag, ippTagString(value_tag), name));
576 
577   value_tag &= IPP_TAG_CUPS_MASK;
578 
579  /*
580   * Range check input...
581   */
582 
583   if (!ipp || !name || group < IPP_TAG_ZERO ||
584       group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
585       (value_tag != IPP_TAG_UNSUPPORTED_VALUE &&
586        value_tag != IPP_TAG_DEFAULT &&
587        value_tag != IPP_TAG_UNKNOWN &&
588        value_tag != IPP_TAG_NOVALUE &&
589        value_tag != IPP_TAG_NOTSETTABLE &&
590        value_tag != IPP_TAG_DELETEATTR &&
591        value_tag != IPP_TAG_ADMINDEFINE))
592     return (NULL);
593 
594  /*
595   * Create the attribute...
596   */
597 
598   return (ipp_add_attr(ipp, name, group, value_tag, 1));
599 }
600 
601 
602 /*
603  * 'ippAddRange()' - Add a range of values to an IPP message.
604  *
605  * The @code ipp@ parameter refers to an IPP message previously created using
606  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
607  *
608  * The @code group@ parameter specifies the IPP attribute group tag: none
609  * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@),
610  * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation
611  * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription
612  * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
613  *
614  * The @code lower@ parameter must be less than or equal to the @code upper@ parameter.
615  */
616 
617 ipp_attribute_t *			/* O - New attribute */
ippAddRange(ipp_t * ipp,ipp_tag_t group,const char * name,int lower,int upper)618 ippAddRange(ipp_t      *ipp,		/* I - IPP message */
619             ipp_tag_t  group,		/* I - IPP group */
620 	    const char *name,		/* I - Name of attribute */
621 	    int        lower,		/* I - Lower value */
622 	    int        upper)		/* I - Upper value */
623 {
624   ipp_attribute_t	*attr;		/* New attribute */
625 
626 
627   DEBUG_printf(("ippAddRange(ipp=%p, group=%02x(%s), name=\"%s\", lower=%d, upper=%d)", (void *)ipp, group, ippTagString(group), name, lower, upper));
628 
629  /*
630   * Range check input...
631   */
632 
633   if (!ipp || !name || group < IPP_TAG_ZERO ||
634       group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE)
635     return (NULL);
636 
637  /*
638   * Create the attribute...
639   */
640 
641   if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_RANGE, 1)) == NULL)
642     return (NULL);
643 
644   attr->values[0].range.lower = lower;
645   attr->values[0].range.upper = upper;
646 
647   return (attr);
648 }
649 
650 
651 /*
652  * 'ippAddRanges()' - Add ranges of values to an IPP message.
653  *
654  * The @code ipp@ parameter refers to an IPP message previously created using
655  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
656  *
657  * The @code group@ parameter specifies the IPP attribute group tag: none
658  * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@),
659  * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation
660  * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription
661  * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
662  */
663 
664 ipp_attribute_t *			/* O - New attribute */
ippAddRanges(ipp_t * ipp,ipp_tag_t group,const char * name,int num_values,const int * lower,const int * upper)665 ippAddRanges(ipp_t      *ipp,		/* I - IPP message */
666              ipp_tag_t  group,		/* I - IPP group */
667 	     const char *name,		/* I - Name of attribute */
668 	     int        num_values,	/* I - Number of values */
669 	     const int  *lower,		/* I - Lower values */
670 	     const int  *upper)		/* I - Upper values */
671 {
672   int			i;		/* Looping var */
673   ipp_attribute_t	*attr;		/* New attribute */
674   _ipp_value_t		*value;		/* Current value */
675 
676 
677   DEBUG_printf(("ippAddRanges(ipp=%p, group=%02x(%s), name=\"%s\", num_values=%d, lower=%p, upper=%p)", (void *)ipp, group, ippTagString(group), name, num_values, (void *)lower, (void *)upper));
678 
679  /*
680   * Range check input...
681   */
682 
683   if (!ipp || !name || group < IPP_TAG_ZERO ||
684       group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
685       num_values < 1)
686     return (NULL);
687 
688  /*
689   * Create the attribute...
690   */
691 
692   if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_RANGE, num_values)) == NULL)
693     return (NULL);
694 
695   if (lower && upper)
696   {
697     for (i = num_values, value = attr->values;
698 	 i > 0;
699 	 i --, value ++)
700     {
701       value->range.lower = *lower++;
702       value->range.upper = *upper++;
703     }
704   }
705 
706   return (attr);
707 }
708 
709 
710 /*
711  * 'ippAddResolution()' - Add a resolution value to an IPP message.
712  *
713  * The @code ipp@ parameter refers to an IPP message previously created using
714  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
715  *
716  * The @code group@ parameter specifies the IPP attribute group tag: none
717  * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@),
718  * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation
719  * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription
720  * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
721  */
722 
723 ipp_attribute_t *			/* O - New attribute */
ippAddResolution(ipp_t * ipp,ipp_tag_t group,const char * name,ipp_res_t units,int xres,int yres)724 ippAddResolution(ipp_t      *ipp,	/* I - IPP message */
725         	 ipp_tag_t  group,	/* I - IPP group */
726 		 const char *name,	/* I - Name of attribute */
727 		 ipp_res_t  units,	/* I - Units for resolution */
728 		 int        xres,	/* I - X resolution */
729 		 int        yres)	/* I - Y resolution */
730 {
731   ipp_attribute_t	*attr;		/* New attribute */
732 
733 
734   DEBUG_printf(("ippAddResolution(ipp=%p, group=%02x(%s), name=\"%s\", units=%d, xres=%d, yres=%d)", (void *)ipp, group,
735 		ippTagString(group), name, units, xres, yres));
736 
737  /*
738   * Range check input...
739   */
740 
741   if (!ipp || !name || group < IPP_TAG_ZERO ||
742       group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
743       units < IPP_RES_PER_INCH || units > IPP_RES_PER_CM ||
744       xres < 0 || yres < 0)
745     return (NULL);
746 
747  /*
748   * Create the attribute...
749   */
750 
751   if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_RESOLUTION, 1)) == NULL)
752     return (NULL);
753 
754   attr->values[0].resolution.xres  = xres;
755   attr->values[0].resolution.yres  = yres;
756   attr->values[0].resolution.units = units;
757 
758   return (attr);
759 }
760 
761 
762 /*
763  * 'ippAddResolutions()' - Add resolution values to an IPP message.
764  *
765  * The @code ipp@ parameter refers to an IPP message previously created using
766  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
767  *
768  * The @code group@ parameter specifies the IPP attribute group tag: none
769  * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@),
770  * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation
771  * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription
772  * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
773  */
774 
775 ipp_attribute_t *			/* O - New attribute */
ippAddResolutions(ipp_t * ipp,ipp_tag_t group,const char * name,int num_values,ipp_res_t units,const int * xres,const int * yres)776 ippAddResolutions(ipp_t      *ipp,	/* I - IPP message */
777         	  ipp_tag_t  group,	/* I - IPP group */
778 		  const char *name,	/* I - Name of attribute */
779 		  int        num_values,/* I - Number of values */
780 		  ipp_res_t  units,	/* I - Units for resolution */
781 		  const int  *xres,	/* I - X resolutions */
782 		  const int  *yres)	/* I - Y resolutions */
783 {
784   int			i;		/* Looping var */
785   ipp_attribute_t	*attr;		/* New attribute */
786   _ipp_value_t		*value;		/* Current value */
787 
788 
789   DEBUG_printf(("ippAddResolutions(ipp=%p, group=%02x(%s), name=\"%s\", num_value=%d, units=%d, xres=%p, yres=%p)", (void *)ipp, group, ippTagString(group), name, num_values, units, (void *)xres, (void *)yres));
790 
791  /*
792   * Range check input...
793   */
794 
795   if (!ipp || !name || group < IPP_TAG_ZERO ||
796       group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
797       num_values < 1 ||
798       units < IPP_RES_PER_INCH || units > IPP_RES_PER_CM)
799     return (NULL);
800 
801  /*
802   * Create the attribute...
803   */
804 
805   if ((attr = ipp_add_attr(ipp, name, group, IPP_TAG_RESOLUTION, num_values)) == NULL)
806     return (NULL);
807 
808   if (xres && yres)
809   {
810     for (i = num_values, value = attr->values;
811 	 i > 0;
812 	 i --, value ++)
813     {
814       value->resolution.xres  = *xres++;
815       value->resolution.yres  = *yres++;
816       value->resolution.units = units;
817     }
818   }
819 
820   return (attr);
821 }
822 
823 
824 /*
825  * 'ippAddSeparator()' - Add a group separator to an IPP message.
826  *
827  * The @code ipp@ parameter refers to an IPP message previously created using
828  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
829  */
830 
831 ipp_attribute_t *			/* O - New attribute */
ippAddSeparator(ipp_t * ipp)832 ippAddSeparator(ipp_t *ipp)		/* I - IPP message */
833 {
834   DEBUG_printf(("ippAddSeparator(ipp=%p)", (void *)ipp));
835 
836  /*
837   * Range check input...
838   */
839 
840   if (!ipp)
841     return (NULL);
842 
843  /*
844   * Create the attribute...
845   */
846 
847   return (ipp_add_attr(ipp, NULL, IPP_TAG_ZERO, IPP_TAG_ZERO, 0));
848 }
849 
850 
851 /*
852  * 'ippAddString()' - Add a language-encoded string to an IPP message.
853  *
854  * The @code ipp@ parameter refers to an IPP message previously created using
855  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
856  *
857  * The @code group@ parameter specifies the IPP attribute group tag: none
858  * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@),
859  * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation
860  * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription
861  * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
862  *
863  * Supported string values include charset (@code IPP_TAG_CHARSET@), keyword
864  * (@code IPP_TAG_KEYWORD@), language (@code IPP_TAG_LANGUAGE@), mimeMediaType
865  * (@code IPP_TAG_MIMETYPE@), name (@code IPP_TAG_NAME@), nameWithLanguage
866  * (@code IPP_TAG_NAMELANG), text (@code IPP_TAG_TEXT@), textWithLanguage
867  * (@code IPP_TAG_TEXTLANG@), uri (@code IPP_TAG_URI@), and uriScheme
868  * (@code IPP_TAG_URISCHEME@).
869  *
870  * The @code language@ parameter must be non-@code NULL@ for nameWithLanguage and
871  * textWithLanguage string values and must be @code NULL@ for all other string values.
872  */
873 
874 ipp_attribute_t *			/* O - New attribute */
ippAddString(ipp_t * ipp,ipp_tag_t group,ipp_tag_t value_tag,const char * name,const char * language,const char * value)875 ippAddString(ipp_t      *ipp,		/* I - IPP message */
876              ipp_tag_t  group,		/* I - IPP group */
877 	     ipp_tag_t  value_tag,	/* I - Type of attribute */
878              const char *name,		/* I - Name of attribute */
879              const char *language,	/* I - Language code */
880              const char *value)		/* I - Value */
881 {
882   ipp_tag_t		temp_tag;	/* Temporary value tag (masked) */
883   ipp_attribute_t	*attr;		/* New attribute */
884   char			code[IPP_MAX_LANGUAGE];
885 					/* Charset/language code buffer */
886 
887 
888   DEBUG_printf(("ippAddString(ipp=%p, group=%02x(%s), value_tag=%02x(%s), name=\"%s\", language=\"%s\", value=\"%s\")", (void *)ipp, group, ippTagString(group), value_tag, ippTagString(value_tag), name, language, value));
889 
890  /*
891   * Range check input...
892   */
893 
894   temp_tag = (ipp_tag_t)((int)value_tag & IPP_TAG_CUPS_MASK);
895 
896 #if 0
897   if (!ipp || !name || group < IPP_TAG_ZERO ||
898       group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
899       (temp_tag < IPP_TAG_TEXT && temp_tag != IPP_TAG_TEXTLANG &&
900        temp_tag != IPP_TAG_NAMELANG) || temp_tag > IPP_TAG_MIMETYPE)
901     return (NULL);
902 
903   if ((temp_tag == IPP_TAG_TEXTLANG || temp_tag == IPP_TAG_NAMELANG)
904           != (language != NULL))
905     return (NULL);
906 #else
907   if (!ipp || !name || group < IPP_TAG_ZERO ||
908       group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE)
909     return (NULL);
910 #endif /* 0 */
911 
912  /*
913   * See if we need to map charset, language, or locale values...
914   */
915 
916   if (language && ((int)value_tag & IPP_TAG_CUPS_CONST) &&
917       strcmp(language, ipp_lang_code(language, code, sizeof(code))))
918     value_tag = temp_tag;		/* Don't do a fast copy */
919   else if (value && value_tag == (ipp_tag_t)(IPP_TAG_CHARSET | IPP_TAG_CUPS_CONST) &&
920            strcmp(value, ipp_get_code(value, code, sizeof(code))))
921     value_tag = temp_tag;		/* Don't do a fast copy */
922   else if (value && value_tag == (ipp_tag_t)(IPP_TAG_LANGUAGE | IPP_TAG_CUPS_CONST) &&
923            strcmp(value, ipp_lang_code(value, code, sizeof(code))))
924     value_tag = temp_tag;		/* Don't do a fast copy */
925 
926  /*
927   * Create the attribute...
928   */
929 
930   if ((attr = ipp_add_attr(ipp, name, group, value_tag, 1)) == NULL)
931     return (NULL);
932 
933  /*
934   * Initialize the attribute data...
935   */
936 
937   if ((int)value_tag & IPP_TAG_CUPS_CONST)
938   {
939     attr->values[0].string.language = (char *)language;
940     attr->values[0].string.text     = (char *)value;
941   }
942   else
943   {
944     if (language)
945       attr->values[0].string.language = _cupsStrAlloc(ipp_lang_code(language, code,
946 						      sizeof(code)));
947 
948     if (value)
949     {
950       if (value_tag == IPP_TAG_CHARSET)
951 	attr->values[0].string.text = _cupsStrAlloc(ipp_get_code(value, code,
952 								 sizeof(code)));
953       else if (value_tag == IPP_TAG_LANGUAGE)
954 	attr->values[0].string.text = _cupsStrAlloc(ipp_lang_code(value, code,
955 								  sizeof(code)));
956       else
957 	attr->values[0].string.text = _cupsStrAlloc(value);
958     }
959   }
960 
961   return (attr);
962 }
963 
964 
965 /*
966  * 'ippAddStringf()' - Add a formatted string to an IPP message.
967  *
968  * The @code ipp@ parameter refers to an IPP message previously created using
969  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
970  *
971  * The @code group@ parameter specifies the IPP attribute group tag: none
972  * (@code IPP_TAG_ZERO@, for member attributes), document
973  * (@code IPP_TAG_DOCUMENT@), event notification
974  * (@code IPP_TAG_EVENT_NOTIFICATION@), operation (@code IPP_TAG_OPERATION@),
975  * printer (@code IPP_TAG_PRINTER@), subscription (@code IPP_TAG_SUBSCRIPTION@),
976  * or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
977  *
978  * Supported string values include charset (@code IPP_TAG_CHARSET@), keyword
979  * (@code IPP_TAG_KEYWORD@), language (@code IPP_TAG_LANGUAGE@), mimeMediaType
980  * (@code IPP_TAG_MIMETYPE@), name (@code IPP_TAG_NAME@), nameWithLanguage
981  * (@code IPP_TAG_NAMELANG), text (@code IPP_TAG_TEXT@), textWithLanguage
982  * (@code IPP_TAG_TEXTLANG@), uri (@code IPP_TAG_URI@), and uriScheme
983  * (@code IPP_TAG_URISCHEME@).
984  *
985  * The @code language@ parameter must be non-@code NULL@ for nameWithLanguage
986  * and textWithLanguage string values and must be @code NULL@ for all other
987  * string values.
988  *
989  * The @code format@ parameter uses formatting characters compatible with the
990  * printf family of standard functions.  Additional arguments follow it as
991  * needed.  The formatted string is truncated as needed to the maximum length of
992  * the corresponding value type.
993  *
994  * @since CUPS 1.7/macOS 10.9@
995  */
996 
997 ipp_attribute_t *			/* O - New attribute */
ippAddStringf(ipp_t * ipp,ipp_tag_t group,ipp_tag_t value_tag,const char * name,const char * language,const char * format,...)998 ippAddStringf(ipp_t      *ipp,		/* I - IPP message */
999               ipp_tag_t  group,		/* I - IPP group */
1000 	      ipp_tag_t  value_tag,	/* I - Type of attribute */
1001 	      const char *name,		/* I - Name of attribute */
1002 	      const char *language,	/* I - Language code (@code NULL@ for default) */
1003 	      const char *format,	/* I - Printf-style format string */
1004 	      ...)			/* I - Additional arguments as needed */
1005 {
1006   ipp_attribute_t	*attr;		/* New attribute */
1007   va_list		ap;		/* Argument pointer */
1008 
1009 
1010   va_start(ap, format);
1011   attr = ippAddStringfv(ipp, group, value_tag, name, language, format, ap);
1012   va_end(ap);
1013 
1014   return (attr);
1015 }
1016 
1017 
1018 /*
1019  * 'ippAddStringfv()' - Add a formatted string to an IPP message.
1020  *
1021  * The @code ipp@ parameter refers to an IPP message previously created using
1022  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
1023  *
1024  * The @code group@ parameter specifies the IPP attribute group tag: none
1025  * (@code IPP_TAG_ZERO@, for member attributes), document
1026  * (@code IPP_TAG_DOCUMENT@), event notification
1027  * (@code IPP_TAG_EVENT_NOTIFICATION@), operation (@code IPP_TAG_OPERATION@),
1028  * printer (@code IPP_TAG_PRINTER@), subscription (@code IPP_TAG_SUBSCRIPTION@),
1029  * or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
1030  *
1031  * Supported string values include charset (@code IPP_TAG_CHARSET@), keyword
1032  * (@code IPP_TAG_KEYWORD@), language (@code IPP_TAG_LANGUAGE@), mimeMediaType
1033  * (@code IPP_TAG_MIMETYPE@), name (@code IPP_TAG_NAME@), nameWithLanguage
1034  * (@code IPP_TAG_NAMELANG), text (@code IPP_TAG_TEXT@), textWithLanguage
1035  * (@code IPP_TAG_TEXTLANG@), uri (@code IPP_TAG_URI@), and uriScheme
1036  * (@code IPP_TAG_URISCHEME@).
1037  *
1038  * The @code language@ parameter must be non-@code NULL@ for nameWithLanguage
1039  * and textWithLanguage string values and must be @code NULL@ for all other
1040  * string values.
1041  *
1042  * The @code format@ parameter uses formatting characters compatible with the
1043  * printf family of standard functions.  Additional arguments are passed in the
1044  * stdarg pointer @code ap@.  The formatted string is truncated as needed to the
1045  * maximum length of the corresponding value type.
1046  *
1047  * @since CUPS 1.7/macOS 10.9@
1048  */
1049 
1050 ipp_attribute_t *			/* O - New attribute */
ippAddStringfv(ipp_t * ipp,ipp_tag_t group,ipp_tag_t value_tag,const char * name,const char * language,const char * format,va_list ap)1051 ippAddStringfv(ipp_t      *ipp,		/* I - IPP message */
1052                ipp_tag_t  group,	/* I - IPP group */
1053 	       ipp_tag_t  value_tag,	/* I - Type of attribute */
1054 	       const char *name,	/* I - Name of attribute */
1055 	       const char *language,	/* I - Language code (@code NULL@ for default) */
1056 	       const char *format,	/* I - Printf-style format string */
1057 	       va_list    ap)		/* I - Additional arguments */
1058 {
1059   char		buffer[IPP_MAX_TEXT + 4];
1060 					/* Formatted text string */
1061   ssize_t	bytes,			/* Length of formatted value */
1062 		max_bytes;		/* Maximum number of bytes for value */
1063 
1064 
1065  /*
1066   * Range check input...
1067   */
1068 
1069   if (!ipp || !name || group < IPP_TAG_ZERO ||
1070       group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
1071       (value_tag < IPP_TAG_TEXT && value_tag != IPP_TAG_TEXTLANG &&
1072        value_tag != IPP_TAG_NAMELANG) || value_tag > IPP_TAG_MIMETYPE ||
1073       !format)
1074     return (NULL);
1075 
1076   if ((value_tag == IPP_TAG_TEXTLANG || value_tag == IPP_TAG_NAMELANG)
1077           != (language != NULL))
1078     return (NULL);
1079 
1080  /*
1081   * Format the string...
1082   */
1083 
1084   if (!strcmp(format, "%s"))
1085   {
1086    /*
1087     * Optimize the simple case...
1088     */
1089 
1090     const char *s = va_arg(ap, char *);
1091 
1092     if (!s)
1093       s = "(null)";
1094 
1095     bytes = (ssize_t)strlen(s);
1096     strlcpy(buffer, s, sizeof(buffer));
1097   }
1098   else
1099   {
1100    /*
1101     * Do a full formatting of the message...
1102     */
1103 
1104     if ((bytes = vsnprintf(buffer, sizeof(buffer), format, ap)) < 0)
1105       return (NULL);
1106   }
1107 
1108  /*
1109   * Limit the length of the string...
1110   */
1111 
1112   switch (value_tag)
1113   {
1114     default :
1115     case IPP_TAG_TEXT :
1116     case IPP_TAG_TEXTLANG :
1117         max_bytes = IPP_MAX_TEXT;
1118         break;
1119 
1120     case IPP_TAG_NAME :
1121     case IPP_TAG_NAMELANG :
1122         max_bytes = IPP_MAX_NAME;
1123         break;
1124 
1125     case IPP_TAG_CHARSET :
1126         max_bytes = IPP_MAX_CHARSET;
1127         break;
1128 
1129     case IPP_TAG_KEYWORD :
1130         max_bytes = IPP_MAX_KEYWORD;
1131         break;
1132 
1133     case IPP_TAG_LANGUAGE :
1134         max_bytes = IPP_MAX_LANGUAGE;
1135         break;
1136 
1137     case IPP_TAG_MIMETYPE :
1138         max_bytes = IPP_MAX_MIMETYPE;
1139         break;
1140 
1141     case IPP_TAG_URI :
1142         max_bytes = IPP_MAX_URI;
1143         break;
1144 
1145     case IPP_TAG_URISCHEME :
1146         max_bytes = IPP_MAX_URISCHEME;
1147         break;
1148   }
1149 
1150   if (bytes >= max_bytes)
1151   {
1152     char	*bufmax,		/* Buffer at max_bytes */
1153 		*bufptr;		/* Pointer into buffer */
1154 
1155     bufptr = buffer + strlen(buffer) - 1;
1156     bufmax = buffer + max_bytes - 1;
1157 
1158     while (bufptr > bufmax)
1159     {
1160       if (*bufptr & 0x80)
1161       {
1162         while ((*bufptr & 0xc0) == 0x80 && bufptr > buffer)
1163           bufptr --;
1164       }
1165 
1166       bufptr --;
1167     }
1168 
1169     *bufptr = '\0';
1170   }
1171 
1172  /*
1173   * Add the formatted string and return...
1174   */
1175 
1176   return (ippAddString(ipp, group, value_tag, name, language, buffer));
1177 }
1178 
1179 
1180 /*
1181  * 'ippAddStrings()' - Add language-encoded strings to an IPP message.
1182  *
1183  * The @code ipp@ parameter refers to an IPP message previously created using
1184  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
1185  *
1186  * The @code group@ parameter specifies the IPP attribute group tag: none
1187  * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@),
1188  * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation
1189  * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription
1190  * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
1191  *
1192  * Supported string values include charset (@code IPP_TAG_CHARSET@), keyword
1193  * (@code IPP_TAG_KEYWORD@), language (@code IPP_TAG_LANGUAGE@), mimeMediaType
1194  * (@code IPP_TAG_MIMETYPE@), name (@code IPP_TAG_NAME@), nameWithLanguage
1195  * (@code IPP_TAG_NAMELANG), text (@code IPP_TAG_TEXT@), textWithLanguage
1196  * (@code IPP_TAG_TEXTLANG@), uri (@code IPP_TAG_URI@), and uriScheme
1197  * (@code IPP_TAG_URISCHEME@).
1198  *
1199  * The @code language@ parameter must be non-@code NULL@ for nameWithLanguage and
1200  * textWithLanguage string values and must be @code NULL@ for all other string values.
1201  */
1202 
1203 ipp_attribute_t *			/* O - New attribute */
ippAddStrings(ipp_t * ipp,ipp_tag_t group,ipp_tag_t value_tag,const char * name,int num_values,const char * language,const char * const * values)1204 ippAddStrings(
1205     ipp_t              *ipp,		/* I - IPP message */
1206     ipp_tag_t          group,		/* I - IPP group */
1207     ipp_tag_t          value_tag,	/* I - Type of attribute */
1208     const char         *name,		/* I - Name of attribute */
1209     int                num_values,	/* I - Number of values */
1210     const char         *language,	/* I - Language code (@code NULL@ for default) */
1211     const char * const *values)		/* I - Values */
1212 {
1213   int			i;		/* Looping var */
1214   ipp_tag_t		temp_tag;	/* Temporary value tag (masked) */
1215   ipp_attribute_t	*attr;		/* New attribute */
1216   _ipp_value_t		*value;		/* Current value */
1217   char			code[32];	/* Language/charset value buffer */
1218 
1219 
1220   DEBUG_printf(("ippAddStrings(ipp=%p, group=%02x(%s), value_tag=%02x(%s), name=\"%s\", num_values=%d, language=\"%s\", values=%p)", (void *)ipp, group, ippTagString(group), value_tag, ippTagString(value_tag), name, num_values, language, (void *)values));
1221 
1222  /*
1223   * Range check input...
1224   */
1225 
1226   temp_tag = (ipp_tag_t)((int)value_tag & IPP_TAG_CUPS_MASK);
1227 
1228 #if 0
1229   if (!ipp || !name || group < IPP_TAG_ZERO ||
1230       group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
1231       (temp_tag < IPP_TAG_TEXT && temp_tag != IPP_TAG_TEXTLANG &&
1232        temp_tag != IPP_TAG_NAMELANG) || temp_tag > IPP_TAG_MIMETYPE ||
1233       num_values < 1)
1234     return (NULL);
1235 
1236   if ((temp_tag == IPP_TAG_TEXTLANG || temp_tag == IPP_TAG_NAMELANG)
1237           != (language != NULL))
1238     return (NULL);
1239 #else
1240   if (!ipp || !name || group < IPP_TAG_ZERO ||
1241       group == IPP_TAG_END || group >= IPP_TAG_UNSUPPORTED_VALUE ||
1242       num_values < 1)
1243     return (NULL);
1244 #endif /* 0 */
1245 
1246  /*
1247   * See if we need to map charset, language, or locale values...
1248   */
1249 
1250   if (language && ((int)value_tag & IPP_TAG_CUPS_CONST) &&
1251       strcmp(language, ipp_lang_code(language, code, sizeof(code))))
1252     value_tag = temp_tag;		/* Don't do a fast copy */
1253   else if (values && value_tag == (ipp_tag_t)(IPP_TAG_CHARSET | IPP_TAG_CUPS_CONST))
1254   {
1255     for (i = 0; i < num_values; i ++)
1256       if (strcmp(values[i], ipp_get_code(values[i], code, sizeof(code))))
1257       {
1258 	value_tag = temp_tag;		/* Don't do a fast copy */
1259         break;
1260       }
1261   }
1262   else if (values && value_tag == (ipp_tag_t)(IPP_TAG_LANGUAGE | IPP_TAG_CUPS_CONST))
1263   {
1264     for (i = 0; i < num_values; i ++)
1265       if (strcmp(values[i], ipp_lang_code(values[i], code, sizeof(code))))
1266       {
1267 	value_tag = temp_tag;		/* Don't do a fast copy */
1268         break;
1269       }
1270   }
1271 
1272  /*
1273   * Create the attribute...
1274   */
1275 
1276   if ((attr = ipp_add_attr(ipp, name, group, value_tag, num_values)) == NULL)
1277     return (NULL);
1278 
1279  /*
1280   * Initialize the attribute data...
1281   */
1282 
1283   for (i = num_values, value = attr->values;
1284        i > 0;
1285        i --, value ++)
1286   {
1287     if (language)
1288     {
1289       if (value == attr->values)
1290       {
1291         if ((int)value_tag & IPP_TAG_CUPS_CONST)
1292           value->string.language = (char *)language;
1293         else
1294           value->string.language = _cupsStrAlloc(ipp_lang_code(language, code,
1295                                                                sizeof(code)));
1296       }
1297       else
1298 	value->string.language = attr->values[0].string.language;
1299     }
1300 
1301     if (values)
1302     {
1303       if ((int)value_tag & IPP_TAG_CUPS_CONST)
1304         value->string.text = (char *)*values++;
1305       else if (value_tag == IPP_TAG_CHARSET)
1306 	value->string.text = _cupsStrAlloc(ipp_get_code(*values++, code, sizeof(code)));
1307       else if (value_tag == IPP_TAG_LANGUAGE)
1308 	value->string.text = _cupsStrAlloc(ipp_lang_code(*values++, code, sizeof(code)));
1309       else
1310 	value->string.text = _cupsStrAlloc(*values++);
1311     }
1312   }
1313 
1314   return (attr);
1315 }
1316 
1317 
1318 /*
1319  * 'ippContainsInteger()' - Determine whether an attribute contains the
1320  *                          specified value or is within the list of ranges.
1321  *
1322  * Returns non-zero when the attribute contains either a matching integer or
1323  * enum value, or the value falls within one of the rangeOfInteger values for
1324  * the attribute.
1325  *
1326  * @since CUPS 1.7/macOS 10.9@
1327  */
1328 
1329 int					/* O - 1 on a match, 0 on no match */
ippContainsInteger(ipp_attribute_t * attr,int value)1330 ippContainsInteger(
1331     ipp_attribute_t *attr,		/* I - Attribute */
1332     int             value)		/* I - Integer/enum value */
1333 {
1334   int		i;			/* Looping var */
1335   _ipp_value_t	*avalue;		/* Current attribute value */
1336 
1337 
1338  /*
1339   * Range check input...
1340   */
1341 
1342   if (!attr)
1343     return (0);
1344 
1345   if (attr->value_tag != IPP_TAG_INTEGER && attr->value_tag != IPP_TAG_ENUM &&
1346       attr->value_tag != IPP_TAG_RANGE)
1347     return (0);
1348 
1349  /*
1350   * Compare...
1351   */
1352 
1353   if (attr->value_tag == IPP_TAG_RANGE)
1354   {
1355     for (i = attr->num_values, avalue = attr->values; i > 0; i --, avalue ++)
1356       if (value >= avalue->range.lower && value <= avalue->range.upper)
1357         return (1);
1358   }
1359   else
1360   {
1361     for (i = attr->num_values, avalue = attr->values; i > 0; i --, avalue ++)
1362       if (value == avalue->integer)
1363         return (1);
1364   }
1365 
1366   return (0);
1367 }
1368 
1369 
1370 /*
1371  * 'ippContainsString()' - Determine whether an attribute contains the
1372  *                         specified string value.
1373  *
1374  * Returns non-zero when the attribute contains a matching charset, keyword,
1375  * naturalLanguage, mimeMediaType, name, text, uri, or uriScheme value.
1376  *
1377  * @since CUPS 1.7/macOS 10.9@
1378  */
1379 
1380 int					/* O - 1 on a match, 0 on no match */
ippContainsString(ipp_attribute_t * attr,const char * value)1381 ippContainsString(
1382     ipp_attribute_t *attr,		/* I - Attribute */
1383     const char      *value)		/* I - String value */
1384 {
1385   int		i;			/* Looping var */
1386   _ipp_value_t	*avalue;		/* Current attribute value */
1387 
1388 
1389   DEBUG_printf(("ippContainsString(attr=%p, value=\"%s\")", (void *)attr, value));
1390 
1391  /*
1392   * Range check input...
1393   */
1394 
1395   if (!attr || !value)
1396   {
1397     DEBUG_puts("1ippContainsString: Returning 0 (bad input)");
1398     return (0);
1399   }
1400 
1401  /*
1402   * Compare...
1403   */
1404 
1405   DEBUG_printf(("1ippContainsString: attr %s, %s with %d values.",
1406 		attr->name, ippTagString(attr->value_tag),
1407 		attr->num_values));
1408 
1409   switch (attr->value_tag & IPP_TAG_CUPS_MASK)
1410   {
1411     case IPP_TAG_CHARSET :
1412     case IPP_TAG_KEYWORD :
1413     case IPP_TAG_LANGUAGE :
1414     case IPP_TAG_URI :
1415     case IPP_TAG_URISCHEME :
1416 	for (i = attr->num_values, avalue = attr->values;
1417 	     i > 0;
1418 	     i --, avalue ++)
1419 	{
1420 	  DEBUG_printf(("1ippContainsString: value[%d]=\"%s\"",
1421 	                attr->num_values - i, avalue->string.text));
1422 
1423 	  if (!strcmp(value, avalue->string.text))
1424 	  {
1425 	    DEBUG_puts("1ippContainsString: Returning 1 (match)");
1426 	    return (1);
1427 	  }
1428         }
1429 
1430     case IPP_TAG_MIMETYPE :
1431     case IPP_TAG_NAME :
1432     case IPP_TAG_NAMELANG :
1433     case IPP_TAG_TEXT :
1434     case IPP_TAG_TEXTLANG :
1435 	for (i = attr->num_values, avalue = attr->values;
1436 	     i > 0;
1437 	     i --, avalue ++)
1438 	{
1439 	  DEBUG_printf(("1ippContainsString: value[%d]=\"%s\"",
1440 	                attr->num_values - i, avalue->string.text));
1441 
1442 	  if (!_cups_strcasecmp(value, avalue->string.text))
1443 	  {
1444 	    DEBUG_puts("1ippContainsString: Returning 1 (match)");
1445 	    return (1);
1446 	  }
1447         }
1448 
1449     default :
1450         break;
1451   }
1452 
1453   DEBUG_puts("1ippContainsString: Returning 0 (no match)");
1454 
1455   return (0);
1456 }
1457 
1458 
1459 /*
1460  * 'ippCopyAttribute()' - Copy an attribute.
1461  *
1462  * The specified attribute, @code attr@, is copied to the destination IPP message.
1463  * When @code quickcopy@ is non-zero, a "shallow" reference copy of the attribute is
1464  * created - this should only be done as long as the original source IPP message will
1465  * not be freed for the life of the destination.
1466  *
1467  * @since CUPS 1.6/macOS 10.8@
1468  */
1469 
1470 
1471 ipp_attribute_t *			/* O - New attribute */
ippCopyAttribute(ipp_t * dst,ipp_attribute_t * srcattr,int quickcopy)1472 ippCopyAttribute(
1473     ipp_t           *dst,		/* I - Destination IPP message */
1474     ipp_attribute_t *srcattr,		/* I - Attribute to copy */
1475     int             quickcopy)		/* I - 1 for a referenced copy, 0 for normal */
1476 {
1477   int			i;		/* Looping var */
1478   ipp_tag_t		srctag;		/* Source value tag */
1479   ipp_attribute_t	*dstattr;	/* Destination attribute */
1480   _ipp_value_t		*srcval,	/* Source value */
1481 			*dstval;	/* Destination value */
1482 
1483 
1484   DEBUG_printf(("ippCopyAttribute(dst=%p, srcattr=%p, quickcopy=%d)", (void *)dst, (void *)srcattr, quickcopy));
1485 
1486  /*
1487   * Range check input...
1488   */
1489 
1490   if (!dst || !srcattr)
1491     return (NULL);
1492 
1493  /*
1494   * Copy it...
1495   */
1496 
1497   quickcopy = (quickcopy && (srcattr->value_tag & IPP_TAG_CUPS_CONST)) ? IPP_TAG_CUPS_CONST : 0;
1498   srctag    = srcattr->value_tag & IPP_TAG_CUPS_MASK;
1499 
1500   switch (srctag)
1501   {
1502     case IPP_TAG_ZERO :
1503         dstattr = ippAddSeparator(dst);
1504 	break;
1505 
1506     case IPP_TAG_UNSUPPORTED_VALUE :
1507     case IPP_TAG_DEFAULT :
1508     case IPP_TAG_UNKNOWN :
1509     case IPP_TAG_NOVALUE :
1510     case IPP_TAG_NOTSETTABLE :
1511     case IPP_TAG_DELETEATTR :
1512     case IPP_TAG_ADMINDEFINE :
1513         dstattr = ippAddOutOfBand(dst, srcattr->group_tag, srctag, srcattr->name);
1514         break;
1515 
1516     case IPP_TAG_INTEGER :
1517     case IPP_TAG_ENUM :
1518     case IPP_TAG_BOOLEAN :
1519     case IPP_TAG_DATE :
1520     case IPP_TAG_RESOLUTION :
1521     case IPP_TAG_RANGE :
1522         if ((dstattr = ipp_add_attr(dst, srcattr->name, srcattr->group_tag, srctag, srcattr->num_values)) != NULL)
1523 	  memcpy(dstattr->values, srcattr->values, (size_t)srcattr->num_values * sizeof(_ipp_value_t));
1524         break;
1525 
1526     case IPP_TAG_TEXT :
1527     case IPP_TAG_NAME :
1528     case IPP_TAG_RESERVED_STRING :
1529     case IPP_TAG_KEYWORD :
1530     case IPP_TAG_URI :
1531     case IPP_TAG_URISCHEME :
1532     case IPP_TAG_CHARSET :
1533     case IPP_TAG_LANGUAGE :
1534     case IPP_TAG_MIMETYPE :
1535         if ((dstattr = ippAddStrings(dst, srcattr->group_tag, (ipp_tag_t)(srctag | quickcopy), srcattr->name, srcattr->num_values, NULL, NULL)) == NULL)
1536           break;
1537 
1538         if (quickcopy)
1539 	{
1540 	 /*
1541 	  * Can safely quick-copy these string values...
1542 	  */
1543 
1544 	  memcpy(dstattr->values, srcattr->values, (size_t)srcattr->num_values * sizeof(_ipp_value_t));
1545         }
1546 	else
1547 	{
1548 	 /*
1549 	  * Otherwise do a normal reference counted copy...
1550 	  */
1551 
1552 	  for (i = srcattr->num_values, srcval = srcattr->values, dstval = dstattr->values; i > 0; i --, srcval ++, dstval ++)
1553 	    dstval->string.text = _cupsStrAlloc(srcval->string.text);
1554 	}
1555         break;
1556 
1557     case IPP_TAG_TEXTLANG :
1558     case IPP_TAG_NAMELANG :
1559         if ((dstattr = ippAddStrings(dst, srcattr->group_tag, (ipp_tag_t)(srctag | quickcopy), srcattr->name, srcattr->num_values, NULL, NULL)) == NULL)
1560           break;
1561 
1562         if (quickcopy)
1563 	{
1564 	 /*
1565 	  * Can safely quick-copy these string values...
1566 	  */
1567 
1568 	  memcpy(dstattr->values, srcattr->values, (size_t)srcattr->num_values * sizeof(_ipp_value_t));
1569         }
1570 	else if (srcattr->value_tag & IPP_TAG_CUPS_CONST)
1571 	{
1572 	 /*
1573 	  * Otherwise do a normal reference counted copy...
1574 	  */
1575 
1576 	  for (i = srcattr->num_values, srcval = srcattr->values, dstval = dstattr->values; i > 0; i --, srcval ++, dstval ++)
1577 	  {
1578 	    if (srcval == srcattr->values)
1579               dstval->string.language = _cupsStrAlloc(srcval->string.language);
1580 	    else
1581               dstval->string.language = dstattr->values[0].string.language;
1582 
1583 	    dstval->string.text = _cupsStrAlloc(srcval->string.text);
1584           }
1585         }
1586         break;
1587 
1588     case IPP_TAG_BEGIN_COLLECTION :
1589         if ((dstattr = ippAddCollections(dst, srcattr->group_tag, srcattr->name, srcattr->num_values, NULL)) == NULL)
1590           break;
1591 
1592         for (i = srcattr->num_values, srcval = srcattr->values, dstval = dstattr->values; i > 0; i --, srcval ++, dstval ++)
1593 	{
1594 	  dstval->collection = srcval->collection;
1595 	  srcval->collection->use ++;
1596 	}
1597         break;
1598 
1599     case IPP_TAG_STRING :
1600     default :
1601         if ((dstattr = ipp_add_attr(dst, srcattr->name, srcattr->group_tag, srctag, srcattr->num_values)) == NULL)
1602           break;
1603 
1604         for (i = srcattr->num_values, srcval = srcattr->values, dstval = dstattr->values; i > 0; i --, srcval ++, dstval ++)
1605 	{
1606 	  dstval->unknown.length = srcval->unknown.length;
1607 
1608 	  if (dstval->unknown.length > 0)
1609 	  {
1610 	    if ((dstval->unknown.data = malloc((size_t)dstval->unknown.length)) == NULL)
1611 	      dstval->unknown.length = 0;
1612 	    else
1613 	      memcpy(dstval->unknown.data, srcval->unknown.data, (size_t)dstval->unknown.length);
1614 	  }
1615 	}
1616         break; /* anti-compiler-warning-code */
1617   }
1618 
1619   return (dstattr);
1620 }
1621 
1622 
1623 /*
1624  * 'ippCopyAttributes()' - Copy attributes from one IPP message to another.
1625  *
1626  * Zero or more attributes are copied from the source IPP message, @code src@, to the
1627  * destination IPP message, @code dst@. When @code quickcopy@ is non-zero, a "shallow"
1628  * reference copy of the attribute is created - this should only be done as long as the
1629  * original source IPP message will not be freed for the life of the destination.
1630  *
1631  * The @code cb@ and @code context@ parameters provide a generic way to "filter" the
1632  * attributes that are copied - the function must return 1 to copy the attribute or
1633  * 0 to skip it. The function may also choose to do a partial copy of the source attribute
1634  * itself.
1635  *
1636  * @since CUPS 1.6/macOS 10.8@
1637  */
1638 
1639 int					/* O - 1 on success, 0 on error */
ippCopyAttributes(ipp_t * dst,ipp_t * src,int quickcopy,ipp_copycb_t cb,void * context)1640 ippCopyAttributes(
1641     ipp_t        *dst,			/* I - Destination IPP message */
1642     ipp_t        *src,			/* I - Source IPP message */
1643     int          quickcopy,		/* I - 1 for a referenced copy, 0 for normal */
1644     ipp_copycb_t cb,			/* I - Copy callback or @code NULL@ for none */
1645     void         *context)		/* I - Context pointer */
1646 {
1647   ipp_attribute_t	*srcattr;	/* Source attribute */
1648 
1649 
1650   DEBUG_printf(("ippCopyAttributes(dst=%p, src=%p, quickcopy=%d, cb=%p, context=%p)", (void *)dst, (void *)src, quickcopy, (void *)cb, context));
1651 
1652  /*
1653   * Range check input...
1654   */
1655 
1656   if (!dst || !src)
1657     return (0);
1658 
1659  /*
1660   * Loop through source attributes and copy as needed...
1661   */
1662 
1663   for (srcattr = src->attrs; srcattr; srcattr = srcattr->next)
1664     if (!cb || (*cb)(context, dst, srcattr))
1665       if (!ippCopyAttribute(dst, srcattr, quickcopy))
1666         return (0);
1667 
1668   return (1);
1669 }
1670 
1671 
1672 /*
1673  * 'ippDateToTime()' - Convert from RFC 2579 Date/Time format to time in
1674  *                     seconds.
1675  */
1676 
1677 time_t					/* O - UNIX time value */
ippDateToTime(const ipp_uchar_t * date)1678 ippDateToTime(const ipp_uchar_t *date)	/* I - RFC 2579 date info */
1679 {
1680   struct tm	unixdate;		/* UNIX date/time info */
1681   time_t	t;			/* Computed time */
1682 
1683 
1684   if (!date)
1685     return (0);
1686 
1687   memset(&unixdate, 0, sizeof(unixdate));
1688 
1689  /*
1690   * RFC-2579 date/time format is:
1691   *
1692   *    Byte(s)  Description
1693   *    -------  -----------
1694   *    0-1      Year (0 to 65535)
1695   *    2        Month (1 to 12)
1696   *    3        Day (1 to 31)
1697   *    4        Hours (0 to 23)
1698   *    5        Minutes (0 to 59)
1699   *    6        Seconds (0 to 60, 60 = "leap second")
1700   *    7        Deciseconds (0 to 9)
1701   *    8        +/- UTC
1702   *    9        UTC hours (0 to 14)
1703   *    10       UTC minutes (0 to 59)
1704   */
1705 
1706   unixdate.tm_year = ((date[0] << 8) | date[1]) - 1900;
1707   unixdate.tm_mon  = date[2] - 1;
1708   unixdate.tm_mday = date[3];
1709   unixdate.tm_hour = date[4];
1710   unixdate.tm_min  = date[5];
1711   unixdate.tm_sec  = date[6];
1712 
1713 #if _WIN32
1714   if ((t = _mkgmtime(&unixdate)) < 0)
1715     return (0);
1716 
1717 #elif defined(HAVE_TIMEGM)
1718   if ((t = timegm(&unixdate)) < 0)
1719     return (0);
1720 
1721 #else
1722   if ((t = mktime(&unixdate)) < 0)
1723     return (0);
1724 
1725 #  if defined(HAVE_TM_GMTOFF)
1726  /*
1727   * Adjust the time value using the "tm_gmtoff" and "tm_isdst" members.  As
1728   * noted by M-HT on Github, this DST hack will fail in timezones where the
1729   * DST offset is not one hour, such as Australia/Lord_Howe.  Fortunately,
1730   * this is unusual and most systems support the "timegm" function...
1731   */
1732 
1733   t += unixdate.tm_gmtoff - 3600 * unixdate.tm_isdst;
1734 #  else
1735  /*
1736   * Adjust the time value using the even more legacy "timezone" variable,
1737   * which also reflects any DST offset...
1738   */
1739 
1740   t += timezone;
1741 #  endif // HAVE_TM_GMTOFF
1742 #endif // _WIN32
1743 
1744  /*
1745   * Subtract the UTC timezone offset to get the actual time_t value...
1746   */
1747 
1748   if (date[8] == '-')
1749     t += date[9] * 3600 + date[10] * 60;/* "t - -offset" is "t + offset" */
1750   else
1751     t -= date[9] * 3600 + date[10] * 60;/* "t - offset" */
1752 
1753   return (t);
1754 }
1755 
1756 
1757 /*
1758  * 'ippDelete()' - Delete an IPP message.
1759  */
1760 
1761 void
ippDelete(ipp_t * ipp)1762 ippDelete(ipp_t *ipp)			/* I - IPP message */
1763 {
1764   ipp_attribute_t	*attr,		/* Current attribute */
1765 			*next;		/* Next attribute */
1766 
1767 
1768   DEBUG_printf(("ippDelete(ipp=%p)", (void *)ipp));
1769 
1770   if (!ipp)
1771     return;
1772 
1773   ipp->use --;
1774   if (ipp->use > 0)
1775   {
1776     DEBUG_printf(("4debug_retain: %p IPP message (use=%d)", (void *)ipp, ipp->use));
1777     return;
1778   }
1779 
1780   DEBUG_printf(("4debug_free: %p IPP message", (void *)ipp));
1781 
1782   for (attr = ipp->attrs; attr != NULL; attr = next)
1783   {
1784     next = attr->next;
1785 
1786     DEBUG_printf(("4debug_free: %p %s %s%s (%d values)", (void *)attr, attr->name, attr->num_values > 1 ? "1setOf " : "", ippTagString(attr->value_tag), attr->num_values));
1787 
1788     ipp_free_values(attr, 0, attr->num_values);
1789 
1790     if (attr->name)
1791       _cupsStrFree(attr->name);
1792 
1793     free(attr);
1794   }
1795 
1796   free(ipp);
1797 }
1798 
1799 
1800 /*
1801  * 'ippDeleteAttribute()' - Delete a single attribute in an IPP message.
1802  *
1803  * @since CUPS 1.1.19/macOS 10.3@
1804  */
1805 
1806 void
ippDeleteAttribute(ipp_t * ipp,ipp_attribute_t * attr)1807 ippDeleteAttribute(
1808     ipp_t           *ipp,		/* I - IPP message */
1809     ipp_attribute_t *attr)		/* I - Attribute to delete */
1810 {
1811   ipp_attribute_t	*current,	/* Current attribute */
1812 			*prev;		/* Previous attribute */
1813 
1814 
1815   DEBUG_printf(("ippDeleteAttribute(ipp=%p, attr=%p(%s))", (void *)ipp, (void *)attr, attr ? attr->name : "(null)"));
1816 
1817  /*
1818   * Range check input...
1819   */
1820 
1821   if (!attr)
1822     return;
1823 
1824   DEBUG_printf(("4debug_free: %p %s %s%s (%d values)", (void *)attr, attr->name, attr->num_values > 1 ? "1setOf " : "", ippTagString(attr->value_tag), attr->num_values));
1825 
1826  /*
1827   * Find the attribute in the list...
1828   */
1829 
1830   if (ipp)
1831   {
1832     for (current = ipp->attrs, prev = NULL;
1833 	 current;
1834 	 prev = current, current = current->next)
1835       if (current == attr)
1836       {
1837        /*
1838 	* Found it, remove the attribute from the list...
1839 	*/
1840 
1841 	if (prev)
1842 	  prev->next = current->next;
1843 	else
1844 	  ipp->attrs = current->next;
1845 
1846 	if (current == ipp->last)
1847 	  ipp->last = prev;
1848 
1849         break;
1850       }
1851 
1852     if (!current)
1853       return;
1854   }
1855 
1856  /*
1857   * Free memory used by the attribute...
1858   */
1859 
1860   ipp_free_values(attr, 0, attr->num_values);
1861 
1862   if (attr->name)
1863     _cupsStrFree(attr->name);
1864 
1865   free(attr);
1866 }
1867 
1868 
1869 /*
1870  * 'ippDeleteValues()' - Delete values in an attribute.
1871  *
1872  * The @code element@ parameter specifies the first value to delete, starting at
1873  * 0. It must be less than the number of values returned by @link ippGetCount@.
1874  *
1875  * The @code attr@ parameter may be modified as a result of setting the value.
1876  *
1877  * Deleting all values in an attribute deletes the attribute.
1878  *
1879  * @since CUPS 1.6/macOS 10.8@
1880  */
1881 
1882 int					/* O  - 1 on success, 0 on failure */
ippDeleteValues(ipp_t * ipp,ipp_attribute_t ** attr,int element,int count)1883 ippDeleteValues(
1884     ipp_t           *ipp,		/* I  - IPP message */
1885     ipp_attribute_t **attr,		/* IO - Attribute */
1886     int             element,		/* I  - Index of first value to delete (0-based) */
1887     int             count)		/* I  - Number of values to delete */
1888 {
1889  /*
1890   * Range check input...
1891   */
1892 
1893   if (!ipp || !attr || !*attr ||
1894       element < 0 || element >= (*attr)->num_values || count <= 0 ||
1895       (element + count) > (*attr)->num_values)
1896     return (0);
1897 
1898  /*
1899   * If we are deleting all values, just delete the attribute entirely.
1900   */
1901 
1902   if (count == (*attr)->num_values)
1903   {
1904     ippDeleteAttribute(ipp, *attr);
1905     *attr = NULL;
1906     return (1);
1907   }
1908 
1909  /*
1910   * Otherwise free the values in question and return.
1911   */
1912 
1913   ipp_free_values(*attr, element, count);
1914 
1915   return (1);
1916 }
1917 
1918 
1919 /*
1920  * 'ippFindAttribute()' - Find a named attribute in a request.
1921  *
1922  * Starting with CUPS 2.0, the attribute name can contain a hierarchical list
1923  * of attribute and member names separated by slashes, for example
1924  * "media-col/media-size".
1925  */
1926 
1927 ipp_attribute_t	*			/* O - Matching attribute */
ippFindAttribute(ipp_t * ipp,const char * name,ipp_tag_t type)1928 ippFindAttribute(ipp_t      *ipp,	/* I - IPP message */
1929                  const char *name,	/* I - Name of attribute */
1930 		 ipp_tag_t  type)	/* I - Type of attribute */
1931 {
1932   DEBUG_printf(("2ippFindAttribute(ipp=%p, name=\"%s\", type=%02x(%s))", (void *)ipp, name, type, ippTagString(type)));
1933 
1934   if (!ipp || !name)
1935     return (NULL);
1936 
1937  /*
1938   * Reset the current pointer...
1939   */
1940 
1941   ipp->current = NULL;
1942   ipp->atend   = 0;
1943 
1944  /*
1945   * Search for the attribute...
1946   */
1947 
1948   return (ippFindNextAttribute(ipp, name, type));
1949 }
1950 
1951 
1952 /*
1953  * 'ippFindNextAttribute()' - Find the next named attribute in a request.
1954  *
1955  * Starting with CUPS 2.0, the attribute name can contain a hierarchical list
1956  * of attribute and member names separated by slashes, for example
1957  * "media-col/media-size".
1958  */
1959 
1960 ipp_attribute_t	*			/* O - Matching attribute */
ippFindNextAttribute(ipp_t * ipp,const char * name,ipp_tag_t type)1961 ippFindNextAttribute(ipp_t      *ipp,	/* I - IPP message */
1962                      const char *name,	/* I - Name of attribute */
1963 		     ipp_tag_t  type)	/* I - Type of attribute */
1964 {
1965   ipp_attribute_t	*attr,		/* Current attribute */
1966 			*childattr;	/* Child attribute */
1967   ipp_tag_t		value_tag;	/* Value tag */
1968   char			parent[1024],	/* Parent attribute name */
1969 			*child = NULL;	/* Child attribute name */
1970 
1971 
1972   DEBUG_printf(("2ippFindNextAttribute(ipp=%p, name=\"%s\", type=%02x(%s))", (void *)ipp, name, type, ippTagString(type)));
1973 
1974   if (!ipp || !name)
1975     return (NULL);
1976 
1977   DEBUG_printf(("3ippFindNextAttribute: atend=%d", ipp->atend));
1978 
1979   if (ipp->atend)
1980     return (NULL);
1981 
1982   if (strchr(name, '/'))
1983   {
1984    /*
1985     * Search for child attribute...
1986     */
1987 
1988     strlcpy(parent, name, sizeof(parent));
1989     if ((child = strchr(parent, '/')) == NULL)
1990     {
1991       DEBUG_puts("3ippFindNextAttribute: Attribute name too long.");
1992       return (NULL);
1993     }
1994 
1995     *child++ = '\0';
1996 
1997     if (ipp->current && ipp->current->name && ipp->current->value_tag == IPP_TAG_BEGIN_COLLECTION && !strcmp(parent, ipp->current->name))
1998     {
1999       while (ipp->curindex < ipp->current->num_values)
2000       {
2001         if ((childattr = ippFindNextAttribute(ipp->current->values[ipp->curindex].collection, child, type)) != NULL)
2002           return (childattr);
2003 
2004         ipp->curindex ++;
2005         if (ipp->curindex < ipp->current->num_values && ipp->current->values[ipp->curindex].collection)
2006           ipp->current->values[ipp->curindex].collection->current = NULL;
2007       }
2008 
2009       ipp->prev     = ipp->current;
2010       ipp->current  = ipp->current->next;
2011       ipp->curindex = 0;
2012 
2013       if (!ipp->current)
2014       {
2015         ipp->atend = 1;
2016         return (NULL);
2017       }
2018     }
2019 
2020     if (!ipp->current)
2021     {
2022       ipp->prev     = NULL;
2023       ipp->current  = ipp->attrs;
2024       ipp->curindex = 0;
2025     }
2026 
2027     name = parent;
2028     attr = ipp->current;
2029   }
2030   else if (ipp->current)
2031   {
2032     ipp->prev = ipp->current;
2033     attr      = ipp->current->next;
2034   }
2035   else
2036   {
2037     ipp->prev = NULL;
2038     attr      = ipp->attrs;
2039   }
2040 
2041   for (; attr != NULL; ipp->prev = attr, attr = attr->next)
2042   {
2043     DEBUG_printf(("4ippFindAttribute: attr=%p, name=\"%s\"", (void *)attr, attr->name));
2044 
2045     value_tag = (ipp_tag_t)(attr->value_tag & IPP_TAG_CUPS_MASK);
2046 
2047     if (attr->name != NULL && strcmp(attr->name, name) == 0 &&
2048         (value_tag == type || type == IPP_TAG_ZERO || name == parent ||
2049 	 (value_tag == IPP_TAG_TEXTLANG && type == IPP_TAG_TEXT) ||
2050 	 (value_tag == IPP_TAG_NAMELANG && type == IPP_TAG_NAME)))
2051     {
2052       ipp->current = attr;
2053 
2054       if (name == parent && attr->value_tag == IPP_TAG_BEGIN_COLLECTION)
2055       {
2056         int i;				/* Looping var */
2057 
2058         for (i = 0; i < attr->num_values; i ++)
2059         {
2060 	  if ((childattr = ippFindAttribute(attr->values[i].collection, child, type)) != NULL)
2061 	  {
2062 	    attr->values[0].collection->curindex = i;
2063 	    return (childattr);
2064 	  }
2065         }
2066       }
2067       else
2068         return (attr);
2069     }
2070   }
2071 
2072   ipp->current = NULL;
2073   ipp->prev    = NULL;
2074   ipp->atend   = 1;
2075 
2076   return (NULL);
2077 }
2078 
2079 
2080 /*
2081  * 'ippFirstAttribute()' - Return the first attribute in the message.
2082  *
2083  * @since CUPS 1.6/macOS 10.8@
2084  */
2085 
2086 ipp_attribute_t	*			/* O - First attribute or @code NULL@ if none */
ippFirstAttribute(ipp_t * ipp)2087 ippFirstAttribute(ipp_t *ipp)		/* I - IPP message */
2088 {
2089  /*
2090   * Range check input...
2091   */
2092 
2093   if (!ipp)
2094     return (NULL);
2095 
2096  /*
2097   * Return the first attribute...
2098   */
2099 
2100   return (ipp->current = ipp->attrs);
2101 }
2102 
2103 
2104 /*
2105  * 'ippGetBoolean()' - Get a boolean value for an attribute.
2106  *
2107  * The @code element@ parameter specifies which value to get from 0 to
2108  * @code ippGetCount(attr)@ - 1.
2109  *
2110  * @since CUPS 1.6/macOS 10.8@
2111  */
2112 
2113 int					/* O - Boolean value or 0 on error */
ippGetBoolean(ipp_attribute_t * attr,int element)2114 ippGetBoolean(ipp_attribute_t *attr,	/* I - IPP attribute */
2115               int             element)	/* I - Value number (0-based) */
2116 {
2117  /*
2118   * Range check input...
2119   */
2120 
2121   if (!attr || attr->value_tag != IPP_TAG_BOOLEAN ||
2122       element < 0 || element >= attr->num_values)
2123     return (0);
2124 
2125  /*
2126   * Return the value...
2127   */
2128 
2129   return (attr->values[element].boolean);
2130 }
2131 
2132 
2133 /*
2134  * 'ippGetCollection()' - Get a collection value for an attribute.
2135  *
2136  * The @code element@ parameter specifies which value to get from 0 to
2137  * @code ippGetCount(attr)@ - 1.
2138  *
2139  * @since CUPS 1.6/macOS 10.8@
2140  */
2141 
2142 ipp_t *					/* O - Collection value or @code NULL@ on error */
ippGetCollection(ipp_attribute_t * attr,int element)2143 ippGetCollection(
2144     ipp_attribute_t *attr,		/* I - IPP attribute */
2145     int             element)		/* I - Value number (0-based) */
2146 {
2147  /*
2148   * Range check input...
2149   */
2150 
2151   if (!attr || attr->value_tag != IPP_TAG_BEGIN_COLLECTION ||
2152       element < 0 || element >= attr->num_values)
2153     return (NULL);
2154 
2155  /*
2156   * Return the value...
2157   */
2158 
2159   return (attr->values[element].collection);
2160 }
2161 
2162 
2163 /*
2164  * 'ippGetCount()' - Get the number of values in an attribute.
2165  *
2166  * @since CUPS 1.6/macOS 10.8@
2167  */
2168 
2169 int					/* O - Number of values or 0 on error */
ippGetCount(ipp_attribute_t * attr)2170 ippGetCount(ipp_attribute_t *attr)	/* I - IPP attribute */
2171 {
2172  /*
2173   * Range check input...
2174   */
2175 
2176   if (!attr)
2177     return (0);
2178 
2179  /*
2180   * Return the number of values...
2181   */
2182 
2183   return (attr->num_values);
2184 }
2185 
2186 
2187 /*
2188  * 'ippGetDate()' - Get a dateTime value for an attribute.
2189  *
2190  * The @code element@ parameter specifies which value to get from 0 to
2191  * @code ippGetCount(attr)@ - 1.
2192  *
2193  * @since CUPS 1.6/macOS 10.8@
2194  */
2195 
2196 const ipp_uchar_t *			/* O - dateTime value or @code NULL@ */
ippGetDate(ipp_attribute_t * attr,int element)2197 ippGetDate(ipp_attribute_t *attr,	/* I - IPP attribute */
2198            int             element)	/* I - Value number (0-based) */
2199 {
2200  /*
2201   * Range check input...
2202   */
2203 
2204   if (!attr || attr->value_tag != IPP_TAG_DATE ||
2205       element < 0 || element >= attr->num_values)
2206     return (NULL);
2207 
2208  /*
2209   * Return the value...
2210   */
2211 
2212   return (attr->values[element].date);
2213 }
2214 
2215 
2216 /*
2217  * 'ippGetGroupTag()' - Get the group associated with an attribute.
2218  *
2219  * @since CUPS 1.6/macOS 10.8@
2220  */
2221 
2222 ipp_tag_t				/* O - Group tag or @code IPP_TAG_ZERO@ on error */
ippGetGroupTag(ipp_attribute_t * attr)2223 ippGetGroupTag(ipp_attribute_t *attr)	/* I - IPP attribute */
2224 {
2225  /*
2226   * Range check input...
2227   */
2228 
2229   if (!attr)
2230     return (IPP_TAG_ZERO);
2231 
2232  /*
2233   * Return the group...
2234   */
2235 
2236   return (attr->group_tag);
2237 }
2238 
2239 
2240 /*
2241  * 'ippGetInteger()' - Get the integer/enum value for an attribute.
2242  *
2243  * The @code element@ parameter specifies which value to get from 0 to
2244  * @code ippGetCount(attr)@ - 1.
2245  *
2246  * @since CUPS 1.6/macOS 10.8@
2247  */
2248 
2249 int					/* O - Value or 0 on error */
ippGetInteger(ipp_attribute_t * attr,int element)2250 ippGetInteger(ipp_attribute_t *attr,	/* I - IPP attribute */
2251               int             element)	/* I - Value number (0-based) */
2252 {
2253  /*
2254   * Range check input...
2255   */
2256 
2257   if (!attr || (attr->value_tag != IPP_TAG_INTEGER && attr->value_tag != IPP_TAG_ENUM) ||
2258       element < 0 || element >= attr->num_values)
2259     return (0);
2260 
2261  /*
2262   * Return the value...
2263   */
2264 
2265   return (attr->values[element].integer);
2266 }
2267 
2268 
2269 /*
2270  * 'ippGetName()' - Get the attribute name.
2271  *
2272  * @since CUPS 1.6/macOS 10.8@
2273  */
2274 
2275 const char *				/* O - Attribute name or @code NULL@ for separators */
ippGetName(ipp_attribute_t * attr)2276 ippGetName(ipp_attribute_t *attr)	/* I - IPP attribute */
2277 {
2278  /*
2279   * Range check input...
2280   */
2281 
2282   if (!attr)
2283     return (NULL);
2284 
2285  /*
2286   * Return the name...
2287   */
2288 
2289   return (attr->name);
2290 }
2291 
2292 
2293 /*
2294  * 'ippGetOctetString()' - Get an octetString value from an IPP attribute.
2295  *
2296  * The @code element@ parameter specifies which value to get from 0 to
2297  * @code ippGetCount(attr)@ - 1.
2298  *
2299  * @since CUPS 1.7/macOS 10.9@
2300  */
2301 
2302 void *					/* O - Pointer to octetString data */
ippGetOctetString(ipp_attribute_t * attr,int element,int * datalen)2303 ippGetOctetString(
2304     ipp_attribute_t *attr,		/* I - IPP attribute */
2305     int             element,		/* I - Value number (0-based) */
2306     int             *datalen)		/* O - Length of octetString data */
2307 {
2308  /*
2309   * Range check input...
2310   */
2311 
2312   if (!attr || attr->value_tag != IPP_TAG_STRING ||
2313       element < 0 || element >= attr->num_values)
2314   {
2315     if (datalen)
2316       *datalen = 0;
2317 
2318     return (NULL);
2319   }
2320 
2321  /*
2322   * Return the values...
2323   */
2324 
2325   if (datalen)
2326     *datalen = attr->values[element].unknown.length;
2327 
2328   return (attr->values[element].unknown.data);
2329 }
2330 
2331 
2332 /*
2333  * 'ippGetOperation()' - Get the operation ID in an IPP message.
2334  *
2335  * @since CUPS 1.6/macOS 10.8@
2336  */
2337 
2338 ipp_op_t				/* O - Operation ID or 0 on error */
ippGetOperation(ipp_t * ipp)2339 ippGetOperation(ipp_t *ipp)		/* I - IPP request message */
2340 {
2341  /*
2342   * Range check input...
2343   */
2344 
2345   if (!ipp)
2346     return ((ipp_op_t)0);
2347 
2348  /*
2349   * Return the value...
2350   */
2351 
2352   return (ipp->request.op.operation_id);
2353 }
2354 
2355 
2356 /*
2357  * 'ippGetRange()' - Get a rangeOfInteger value from an attribute.
2358  *
2359  * The @code element@ parameter specifies which value to get from 0 to
2360  * @code ippGetCount(attr)@ - 1.
2361  *
2362  * @since CUPS 1.6/macOS 10.8@
2363  */
2364 
2365 int					/* O - Lower value of range or 0 */
ippGetRange(ipp_attribute_t * attr,int element,int * uppervalue)2366 ippGetRange(ipp_attribute_t *attr,	/* I - IPP attribute */
2367 	    int             element,	/* I - Value number (0-based) */
2368 	    int             *uppervalue)/* O - Upper value of range */
2369 {
2370  /*
2371   * Range check input...
2372   */
2373 
2374   if (!attr || attr->value_tag != IPP_TAG_RANGE ||
2375       element < 0 || element >= attr->num_values)
2376   {
2377     if (uppervalue)
2378       *uppervalue = 0;
2379 
2380     return (0);
2381   }
2382 
2383  /*
2384   * Return the values...
2385   */
2386 
2387   if (uppervalue)
2388     *uppervalue = attr->values[element].range.upper;
2389 
2390   return (attr->values[element].range.lower);
2391 }
2392 
2393 
2394 /*
2395  * 'ippGetRequestId()' - Get the request ID from an IPP message.
2396  *
2397  * @since CUPS 1.6/macOS 10.8@
2398  */
2399 
2400 int					/* O - Request ID or 0 on error */
ippGetRequestId(ipp_t * ipp)2401 ippGetRequestId(ipp_t *ipp)		/* I - IPP message */
2402 {
2403  /*
2404   * Range check input...
2405   */
2406 
2407   if (!ipp)
2408     return (0);
2409 
2410  /*
2411   * Return the request ID...
2412   */
2413 
2414   return (ipp->request.any.request_id);
2415 }
2416 
2417 
2418 /*
2419  * 'ippGetResolution()' - Get a resolution value for an attribute.
2420  *
2421  * The @code element@ parameter specifies which value to get from 0 to
2422  * @code ippGetCount(attr)@ - 1.
2423  *
2424  * @since CUPS 1.6/macOS 10.8@
2425  */
2426 
2427 int					/* O - Horizontal/cross feed resolution or 0 */
ippGetResolution(ipp_attribute_t * attr,int element,int * yres,ipp_res_t * units)2428 ippGetResolution(
2429     ipp_attribute_t *attr,		/* I - IPP attribute */
2430     int             element,		/* I - Value number (0-based) */
2431     int             *yres,		/* O - Vertical/feed resolution */
2432     ipp_res_t       *units)		/* O - Units for resolution */
2433 {
2434  /*
2435   * Range check input...
2436   */
2437 
2438   if (!attr || attr->value_tag != IPP_TAG_RESOLUTION ||
2439       element < 0 || element >= attr->num_values)
2440   {
2441     if (yres)
2442       *yres = 0;
2443 
2444     if (units)
2445       *units = (ipp_res_t)0;
2446 
2447     return (0);
2448   }
2449 
2450  /*
2451   * Return the value...
2452   */
2453 
2454   if (yres)
2455     *yres = attr->values[element].resolution.yres;
2456 
2457   if (units)
2458     *units = attr->values[element].resolution.units;
2459 
2460   return (attr->values[element].resolution.xres);
2461 }
2462 
2463 
2464 /*
2465  * 'ippGetState()' - Get the IPP message state.
2466  *
2467  * @since CUPS 1.6/macOS 10.8@
2468  */
2469 
2470 ipp_state_t				/* O - IPP message state value */
ippGetState(ipp_t * ipp)2471 ippGetState(ipp_t *ipp)			/* I - IPP message */
2472 {
2473  /*
2474   * Range check input...
2475   */
2476 
2477   if (!ipp)
2478     return (IPP_STATE_IDLE);
2479 
2480  /*
2481   * Return the value...
2482   */
2483 
2484   return (ipp->state);
2485 }
2486 
2487 
2488 /*
2489  * 'ippGetStatusCode()' - Get the status code from an IPP response or event message.
2490  *
2491  * @since CUPS 1.6/macOS 10.8@
2492  */
2493 
2494 ipp_status_t				/* O - Status code in IPP message */
ippGetStatusCode(ipp_t * ipp)2495 ippGetStatusCode(ipp_t *ipp)		/* I - IPP response or event message */
2496 {
2497  /*
2498   * Range check input...
2499   */
2500 
2501   if (!ipp)
2502     return (IPP_STATUS_ERROR_INTERNAL);
2503 
2504  /*
2505   * Return the value...
2506   */
2507 
2508   return (ipp->request.status.status_code);
2509 }
2510 
2511 
2512 /*
2513  * 'ippGetString()' - Get the string and optionally the language code for an attribute.
2514  *
2515  * The @code element@ parameter specifies which value to get from 0 to
2516  * @code ippGetCount(attr)@ - 1.
2517  *
2518  * @since CUPS 1.6/macOS 10.8@
2519  */
2520 
2521 const char *
ippGetString(ipp_attribute_t * attr,int element,const char ** language)2522 ippGetString(ipp_attribute_t *attr,	/* I - IPP attribute */
2523              int             element,	/* I - Value number (0-based) */
2524 	     const char      **language)/* O - Language code (@code NULL@ for don't care) */
2525 {
2526   ipp_tag_t	tag;			/* Value tag */
2527 
2528 
2529  /*
2530   * Range check input...
2531   */
2532 
2533   tag = ippGetValueTag(attr);
2534 
2535   if (!attr || element < 0 || element >= attr->num_values || (tag != IPP_TAG_TEXTLANG && tag != IPP_TAG_NAMELANG && (tag < IPP_TAG_TEXT || tag > IPP_TAG_MIMETYPE)))
2536     return (NULL);
2537 
2538  /*
2539   * Return the value...
2540   */
2541 
2542   if (language)
2543     *language = attr->values[element].string.language;
2544 
2545   return (attr->values[element].string.text);
2546 }
2547 
2548 
2549 /*
2550  * 'ippGetValueTag()' - Get the value tag for an attribute.
2551  *
2552  * @since CUPS 1.6/macOS 10.8@
2553  */
2554 
2555 ipp_tag_t				/* O - Value tag or @code IPP_TAG_ZERO@ on error */
ippGetValueTag(ipp_attribute_t * attr)2556 ippGetValueTag(ipp_attribute_t *attr)	/* I - IPP attribute */
2557 {
2558  /*
2559   * Range check input...
2560   */
2561 
2562   if (!attr)
2563     return (IPP_TAG_ZERO);
2564 
2565  /*
2566   * Return the value...
2567   */
2568 
2569   return (attr->value_tag & IPP_TAG_CUPS_MASK);
2570 }
2571 
2572 
2573 /*
2574  * 'ippGetVersion()' - Get the major and minor version number from an IPP message.
2575  *
2576  * @since CUPS 1.6/macOS 10.8@
2577  */
2578 
2579 int					/* O - Major version number or 0 on error */
ippGetVersion(ipp_t * ipp,int * minor)2580 ippGetVersion(ipp_t *ipp,		/* I - IPP message */
2581               int   *minor)		/* O - Minor version number or @code NULL@ for don't care */
2582 {
2583  /*
2584   * Range check input...
2585   */
2586 
2587   if (!ipp)
2588   {
2589     if (minor)
2590       *minor = 0;
2591 
2592     return (0);
2593   }
2594 
2595  /*
2596   * Return the value...
2597   */
2598 
2599   if (minor)
2600     *minor = ipp->request.any.version[1];
2601 
2602   return (ipp->request.any.version[0]);
2603 }
2604 
2605 
2606 /*
2607  * 'ippLength()' - Compute the length of an IPP message.
2608  */
2609 
2610 size_t					/* O - Size of IPP message */
ippLength(ipp_t * ipp)2611 ippLength(ipp_t *ipp)			/* I - IPP message */
2612 {
2613   return (ipp_length(ipp, 0));
2614 }
2615 
2616 
2617 /*
2618  * 'ippNextAttribute()' - Return the next attribute in the message.
2619  *
2620  * @since CUPS 1.6/macOS 10.8@
2621  */
2622 
2623 ipp_attribute_t *			/* O - Next attribute or @code NULL@ if none */
ippNextAttribute(ipp_t * ipp)2624 ippNextAttribute(ipp_t *ipp)		/* I - IPP message */
2625 {
2626  /*
2627   * Range check input...
2628   */
2629 
2630   if (!ipp || !ipp->current)
2631     return (NULL);
2632 
2633  /*
2634   * Return the next attribute...
2635   */
2636 
2637   return (ipp->current = ipp->current->next);
2638 }
2639 
2640 
2641 /*
2642  * 'ippNew()' - Allocate a new IPP message.
2643  */
2644 
2645 ipp_t *					/* O - New IPP message */
ippNew(void)2646 ippNew(void)
2647 {
2648   ipp_t			*temp;		/* New IPP message */
2649   _cups_globals_t	*cg = _cupsGlobals();
2650 					/* Global data */
2651 
2652 
2653   DEBUG_puts("ippNew()");
2654 
2655   if ((temp = (ipp_t *)calloc(1, sizeof(ipp_t))) != NULL)
2656   {
2657    /*
2658     * Set default version - usually 2.0...
2659     */
2660 
2661     DEBUG_printf(("4debug_alloc: %p IPP message", (void *)temp));
2662 
2663     if (cg->server_version == 0)
2664       _cupsSetDefaults();
2665 
2666     temp->request.any.version[0] = (ipp_uchar_t)(cg->server_version / 10);
2667     temp->request.any.version[1] = (ipp_uchar_t)(cg->server_version % 10);
2668     temp->use                    = 1;
2669   }
2670 
2671   DEBUG_printf(("1ippNew: Returning %p", (void *)temp));
2672 
2673   return (temp);
2674 }
2675 
2676 
2677 /*
2678  *  'ippNewRequest()' - Allocate a new IPP request message.
2679  *
2680  * The new request message is initialized with the "attributes-charset" and
2681  * "attributes-natural-language" attributes added. The
2682  * "attributes-natural-language" value is derived from the current locale.
2683  *
2684  * @since CUPS 1.2/macOS 10.5@
2685  */
2686 
2687 ipp_t *					/* O - IPP request message */
ippNewRequest(ipp_op_t op)2688 ippNewRequest(ipp_op_t op)		/* I - Operation code */
2689 {
2690   ipp_t		*request;		/* IPP request message */
2691   cups_lang_t	*language;		/* Current language localization */
2692   static int	request_id = 0;		/* Current request ID */
2693   static _cups_mutex_t request_mutex = _CUPS_MUTEX_INITIALIZER;
2694 					/* Mutex for request ID */
2695 
2696 
2697   DEBUG_printf(("ippNewRequest(op=%02x(%s))", op, ippOpString(op)));
2698 
2699  /*
2700   * Create a new IPP message...
2701   */
2702 
2703   if ((request = ippNew()) == NULL)
2704     return (NULL);
2705 
2706  /*
2707   * Set the operation and request ID...
2708   */
2709 
2710   _cupsMutexLock(&request_mutex);
2711 
2712   request->request.op.operation_id = op;
2713   request->request.op.request_id   = ++request_id;
2714 
2715   _cupsMutexUnlock(&request_mutex);
2716 
2717  /*
2718   * Use UTF-8 as the character set...
2719   */
2720 
2721   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
2722                "attributes-charset", NULL, "utf-8");
2723 
2724  /*
2725   * Get the language from the current locale...
2726   */
2727 
2728   language = cupsLangDefault();
2729 
2730   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
2731                "attributes-natural-language", NULL, language->language);
2732 
2733  /*
2734   * Return the new request...
2735   */
2736 
2737   return (request);
2738 }
2739 
2740 
2741 /*
2742  * 'ippNewResponse()' - Allocate a new IPP response message.
2743  *
2744  * The new response message is initialized with the same "version-number",
2745  * "request-id", "attributes-charset", and "attributes-natural-language" as the
2746  * provided request message.  If the "attributes-charset" or
2747  * "attributes-natural-language" attributes are missing from the request,
2748  * 'utf-8' and a value derived from the current locale are substituted,
2749  * respectively.
2750  *
2751  * @since CUPS 1.7/macOS 10.9@
2752  */
2753 
2754 ipp_t *					/* O - IPP response message */
ippNewResponse(ipp_t * request)2755 ippNewResponse(ipp_t *request)		/* I - IPP request message */
2756 {
2757   ipp_t			*response;	/* IPP response message */
2758   ipp_attribute_t	*attr;		/* Current attribute */
2759 
2760 
2761  /*
2762   * Range check input...
2763   */
2764 
2765   if (!request)
2766     return (NULL);
2767 
2768  /*
2769   * Create a new IPP message...
2770   */
2771 
2772   if ((response = ippNew()) == NULL)
2773     return (NULL);
2774 
2775  /*
2776   * Copy the request values over to the response...
2777   */
2778 
2779   response->request.status.version[0] = request->request.op.version[0];
2780   response->request.status.version[1] = request->request.op.version[1];
2781   response->request.status.request_id = request->request.op.request_id;
2782 
2783  /*
2784   * The first attribute MUST be attributes-charset...
2785   */
2786 
2787   attr = request->attrs;
2788 
2789   if (attr && attr->name && !strcmp(attr->name, "attributes-charset") &&
2790       attr->group_tag == IPP_TAG_OPERATION &&
2791       attr->value_tag == IPP_TAG_CHARSET &&
2792       attr->num_values == 1)
2793   {
2794    /*
2795     * Copy charset from request...
2796     */
2797 
2798     ippAddString(response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
2799 		 "attributes-charset", NULL, attr->values[0].string.text);
2800   }
2801   else
2802   {
2803    /*
2804     * Use "utf-8" as the default...
2805     */
2806 
2807     ippAddString(response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
2808 		 "attributes-charset", NULL, "utf-8");
2809   }
2810 
2811  /*
2812   * Then attributes-natural-language...
2813   */
2814 
2815   if (attr)
2816     attr = attr->next;
2817 
2818   if (attr && attr->name &&
2819       !strcmp(attr->name, "attributes-natural-language") &&
2820       attr->group_tag == IPP_TAG_OPERATION &&
2821       attr->value_tag == IPP_TAG_LANGUAGE &&
2822       attr->num_values == 1)
2823   {
2824    /*
2825     * Copy language from request...
2826     */
2827 
2828     ippAddString(response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
2829 		 "attributes-natural-language", NULL,
2830 		 attr->values[0].string.text);
2831   }
2832   else
2833   {
2834    /*
2835     * Use the language from the current locale...
2836     */
2837 
2838     cups_lang_t *language = cupsLangDefault();
2839 					/* Current locale */
2840 
2841     ippAddString(response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
2842 		 "attributes-natural-language", NULL, language->language);
2843   }
2844 
2845   return (response);
2846 }
2847 
2848 
2849 /*
2850  * 'ippRead()' - Read data for an IPP message from a HTTP connection.
2851  */
2852 
2853 ipp_state_t				/* O - Current state */
ippRead(http_t * http,ipp_t * ipp)2854 ippRead(http_t *http,			/* I - HTTP connection */
2855         ipp_t  *ipp)			/* I - IPP data */
2856 {
2857   DEBUG_printf(("ippRead(http=%p, ipp=%p), data_remaining=" CUPS_LLFMT, (void *)http, (void *)ipp, CUPS_LLCAST (http ? http->data_remaining : -1)));
2858 
2859   if (!http | !ipp)
2860     return (IPP_STATE_ERROR);
2861 
2862   DEBUG_printf(("2ippRead: http->state=%d, http->used=%d", http->state, http->used));
2863 
2864   return (ipp_read_io(http, (ipp_iocb_t)ipp_read_http, http->blocking, /*parent*/NULL, ipp, /*depth*/0));
2865 }
2866 
2867 
2868 /*
2869  * 'ippReadFile()' - Read data for an IPP message from a file.
2870  *
2871  * @since CUPS 1.1.19/macOS 10.3@
2872  */
2873 
2874 ipp_state_t				/* O - Current state */
ippReadFile(int fd,ipp_t * ipp)2875 ippReadFile(int   fd,			/* I - HTTP data */
2876             ipp_t *ipp)			/* I - IPP data */
2877 {
2878   DEBUG_printf(("ippReadFile(fd=%d, ipp=%p)", fd, (void *)ipp));
2879 
2880   if (!ipp)
2881     return (IPP_STATE_ERROR);
2882 
2883   return (ipp_read_io(&fd, (ipp_iocb_t)ipp_read_file, /*blocking*/1, /*parent*/NULL, ipp, /*depth*/0));
2884 }
2885 
2886 
2887 /*
2888  * 'ippReadIO()' - Read data for an IPP message.
2889  *
2890  * @since CUPS 1.2/macOS 10.5@
2891  */
2892 
2893 ipp_state_t				/* O - Current state */
ippReadIO(void * src,ipp_iocb_t cb,int blocking,ipp_t * parent,ipp_t * ipp)2894 ippReadIO(void       *src,		/* I - Data source */
2895           ipp_iocb_t cb,		/* I - Read callback function */
2896 	  int        blocking,		/* I - Use blocking IO? */
2897 	  ipp_t      *parent,		/* I - Parent request, if any */
2898           ipp_t      *ipp)		/* I - IPP data */
2899 {
2900   DEBUG_printf(("ippReadIO(src=%p, cb=%p, blocking=%d, parent=%p, ipp=%p)", (void *)src, (void *)cb, blocking, (void *)parent, (void *)ipp));
2901 
2902   if (!src || !ipp)
2903     return (IPP_STATE_ERROR);
2904 
2905   return (ipp_read_io(src, cb, blocking, parent, ipp, /*depth*/0));
2906 }
2907 
2908 
2909 /*
2910  * 'ippSetBoolean()' - Set a boolean value in an attribute.
2911  *
2912  * The @code ipp@ parameter refers to an IPP message previously created using
2913  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
2914  *
2915  * The @code attr@ parameter may be modified as a result of setting the value.
2916  *
2917  * The @code element@ parameter specifies which value to set from 0 to
2918  * @code ippGetCount(attr)@.
2919  *
2920  * @since CUPS 1.6/macOS 10.8@
2921  */
2922 
2923 int					/* O  - 1 on success, 0 on failure */
ippSetBoolean(ipp_t * ipp,ipp_attribute_t ** attr,int element,int boolvalue)2924 ippSetBoolean(ipp_t           *ipp,	/* I  - IPP message */
2925               ipp_attribute_t **attr,	/* IO - IPP attribute */
2926               int             element,	/* I  - Value number (0-based) */
2927               int             boolvalue)/* I  - Boolean value */
2928 {
2929   _ipp_value_t	*value;			/* Current value */
2930 
2931 
2932  /*
2933   * Range check input...
2934   */
2935 
2936   if (!ipp || !attr || !*attr || (*attr)->value_tag != IPP_TAG_BOOLEAN ||
2937       element < 0 || element > (*attr)->num_values)
2938     return (0);
2939 
2940  /*
2941   * Set the value and return...
2942   */
2943 
2944   if ((value = ipp_set_value(ipp, attr, element)) != NULL)
2945     value->boolean = (char)boolvalue;
2946 
2947   return (value != NULL);
2948 }
2949 
2950 
2951 /*
2952  * 'ippSetCollection()' - Set a collection value in an attribute.
2953  *
2954  * The @code ipp@ parameter refers to an IPP message previously created using
2955  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
2956  *
2957  * The @code attr@ parameter may be modified as a result of setting the value.
2958  *
2959  * The @code element@ parameter specifies which value to set from 0 to
2960  * @code ippGetCount(attr)@.
2961  *
2962  * @since CUPS 1.6/macOS 10.8@
2963  */
2964 
2965 int					/* O  - 1 on success, 0 on failure */
ippSetCollection(ipp_t * ipp,ipp_attribute_t ** attr,int element,ipp_t * colvalue)2966 ippSetCollection(
2967     ipp_t           *ipp,		/* I  - IPP message */
2968     ipp_attribute_t **attr,		/* IO - IPP attribute */
2969     int             element,		/* I  - Value number (0-based) */
2970     ipp_t           *colvalue)		/* I  - Collection value */
2971 {
2972   _ipp_value_t	*value;			/* Current value */
2973 
2974 
2975  /*
2976   * Range check input...
2977   */
2978 
2979   if (!ipp || !attr || !*attr || (*attr)->value_tag != IPP_TAG_BEGIN_COLLECTION ||
2980       element < 0 || element > (*attr)->num_values || !colvalue)
2981     return (0);
2982 
2983  /*
2984   * Set the value and return...
2985   */
2986 
2987   if ((value = ipp_set_value(ipp, attr, element)) != NULL)
2988   {
2989     if (value->collection)
2990       ippDelete(value->collection);
2991 
2992     value->collection = colvalue;
2993     colvalue->use ++;
2994   }
2995 
2996   return (value != NULL);
2997 }
2998 
2999 
3000 /*
3001  * 'ippSetDate()' - Set a dateTime value in an attribute.
3002  *
3003  * The @code ipp@ parameter refers to an IPP message previously created using
3004  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
3005  *
3006  * The @code attr@ parameter may be modified as a result of setting the value.
3007  *
3008  * The @code element@ parameter specifies which value to set from 0 to
3009  * @code ippGetCount(attr)@.
3010  *
3011  * @since CUPS 1.6/macOS 10.8@
3012  */
3013 
3014 int					/* O  - 1 on success, 0 on failure */
ippSetDate(ipp_t * ipp,ipp_attribute_t ** attr,int element,const ipp_uchar_t * datevalue)3015 ippSetDate(ipp_t             *ipp,	/* I  - IPP message */
3016            ipp_attribute_t   **attr,	/* IO - IPP attribute */
3017            int               element,	/* I  - Value number (0-based) */
3018            const ipp_uchar_t *datevalue)/* I  - dateTime value */
3019 {
3020   _ipp_value_t	*value;			/* Current value */
3021 
3022 
3023  /*
3024   * Range check input...
3025   */
3026 
3027   if (!ipp || !attr || !*attr || ((*attr)->value_tag != IPP_TAG_DATE && (*attr)->value_tag != IPP_TAG_NOVALUE && (*attr)->value_tag != IPP_TAG_UNKNOWN) || element < 0 || element > (*attr)->num_values || !datevalue)
3028     return (0);
3029 
3030  /*
3031   * Set the value and return...
3032   */
3033 
3034   if ((value = ipp_set_value(ipp, attr, element)) != NULL)
3035     memcpy(value->date, datevalue, sizeof(value->date));
3036 
3037   return (value != NULL);
3038 }
3039 
3040 
3041 /*
3042  * 'ippSetGroupTag()' - Set the group tag of an attribute.
3043  *
3044  * The @code ipp@ parameter refers to an IPP message previously created using
3045  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
3046  *
3047  * The @code attr@ parameter may be modified as a result of setting the value.
3048  *
3049  * The @code group@ parameter specifies the IPP attribute group tag: none
3050  * (@code IPP_TAG_ZERO@, for member attributes), document (@code IPP_TAG_DOCUMENT@),
3051  * event notification (@code IPP_TAG_EVENT_NOTIFICATION@), operation
3052  * (@code IPP_TAG_OPERATION@), printer (@code IPP_TAG_PRINTER@), subscription
3053  * (@code IPP_TAG_SUBSCRIPTION@), or unsupported (@code IPP_TAG_UNSUPPORTED_GROUP@).
3054  *
3055  * @since CUPS 1.6/macOS 10.8@
3056  */
3057 
3058 int					/* O  - 1 on success, 0 on failure */
ippSetGroupTag(ipp_t * ipp,ipp_attribute_t ** attr,ipp_tag_t group_tag)3059 ippSetGroupTag(
3060     ipp_t           *ipp,		/* I  - IPP message */
3061     ipp_attribute_t **attr,		/* IO - Attribute */
3062     ipp_tag_t       group_tag)		/* I  - Group tag */
3063 {
3064  /*
3065   * Range check input - group tag must be 0x01 to 0x0F, per RFC 8011...
3066   */
3067 
3068   if (!ipp || !attr || !*attr ||
3069       group_tag < IPP_TAG_ZERO || group_tag == IPP_TAG_END ||
3070       group_tag >= IPP_TAG_UNSUPPORTED_VALUE)
3071     return (0);
3072 
3073  /*
3074   * Set the group tag and return...
3075   */
3076 
3077   (*attr)->group_tag = group_tag;
3078 
3079   return (1);
3080 }
3081 
3082 
3083 /*
3084  * 'ippSetInteger()' - Set an integer or enum value in an attribute.
3085  *
3086  * The @code ipp@ parameter refers to an IPP message previously created using
3087  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
3088  *
3089  * The @code attr@ parameter may be modified as a result of setting the value.
3090  *
3091  * The @code element@ parameter specifies which value to set from 0 to
3092  * @code ippGetCount(attr)@.
3093  *
3094  * @since CUPS 1.6/macOS 10.8@
3095  */
3096 
3097 int					/* O  - 1 on success, 0 on failure */
ippSetInteger(ipp_t * ipp,ipp_attribute_t ** attr,int element,int intvalue)3098 ippSetInteger(ipp_t           *ipp,	/* I  - IPP message */
3099               ipp_attribute_t **attr,	/* IO - IPP attribute */
3100               int             element,	/* I  - Value number (0-based) */
3101               int             intvalue)	/* I  - Integer/enum value */
3102 {
3103   _ipp_value_t	*value;			/* Current value */
3104 
3105 
3106  /*
3107   * Range check input...
3108   */
3109 
3110   if (!ipp || !attr || !*attr || ((*attr)->value_tag != IPP_TAG_INTEGER && (*attr)->value_tag != IPP_TAG_ENUM && (*attr)->value_tag != IPP_TAG_NOVALUE && (*attr)->value_tag != IPP_TAG_UNKNOWN) || element < 0 || element > (*attr)->num_values)
3111     return (0);
3112 
3113  /*
3114   * Set the value and return...
3115   */
3116 
3117   if ((value = ipp_set_value(ipp, attr, element)) != NULL)
3118   {
3119     if ((*attr)->value_tag != IPP_TAG_ENUM)
3120       (*attr)->value_tag = IPP_TAG_INTEGER;
3121 
3122     value->integer = intvalue;
3123   }
3124 
3125   return (value != NULL);
3126 }
3127 
3128 
3129 /*
3130  * 'ippSetName()' - Set the name of an attribute.
3131  *
3132  * The @code ipp@ parameter refers to an IPP message previously created using
3133  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
3134  *
3135  * The @code attr@ parameter may be modified as a result of setting the value.
3136  *
3137  * @since CUPS 1.6/macOS 10.8@
3138  */
3139 
3140 int					/* O  - 1 on success, 0 on failure */
ippSetName(ipp_t * ipp,ipp_attribute_t ** attr,const char * name)3141 ippSetName(ipp_t           *ipp,	/* I  - IPP message */
3142 	   ipp_attribute_t **attr,	/* IO - IPP attribute */
3143 	   const char      *name)	/* I  - Attribute name */
3144 {
3145   char	*temp;				/* Temporary name value */
3146 
3147 
3148  /*
3149   * Range check input...
3150   */
3151 
3152   if (!ipp || !attr || !*attr)
3153     return (0);
3154 
3155  /*
3156   * Set the value and return...
3157   */
3158 
3159   if ((temp = _cupsStrAlloc(name)) != NULL)
3160   {
3161     if ((*attr)->name)
3162       _cupsStrFree((*attr)->name);
3163 
3164     (*attr)->name = temp;
3165   }
3166 
3167   return (temp != NULL);
3168 }
3169 
3170 
3171 /*
3172  * 'ippSetOctetString()' - Set an octetString value in an IPP attribute.
3173  *
3174  * The @code ipp@ parameter refers to an IPP message previously created using
3175  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
3176  *
3177  * The @code attr@ parameter may be modified as a result of setting the value.
3178  *
3179  * The @code element@ parameter specifies which value to set from 0 to
3180  * @code ippGetCount(attr)@.
3181  *
3182  * @since CUPS 1.7/macOS 10.9@
3183  */
3184 
3185 int					/* O  - 1 on success, 0 on failure */
ippSetOctetString(ipp_t * ipp,ipp_attribute_t ** attr,int element,const void * data,int datalen)3186 ippSetOctetString(
3187     ipp_t           *ipp,		/* I  - IPP message */
3188     ipp_attribute_t **attr,		/* IO - IPP attribute */
3189     int             element,		/* I  - Value number (0-based) */
3190     const void      *data,		/* I  - Pointer to octetString data */
3191     int             datalen)		/* I  - Length of octetString data */
3192 {
3193   _ipp_value_t	*value;			/* Current value */
3194 
3195 
3196  /*
3197   * Range check input...
3198   */
3199 
3200   if (!ipp || !attr || !*attr || ((*attr)->value_tag != IPP_TAG_STRING && (*attr)->value_tag != IPP_TAG_NOVALUE && (*attr)->value_tag != IPP_TAG_UNKNOWN) || element < 0 || element > (*attr)->num_values || datalen < 0 || datalen > IPP_MAX_LENGTH)
3201     return (0);
3202 
3203  /*
3204   * Set the value and return...
3205   */
3206 
3207   if ((value = ipp_set_value(ipp, attr, element)) != NULL)
3208   {
3209     if ((int)((*attr)->value_tag) & IPP_TAG_CUPS_CONST)
3210     {
3211      /*
3212       * Just copy the pointer...
3213       */
3214 
3215       value->unknown.data   = (void *)data;
3216       value->unknown.length = datalen;
3217     }
3218     else
3219     {
3220      /*
3221       * Copy the data...
3222       */
3223 
3224       (*attr)->value_tag = IPP_TAG_STRING;
3225 
3226       if (value->unknown.data)
3227       {
3228        /*
3229 	* Free previous data...
3230 	*/
3231 
3232 	free(value->unknown.data);
3233 
3234 	value->unknown.data   = NULL;
3235         value->unknown.length = 0;
3236       }
3237 
3238       if (datalen > 0)
3239       {
3240 	void	*temp;			/* Temporary data pointer */
3241 
3242 	if ((temp = malloc((size_t)datalen)) != NULL)
3243 	{
3244 	  memcpy(temp, data, (size_t)datalen);
3245 
3246 	  value->unknown.data   = temp;
3247 	  value->unknown.length = datalen;
3248 	}
3249 	else
3250 	  return (0);
3251       }
3252     }
3253   }
3254 
3255   return (value != NULL);
3256 }
3257 
3258 
3259 /*
3260  * 'ippSetOperation()' - Set the operation ID in an IPP request message.
3261  *
3262  * The @code ipp@ parameter refers to an IPP message previously created using
3263  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
3264  *
3265  * @since CUPS 1.6/macOS 10.8@
3266  */
3267 
3268 int					/* O - 1 on success, 0 on failure */
ippSetOperation(ipp_t * ipp,ipp_op_t op)3269 ippSetOperation(ipp_t    *ipp,		/* I - IPP request message */
3270                 ipp_op_t op)		/* I - Operation ID */
3271 {
3272  /*
3273   * Range check input...
3274   */
3275 
3276   if (!ipp)
3277     return (0);
3278 
3279  /*
3280   * Set the operation and return...
3281   */
3282 
3283   ipp->request.op.operation_id = op;
3284 
3285   return (1);
3286 }
3287 
3288 
3289 /*
3290  * 'ippSetRange()' - Set a rangeOfInteger value in an attribute.
3291  *
3292  * The @code ipp@ parameter refers to an IPP message previously created using
3293  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
3294  *
3295  * The @code attr@ parameter may be modified as a result of setting the value.
3296  *
3297  * The @code element@ parameter specifies which value to set from 0 to
3298  * @code ippGetCount(attr)@.
3299  *
3300  * @since CUPS 1.6/macOS 10.8@
3301  */
3302 
3303 int					/* O  - 1 on success, 0 on failure */
ippSetRange(ipp_t * ipp,ipp_attribute_t ** attr,int element,int lowervalue,int uppervalue)3304 ippSetRange(ipp_t           *ipp,	/* I  - IPP message */
3305             ipp_attribute_t **attr,	/* IO - IPP attribute */
3306             int             element,	/* I  - Value number (0-based) */
3307 	    int             lowervalue,	/* I  - Lower bound for range */
3308 	    int             uppervalue)	/* I  - Upper bound for range */
3309 {
3310   _ipp_value_t	*value;			/* Current value */
3311 
3312 
3313  /*
3314   * Range check input...
3315   */
3316 
3317   if (!ipp || !attr || !*attr || ((*attr)->value_tag != IPP_TAG_RANGE && (*attr)->value_tag != IPP_TAG_NOVALUE && (*attr)->value_tag != IPP_TAG_UNKNOWN) || element < 0 || element > (*attr)->num_values || lowervalue > uppervalue)
3318     return (0);
3319 
3320  /*
3321   * Set the value and return...
3322   */
3323 
3324   if ((value = ipp_set_value(ipp, attr, element)) != NULL)
3325   {
3326     (*attr)->value_tag = IPP_TAG_RANGE;
3327     value->range.lower = lowervalue;
3328     value->range.upper = uppervalue;
3329   }
3330 
3331   return (value != NULL);
3332 }
3333 
3334 
3335 /*
3336  * 'ippSetRequestId()' - Set the request ID in an IPP message.
3337  *
3338  * The @code ipp@ parameter refers to an IPP message previously created using
3339  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
3340  *
3341  * The @code request_id@ parameter must be greater than 0.
3342  *
3343  * @since CUPS 1.6/macOS 10.8@
3344  */
3345 
3346 int					/* O - 1 on success, 0 on failure */
ippSetRequestId(ipp_t * ipp,int request_id)3347 ippSetRequestId(ipp_t *ipp,		/* I - IPP message */
3348                 int   request_id)	/* I - Request ID */
3349 {
3350  /*
3351   * Range check input; not checking request_id values since ipptool wants to send
3352   * invalid values for conformance testing and a bad request_id does not affect the
3353   * encoding of a message...
3354   */
3355 
3356   if (!ipp)
3357     return (0);
3358 
3359  /*
3360   * Set the request ID and return...
3361   */
3362 
3363   ipp->request.any.request_id = request_id;
3364 
3365   return (1);
3366 }
3367 
3368 
3369 /*
3370  * 'ippSetResolution()' - Set a resolution value in an attribute.
3371  *
3372  * The @code ipp@ parameter refers to an IPP message previously created using
3373  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
3374  *
3375  * The @code attr@ parameter may be modified as a result of setting the value.
3376  *
3377  * The @code element@ parameter specifies which value to set from 0 to
3378  * @code ippGetCount(attr)@.
3379  *
3380  * @since CUPS 1.6/macOS 10.8@
3381  */
3382 
3383 int					/* O  - 1 on success, 0 on failure */
ippSetResolution(ipp_t * ipp,ipp_attribute_t ** attr,int element,ipp_res_t unitsvalue,int xresvalue,int yresvalue)3384 ippSetResolution(
3385     ipp_t           *ipp,		/* I  - IPP message */
3386     ipp_attribute_t **attr,		/* IO - IPP attribute */
3387     int             element,		/* I  - Value number (0-based) */
3388     ipp_res_t       unitsvalue,		/* I  - Resolution units */
3389     int             xresvalue,		/* I  - Horizontal/cross feed resolution */
3390     int             yresvalue)		/* I  - Vertical/feed resolution */
3391 {
3392   _ipp_value_t	*value;			/* Current value */
3393 
3394 
3395  /*
3396   * Range check input...
3397   */
3398 
3399   if (!ipp || !attr || !*attr || ((*attr)->value_tag != IPP_TAG_RESOLUTION && (*attr)->value_tag != IPP_TAG_NOVALUE && (*attr)->value_tag != IPP_TAG_UNKNOWN) || element < 0 || element > (*attr)->num_values || xresvalue <= 0 || yresvalue <= 0 || unitsvalue < IPP_RES_PER_INCH || unitsvalue > IPP_RES_PER_CM)
3400     return (0);
3401 
3402  /*
3403   * Set the value and return...
3404   */
3405 
3406   if ((value = ipp_set_value(ipp, attr, element)) != NULL)
3407   {
3408     (*attr)->value_tag      = IPP_TAG_RESOLUTION;
3409     value->resolution.units = unitsvalue;
3410     value->resolution.xres  = xresvalue;
3411     value->resolution.yres  = yresvalue;
3412   }
3413 
3414   return (value != NULL);
3415 }
3416 
3417 
3418 /*
3419  * 'ippSetState()' - Set the current state of the IPP message.
3420  *
3421  * @since CUPS 1.6/macOS 10.8@
3422  */
3423 
3424 int					/* O - 1 on success, 0 on failure */
ippSetState(ipp_t * ipp,ipp_state_t state)3425 ippSetState(ipp_t       *ipp,		/* I - IPP message */
3426             ipp_state_t state)		/* I - IPP state value */
3427 {
3428  /*
3429   * Range check input...
3430   */
3431 
3432   if (!ipp)
3433     return (0);
3434 
3435  /*
3436   * Set the state and return...
3437   */
3438 
3439   ipp->state   = state;
3440   ipp->current = NULL;
3441 
3442   return (1);
3443 }
3444 
3445 
3446 /*
3447  * 'ippSetStatusCode()' - Set the status code in an IPP response or event message.
3448  *
3449  * The @code ipp@ parameter refers to an IPP message previously created using
3450  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
3451  *
3452  * @since CUPS 1.6/macOS 10.8@
3453  */
3454 
3455 int					/* O - 1 on success, 0 on failure */
ippSetStatusCode(ipp_t * ipp,ipp_status_t status)3456 ippSetStatusCode(ipp_t        *ipp,	/* I - IPP response or event message */
3457                  ipp_status_t status)	/* I - Status code */
3458 {
3459  /*
3460   * Range check input...
3461   */
3462 
3463   if (!ipp)
3464     return (0);
3465 
3466  /*
3467   * Set the status code and return...
3468   */
3469 
3470   ipp->request.status.status_code = status;
3471 
3472   return (1);
3473 }
3474 
3475 
3476 /*
3477  * 'ippSetString()' - Set a string value in an attribute.
3478  *
3479  * The @code ipp@ parameter refers to an IPP message previously created using
3480  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
3481  *
3482  * The @code attr@ parameter may be modified as a result of setting the value.
3483  *
3484  * The @code element@ parameter specifies which value to set from 0 to
3485  * @code ippGetCount(attr)@.
3486  *
3487  * @since CUPS 1.6/macOS 10.8@
3488  */
3489 
3490 int					/* O  - 1 on success, 0 on failure */
ippSetString(ipp_t * ipp,ipp_attribute_t ** attr,int element,const char * strvalue)3491 ippSetString(ipp_t           *ipp,	/* I  - IPP message */
3492              ipp_attribute_t **attr,	/* IO - IPP attribute */
3493              int             element,	/* I  - Value number (0-based) */
3494 	     const char      *strvalue)	/* I  - String value */
3495 {
3496   char		*temp;			/* Temporary string */
3497   _ipp_value_t	*value;			/* Current value */
3498   ipp_tag_t	value_tag;		/* Value tag */
3499 
3500 
3501  /*
3502   * Range check input...
3503   */
3504 
3505   if (attr && *attr)
3506     value_tag = (*attr)->value_tag & IPP_TAG_CUPS_MASK;
3507   else
3508     value_tag = IPP_TAG_ZERO;
3509 
3510   if (!ipp || !attr || !*attr || (value_tag < IPP_TAG_TEXT && value_tag != IPP_TAG_TEXTLANG && value_tag != IPP_TAG_NAMELANG && value_tag != IPP_TAG_NOVALUE && value_tag != IPP_TAG_UNKNOWN) || value_tag > IPP_TAG_MIMETYPE || element < 0 || element > (*attr)->num_values || !strvalue)
3511     return (0);
3512 
3513  /*
3514   * Set the value and return...
3515   */
3516 
3517   if ((value = ipp_set_value(ipp, attr, element)) != NULL)
3518   {
3519     if (value_tag == IPP_TAG_NOVALUE || value_tag == IPP_TAG_UNKNOWN)
3520       (*attr)->value_tag = IPP_TAG_KEYWORD;
3521 
3522     if (element > 0)
3523       value->string.language = (*attr)->values[0].string.language;
3524 
3525     if ((int)((*attr)->value_tag) & IPP_TAG_CUPS_CONST)
3526       value->string.text = (char *)strvalue;
3527     else if ((temp = _cupsStrAlloc(strvalue)) != NULL)
3528     {
3529       if (value->string.text)
3530         _cupsStrFree(value->string.text);
3531 
3532       value->string.text = temp;
3533     }
3534     else
3535       return (0);
3536   }
3537 
3538   return (value != NULL);
3539 }
3540 
3541 
3542 /*
3543  * 'ippSetStringf()' - Set a formatted string value of an attribute.
3544  *
3545  * The @code ipp@ parameter refers to an IPP message previously created using
3546  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
3547  *
3548  * The @code attr@ parameter may be modified as a result of setting the value.
3549  *
3550  * The @code element@ parameter specifies which value to set from 0 to
3551  * @code ippGetCount(attr)@.
3552  *
3553  * The @code format@ parameter uses formatting characters compatible with the
3554  * printf family of standard functions.  Additional arguments follow it as
3555  * needed.  The formatted string is truncated as needed to the maximum length of
3556  * the corresponding value type.
3557  *
3558  * @since CUPS 1.7/macOS 10.9@
3559  */
3560 
3561 int					/* O  - 1 on success, 0 on failure */
ippSetStringf(ipp_t * ipp,ipp_attribute_t ** attr,int element,const char * format,...)3562 ippSetStringf(ipp_t           *ipp,	/* I  - IPP message */
3563               ipp_attribute_t **attr,	/* IO - IPP attribute */
3564               int             element,	/* I  - Value number (0-based) */
3565 	      const char      *format,	/* I  - Printf-style format string */
3566 	      ...)			/* I  - Additional arguments as needed */
3567 {
3568   int		ret;			/* Return value */
3569   va_list	ap;			/* Pointer to additional arguments */
3570 
3571 
3572   va_start(ap, format);
3573   ret = ippSetStringfv(ipp, attr, element, format, ap);
3574   va_end(ap);
3575 
3576   return (ret);
3577 }
3578 
3579 
3580 /*
3581  * 'ippSetStringf()' - Set a formatted string value of an attribute.
3582  *
3583  * The @code ipp@ parameter refers to an IPP message previously created using
3584  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
3585  *
3586  * The @code attr@ parameter may be modified as a result of setting the value.
3587  *
3588  * The @code element@ parameter specifies which value to set from 0 to
3589  * @code ippGetCount(attr)@.
3590  *
3591  * The @code format@ parameter uses formatting characters compatible with the
3592  * printf family of standard functions.  Additional arguments follow it as
3593  * needed.  The formatted string is truncated as needed to the maximum length of
3594  * the corresponding value type.
3595  *
3596  * @since CUPS 1.7/macOS 10.9@
3597  */
3598 
3599 int					/* O  - 1 on success, 0 on failure */
ippSetStringfv(ipp_t * ipp,ipp_attribute_t ** attr,int element,const char * format,va_list ap)3600 ippSetStringfv(ipp_t           *ipp,	/* I  - IPP message */
3601                ipp_attribute_t **attr,	/* IO - IPP attribute */
3602                int             element,	/* I  - Value number (0-based) */
3603 	       const char      *format,	/* I  - Printf-style format string */
3604 	       va_list         ap)	/* I  - Pointer to additional arguments */
3605 {
3606   ipp_tag_t	value_tag;		/* Value tag */
3607   char		buffer[IPP_MAX_TEXT + 4];
3608 					/* Formatted text string */
3609   ssize_t	bytes,			/* Length of formatted value */
3610 		max_bytes;		/* Maximum number of bytes for value */
3611 
3612 
3613  /*
3614   * Range check input...
3615   */
3616 
3617   if (attr && *attr)
3618     value_tag = (*attr)->value_tag & IPP_TAG_CUPS_MASK;
3619   else
3620     value_tag = IPP_TAG_ZERO;
3621 
3622   if (!ipp || !attr || !*attr || (value_tag < IPP_TAG_TEXT && value_tag != IPP_TAG_TEXTLANG && value_tag != IPP_TAG_NAMELANG && value_tag != IPP_TAG_NOVALUE && value_tag != IPP_TAG_UNKNOWN) || value_tag > IPP_TAG_MIMETYPE || !format)
3623     return (0);
3624 
3625  /*
3626   * Format the string...
3627   */
3628 
3629   if (!strcmp(format, "%s"))
3630   {
3631    /*
3632     * Optimize the simple case...
3633     */
3634 
3635     const char *s = va_arg(ap, char *);
3636 
3637     if (!s)
3638       s = "(null)";
3639 
3640     bytes = (ssize_t)strlen(s);
3641     strlcpy(buffer, s, sizeof(buffer));
3642   }
3643   else
3644   {
3645    /*
3646     * Do a full formatting of the message...
3647     */
3648 
3649     if ((bytes = vsnprintf(buffer, sizeof(buffer), format, ap)) < 0)
3650       return (0);
3651   }
3652 
3653  /*
3654   * Limit the length of the string...
3655   */
3656 
3657   switch (value_tag)
3658   {
3659     default :
3660     case IPP_TAG_TEXT :
3661     case IPP_TAG_TEXTLANG :
3662         max_bytes = IPP_MAX_TEXT;
3663         break;
3664 
3665     case IPP_TAG_NAME :
3666     case IPP_TAG_NAMELANG :
3667         max_bytes = IPP_MAX_NAME;
3668         break;
3669 
3670     case IPP_TAG_CHARSET :
3671         max_bytes = IPP_MAX_CHARSET;
3672         break;
3673 
3674     case IPP_TAG_NOVALUE :
3675     case IPP_TAG_UNKNOWN :
3676     case IPP_TAG_KEYWORD :
3677         max_bytes = IPP_MAX_KEYWORD;
3678         break;
3679 
3680     case IPP_TAG_LANGUAGE :
3681         max_bytes = IPP_MAX_LANGUAGE;
3682         break;
3683 
3684     case IPP_TAG_MIMETYPE :
3685         max_bytes = IPP_MAX_MIMETYPE;
3686         break;
3687 
3688     case IPP_TAG_URI :
3689         max_bytes = IPP_MAX_URI;
3690         break;
3691 
3692     case IPP_TAG_URISCHEME :
3693         max_bytes = IPP_MAX_URISCHEME;
3694         break;
3695   }
3696 
3697   if (bytes >= max_bytes)
3698   {
3699     char	*bufmax,		/* Buffer at max_bytes */
3700 		*bufptr;		/* Pointer into buffer */
3701 
3702     bufptr = buffer + strlen(buffer) - 1;
3703     bufmax = buffer + max_bytes - 1;
3704 
3705     while (bufptr > bufmax)
3706     {
3707       if (*bufptr & 0x80)
3708       {
3709         while ((*bufptr & 0xc0) == 0x80 && bufptr > buffer)
3710           bufptr --;
3711       }
3712 
3713       bufptr --;
3714     }
3715 
3716     *bufptr = '\0';
3717   }
3718 
3719  /*
3720   * Set the formatted string and return...
3721   */
3722 
3723   return (ippSetString(ipp, attr, element, buffer));
3724 }
3725 
3726 
3727 /*
3728  * 'ippSetValueTag()' - Set the value tag of an attribute.
3729  *
3730  * The @code ipp@ parameter refers to an IPP message previously created using
3731  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
3732  *
3733  * The @code attr@ parameter may be modified as a result of setting the value.
3734  *
3735  * Integer (@code IPP_TAG_INTEGER@) values can be promoted to rangeOfInteger
3736  * (@code IPP_TAG_RANGE@) values, the various string tags can be promoted to name
3737  * (@code IPP_TAG_NAME@) or nameWithLanguage (@code IPP_TAG_NAMELANG@) values, text
3738  * (@code IPP_TAG_TEXT@) values can be promoted to textWithLanguage
3739  * (@code IPP_TAG_TEXTLANG@) values, and all values can be demoted to the various
3740  * out-of-band value tags such as no-value (@code IPP_TAG_NOVALUE@). All other changes
3741  * will be rejected.
3742  *
3743  * Promoting a string attribute to nameWithLanguage or textWithLanguage adds the language
3744  * code in the "attributes-natural-language" attribute or, if not present, the language
3745  * code for the current locale.
3746  *
3747  * @since CUPS 1.6/macOS 10.8@
3748  */
3749 
3750 int					/* O  - 1 on success, 0 on failure */
ippSetValueTag(ipp_t * ipp,ipp_attribute_t ** attr,ipp_tag_t value_tag)3751 ippSetValueTag(
3752     ipp_t          *ipp,		/* I  - IPP message */
3753     ipp_attribute_t **attr,		/* IO - IPP attribute */
3754     ipp_tag_t       value_tag)		/* I  - Value tag */
3755 {
3756   int		i;			/* Looping var */
3757   _ipp_value_t	*value;			/* Current value */
3758   int		integer;		/* Current integer value */
3759   cups_lang_t	*language;		/* Current language */
3760   char		code[32];		/* Language code */
3761   ipp_tag_t	temp_tag;		/* Temporary value tag */
3762 
3763 
3764  /*
3765   * Range check input...
3766   */
3767 
3768   if (!ipp || !attr || !*attr)
3769     return (0);
3770 
3771  /*
3772   * If there is no change, return immediately...
3773   */
3774 
3775   if (value_tag == (*attr)->value_tag)
3776     return (1);
3777 
3778  /*
3779   * Otherwise implement changes as needed...
3780   */
3781 
3782   temp_tag = (ipp_tag_t)((int)((*attr)->value_tag) & IPP_TAG_CUPS_MASK);
3783 
3784   switch (value_tag)
3785   {
3786     case IPP_TAG_UNSUPPORTED_VALUE :
3787     case IPP_TAG_DEFAULT :
3788     case IPP_TAG_UNKNOWN :
3789     case IPP_TAG_NOVALUE :
3790     case IPP_TAG_NOTSETTABLE :
3791     case IPP_TAG_DELETEATTR :
3792     case IPP_TAG_ADMINDEFINE :
3793        /*
3794         * Free any existing values...
3795         */
3796 
3797         if ((*attr)->num_values > 0)
3798           ipp_free_values(*attr, 0, (*attr)->num_values);
3799 
3800        /*
3801         * Set out-of-band value...
3802         */
3803 
3804         (*attr)->value_tag = value_tag;
3805         break;
3806 
3807     case IPP_TAG_RANGE :
3808         if (temp_tag != IPP_TAG_INTEGER)
3809           return (0);
3810 
3811         for (i = (*attr)->num_values, value = (*attr)->values;
3812              i > 0;
3813              i --, value ++)
3814         {
3815           integer            = value->integer;
3816           value->range.lower = value->range.upper = integer;
3817         }
3818 
3819         (*attr)->value_tag = IPP_TAG_RANGE;
3820         break;
3821 
3822     case IPP_TAG_NAME :
3823         if (temp_tag != IPP_TAG_KEYWORD)
3824           return (0);
3825 
3826         (*attr)->value_tag = (ipp_tag_t)(IPP_TAG_NAME | ((*attr)->value_tag & IPP_TAG_CUPS_CONST));
3827         break;
3828 
3829     case IPP_TAG_NAMELANG :
3830     case IPP_TAG_TEXTLANG :
3831         if (value_tag == IPP_TAG_NAMELANG && (temp_tag != IPP_TAG_NAME && temp_tag != IPP_TAG_KEYWORD))
3832           return (0);
3833 
3834         if (value_tag == IPP_TAG_TEXTLANG && temp_tag != IPP_TAG_TEXT)
3835           return (0);
3836 
3837         if (ipp->attrs && ipp->attrs->next && ipp->attrs->next->name &&
3838             !strcmp(ipp->attrs->next->name, "attributes-natural-language") && (ipp->attrs->next->value_tag & IPP_TAG_CUPS_MASK) == IPP_TAG_LANGUAGE)
3839         {
3840          /*
3841           * Use the language code from the IPP message...
3842           */
3843 
3844 	  (*attr)->values[0].string.language =
3845 	      _cupsStrAlloc(ipp->attrs->next->values[0].string.text);
3846         }
3847         else
3848         {
3849          /*
3850           * Otherwise, use the language code corresponding to the locale...
3851           */
3852 
3853 	  language = cupsLangDefault();
3854 	  (*attr)->values[0].string.language = _cupsStrAlloc(ipp_lang_code(language->language,
3855 									code,
3856 									sizeof(code)));
3857         }
3858 
3859         for (i = (*attr)->num_values - 1, value = (*attr)->values + 1;
3860              i > 0;
3861              i --, value ++)
3862           value->string.language = (*attr)->values[0].string.language;
3863 
3864         if ((int)(*attr)->value_tag & IPP_TAG_CUPS_CONST)
3865         {
3866          /*
3867           * Make copies of all values...
3868           */
3869 
3870 	  for (i = (*attr)->num_values, value = (*attr)->values;
3871 	       i > 0;
3872 	       i --, value ++)
3873 	    value->string.text = _cupsStrAlloc(value->string.text);
3874         }
3875 
3876         (*attr)->value_tag = IPP_TAG_NAMELANG;
3877         break;
3878 
3879     case IPP_TAG_KEYWORD :
3880         if (temp_tag == IPP_TAG_NAME || temp_tag == IPP_TAG_NAMELANG)
3881           break;			/* Silently "allow" name -> keyword */
3882 
3883     default :
3884         return (0);
3885   }
3886 
3887   return (1);
3888 }
3889 
3890 
3891 /*
3892  * 'ippSetVersion()' - Set the version number in an IPP message.
3893  *
3894  * The @code ipp@ parameter refers to an IPP message previously created using
3895  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
3896  *
3897  * The valid version numbers are currently 1.0, 1.1, 2.0, 2.1, and 2.2.
3898  *
3899  * @since CUPS 1.6/macOS 10.8@
3900  */
3901 
3902 int					/* O - 1 on success, 0 on failure */
ippSetVersion(ipp_t * ipp,int major,int minor)3903 ippSetVersion(ipp_t *ipp,		/* I - IPP message */
3904               int   major,		/* I - Major version number (major.minor) */
3905               int   minor)		/* I - Minor version number (major.minor) */
3906 {
3907  /*
3908   * Range check input...
3909   */
3910 
3911   if (!ipp || major < 0 || minor < 0)
3912     return (0);
3913 
3914  /*
3915   * Set the version number...
3916   */
3917 
3918   ipp->request.any.version[0] = (ipp_uchar_t)major;
3919   ipp->request.any.version[1] = (ipp_uchar_t)minor;
3920 
3921   return (1);
3922 }
3923 
3924 
3925 /*
3926  * 'ippTimeToDate()' - Convert from time in seconds to RFC 2579 format.
3927  */
3928 
3929 const ipp_uchar_t *			/* O - RFC-2579 date/time data */
ippTimeToDate(time_t t)3930 ippTimeToDate(time_t t)			/* I - Time in seconds */
3931 {
3932   struct tm	unixdate;		/* UNIX unixdate/time info */
3933   ipp_uchar_t	*date = _cupsGlobals()->ipp_date;
3934 					/* RFC-2579 date/time data */
3935 
3936 
3937  /*
3938   * RFC-2579 date/time format is:
3939   *
3940   *    Byte(s)  Description
3941   *    -------  -----------
3942   *    0-1      Year (0 to 65535)
3943   *    2        Month (1 to 12)
3944   *    3        Day (1 to 31)
3945   *    4        Hours (0 to 23)
3946   *    5        Minutes (0 to 59)
3947   *    6        Seconds (0 to 60, 60 = "leap second")
3948   *    7        Deciseconds (0 to 9)
3949   *    8        +/- UTC
3950   *    9        UTC hours (0 to 11)
3951   *    10       UTC minutes (0 to 59)
3952   */
3953 
3954   gmtime_r(&t, &unixdate);
3955   unixdate.tm_year += 1900;
3956 
3957   date[0]  = (ipp_uchar_t)(unixdate.tm_year >> 8);
3958   date[1]  = (ipp_uchar_t)(unixdate.tm_year);
3959   date[2]  = (ipp_uchar_t)(unixdate.tm_mon + 1);
3960   date[3]  = (ipp_uchar_t)unixdate.tm_mday;
3961   date[4]  = (ipp_uchar_t)unixdate.tm_hour;
3962   date[5]  = (ipp_uchar_t)unixdate.tm_min;
3963   date[6]  = (ipp_uchar_t)unixdate.tm_sec;
3964   date[7]  = 0;
3965   date[8]  = '+';
3966   date[9]  = 0;
3967   date[10] = 0;
3968 
3969   return (date);
3970 }
3971 
3972 
3973 /*
3974  * 'ippValidateAttribute()' - Validate the contents of an attribute.
3975  *
3976  * This function validates the contents of an attribute based on the name and
3977  * value tag.  1 is returned if the attribute is valid, 0 otherwise.  On
3978  * failure, @link cupsLastErrorString@ is set to a human-readable message.
3979  *
3980  * @since CUPS 1.7/macOS 10.9@
3981  */
3982 
3983 int					/* O - 1 if valid, 0 otherwise */
ippValidateAttribute(ipp_attribute_t * attr)3984 ippValidateAttribute(
3985     ipp_attribute_t *attr)		/* I - Attribute */
3986 {
3987   int		i;			/* Looping var */
3988   char		scheme[64],		/* Scheme from URI */
3989 		userpass[256],		/* Username/password from URI */
3990 		hostname[256],		/* Hostname from URI */
3991 		resource[1024];		/* Resource from URI */
3992   int		port,			/* Port number from URI */
3993 		uri_status;		/* URI separation status */
3994   const char	*ptr;			/* Pointer into string */
3995   ipp_attribute_t *colattr;		/* Collection attribute */
3996   regex_t	re;			/* Regular expression */
3997   ipp_uchar_t	*date;			/* Current date value */
3998 
3999 
4000  /*
4001   * Skip separators.
4002   */
4003 
4004   if (!attr->name)
4005     return (1);
4006 
4007  /*
4008   * Validate the attribute name.
4009   */
4010 
4011   for (ptr = attr->name; *ptr; ptr ++)
4012     if (!isalnum(*ptr & 255) && *ptr != '-' && *ptr != '.' && *ptr != '_')
4013       break;
4014 
4015   if (*ptr || ptr == attr->name)
4016   {
4017     ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad attribute name - invalid character (RFC 8011 section 5.1.4)."), attr->name);
4018     return (0);
4019   }
4020 
4021   if ((ptr - attr->name) > 255)
4022   {
4023     ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad attribute name - bad length %d (RFC 8011 section 5.1.4)."), attr->name, (int)(ptr - attr->name));
4024     return (0);
4025   }
4026 
4027   switch (attr->value_tag)
4028   {
4029     case IPP_TAG_INTEGER :
4030         break;
4031 
4032     case IPP_TAG_BOOLEAN :
4033         for (i = 0; i < attr->num_values; i ++)
4034 	{
4035 	  if (attr->values[i].boolean != 0 &&
4036 	      attr->values[i].boolean != 1)
4037 	  {
4038 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad boolean value %d (RFC 8011 section 5.1.21)."), attr->name, attr->values[i].boolean);
4039 	    return (0);
4040 	  }
4041 	}
4042         break;
4043 
4044     case IPP_TAG_ENUM :
4045         for (i = 0; i < attr->num_values; i ++)
4046 	{
4047 	  if (attr->values[i].integer < 1)
4048 	  {
4049 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad enum value %d - out of range (RFC 8011 section 5.1.5)."), attr->name, attr->values[i].integer);
4050             return (0);
4051 	  }
4052 	}
4053         break;
4054 
4055     case IPP_TAG_STRING :
4056         for (i = 0; i < attr->num_values; i ++)
4057 	{
4058 	  if (attr->values[i].unknown.length > IPP_MAX_OCTETSTRING)
4059 	  {
4060 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad octetString value - bad length %d (RFC 8011 section 5.1.20)."), attr->name, attr->values[i].unknown.length);
4061 	    return (0);
4062 	  }
4063 	}
4064         break;
4065 
4066     case IPP_TAG_DATE :
4067         for (i = 0; i < attr->num_values; i ++)
4068 	{
4069 	  date = attr->values[i].date;
4070 
4071           if (date[2] < 1 || date[2] > 12)
4072 	  {
4073 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad dateTime month %u (RFC 8011 section 5.1.15)."), attr->name, date[2]);
4074 	    return (0);
4075 	  }
4076 
4077           if (date[3] < 1 || date[3] > 31)
4078 	  {
4079 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad dateTime day %u (RFC 8011 section 5.1.15)."), attr->name, date[3]);
4080 	    return (0);
4081 	  }
4082 
4083           if (date[4] > 23)
4084 	  {
4085 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad dateTime hours %u (RFC 8011 section 5.1.15)."), attr->name, date[4]);
4086 	    return (0);
4087 	  }
4088 
4089           if (date[5] > 59)
4090 	  {
4091 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad dateTime minutes %u (RFC 8011 section 5.1.15)."), attr->name, date[5]);
4092 	    return (0);
4093 	  }
4094 
4095           if (date[6] > 60)
4096 	  {
4097 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad dateTime seconds %u (RFC 8011 section 5.1.15)."), attr->name, date[6]);
4098 	    return (0);
4099 	  }
4100 
4101           if (date[7] > 9)
4102 	  {
4103 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad dateTime deciseconds %u (RFC 8011 section 5.1.15)."), attr->name, date[7]);
4104 	    return (0);
4105 	  }
4106 
4107           if (date[8] != '-' && date[8] != '+')
4108 	  {
4109 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad dateTime UTC sign '%c' (RFC 8011 section 5.1.15)."), attr->name, date[8]);
4110 	    return (0);
4111 	  }
4112 
4113           if (date[9] > 14)
4114 	  {
4115 	    // Kiribata has a UTC+14 time zone, RFC 2579 calls for UTC+13 support, errata filed...
4116 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad dateTime UTC hours %u (RFC 8011 section 5.1.15)."), attr->name, date[9]);
4117 	    return (0);
4118 	  }
4119 
4120           if (date[10] > 59)
4121 	  {
4122 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad dateTime UTC minutes %u (RFC 8011 section 5.1.15)."), attr->name, date[10]);
4123 	    return (0);
4124 	  }
4125 	}
4126         break;
4127 
4128     case IPP_TAG_RESOLUTION :
4129         for (i = 0; i < attr->num_values; i ++)
4130 	{
4131 	  if (attr->values[i].resolution.xres <= 0)
4132 	  {
4133 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad resolution value %dx%d%s - cross feed resolution must be positive (RFC 8011 section 5.1.16)."), attr->name, attr->values[i].resolution.xres, attr->values[i].resolution.yres, attr->values[i].resolution.units == IPP_RES_PER_INCH ? "dpi" : attr->values[i].resolution.units == IPP_RES_PER_CM ? "dpcm" : "unknown");
4134 	    return (0);
4135 	  }
4136 
4137 	  if (attr->values[i].resolution.yres <= 0)
4138 	  {
4139 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad resolution value %dx%d%s - feed resolution must be positive (RFC 8011 section 5.1.16)."), attr->name, attr->values[i].resolution.xres, attr->values[i].resolution.yres, attr->values[i].resolution.units == IPP_RES_PER_INCH ? "dpi" : attr->values[i].resolution.units == IPP_RES_PER_CM ? "dpcm" : "unknown");
4140             return (0);
4141 	  }
4142 
4143 	  if (attr->values[i].resolution.units != IPP_RES_PER_INCH && attr->values[i].resolution.units != IPP_RES_PER_CM)
4144 	  {
4145 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad resolution value %dx%d%s - bad units value (RFC 8011 section 5.1.16)."), attr->name, attr->values[i].resolution.xres, attr->values[i].resolution.yres, attr->values[i].resolution.units == IPP_RES_PER_INCH ? "dpi" : attr->values[i].resolution.units == IPP_RES_PER_CM ? "dpcm" : "unknown");
4146 	    return (0);
4147 	  }
4148 	}
4149         break;
4150 
4151     case IPP_TAG_RANGE :
4152         for (i = 0; i < attr->num_values; i ++)
4153 	{
4154 	  if (attr->values[i].range.lower > attr->values[i].range.upper)
4155 	  {
4156 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad rangeOfInteger value %d-%d - lower greater than upper (RFC 8011 section 5.1.14)."), attr->name, attr->values[i].range.lower, attr->values[i].range.upper);
4157 	    return (0);
4158 	  }
4159 	}
4160         break;
4161 
4162     case IPP_TAG_BEGIN_COLLECTION :
4163         for (i = 0; i < attr->num_values; i ++)
4164 	{
4165 	  for (colattr = attr->values[i].collection->attrs;
4166 	       colattr;
4167 	       colattr = colattr->next)
4168 	  {
4169 	    if (!ippValidateAttribute(colattr))
4170 	      return (0);
4171 	  }
4172 	}
4173         break;
4174 
4175     case IPP_TAG_TEXT :
4176     case IPP_TAG_TEXTLANG :
4177         for (i = 0; i < attr->num_values; i ++)
4178 	{
4179 	  for (ptr = attr->values[i].string.text; *ptr; ptr ++)
4180 	  {
4181 	    if ((*ptr & 0xe0) == 0xc0)
4182 	    {
4183 	      if ((ptr[1] & 0xc0) != 0x80)
4184 	        break;
4185 
4186 	      ptr ++;
4187 	    }
4188 	    else if ((*ptr & 0xf0) == 0xe0)
4189 	    {
4190 	      if ((ptr[1] & 0xc0) != 0x80 || (ptr[2] & 0xc0) != 0x80)
4191 	        break;
4192 
4193 	      ptr += 2;
4194 	    }
4195 	    else if ((*ptr & 0xf8) == 0xf0)
4196 	    {
4197 	      if ((ptr[1] & 0xc0) != 0x80 || (ptr[2] & 0xc0) != 0x80 || (ptr[3] & 0xc0) != 0x80)
4198 	        break;
4199 
4200 	      ptr += 3;
4201 	    }
4202 	    else if (*ptr & 0x80)
4203 	      break;
4204 	    else if ((*ptr < ' ' && *ptr != '\n' && *ptr != '\r' && *ptr != '\t') || *ptr == 0x7f)
4205 	      break;
4206 	  }
4207 
4208           if (*ptr)
4209           {
4210 	    if (*ptr < ' ' || *ptr == 0x7f)
4211 	    {
4212 	      ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad text value \"%s\" - bad control character (PWG 5100.14 section 8.3)."), attr->name, attr->values[i].string.text);
4213 	      return (0);
4214 	    }
4215 	    else
4216 	    {
4217 	      ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad text value \"%s\" - bad UTF-8 sequence (RFC 8011 section 5.1.2)."), attr->name, attr->values[i].string.text);
4218 	      return (0);
4219 	    }
4220           }
4221 
4222 	  if ((ptr - attr->values[i].string.text) > (IPP_MAX_TEXT - 1))
4223 	  {
4224 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad text value \"%s\" - bad length %d (RFC 8011 section 5.1.2)."), attr->name, attr->values[i].string.text, (int)(ptr - attr->values[i].string.text));
4225 	    return (0);
4226 	  }
4227 	}
4228         break;
4229 
4230     case IPP_TAG_NAME :
4231     case IPP_TAG_NAMELANG :
4232         for (i = 0; i < attr->num_values; i ++)
4233 	{
4234 	  for (ptr = attr->values[i].string.text; *ptr; ptr ++)
4235 	  {
4236 	    if ((*ptr & 0xe0) == 0xc0)
4237 	    {
4238 	      if ((ptr[1] & 0xc0) != 0x80)
4239 	        break;
4240 
4241 	      ptr ++;
4242 	    }
4243 	    else if ((*ptr & 0xf0) == 0xe0)
4244 	    {
4245 	      if ((ptr[1] & 0xc0) != 0x80 || (ptr[2] & 0xc0) != 0x80)
4246 	        break;
4247 
4248 	      ptr += 2;
4249 	    }
4250 	    else if ((*ptr & 0xf8) == 0xf0)
4251 	    {
4252 	      if ((ptr[1] & 0xc0) != 0x80 || (ptr[2] & 0xc0) != 0x80 || (ptr[3] & 0xc0) != 0x80)
4253 	        break;
4254 
4255 	      ptr += 3;
4256 	    }
4257 	    else if (*ptr & 0x80)
4258 	      break;
4259 	    else if (*ptr < ' ' || *ptr == 0x7f)
4260 	      break;
4261 	  }
4262 
4263 	  if (*ptr)
4264 	  {
4265 	    if (*ptr < ' ' || *ptr == 0x7f)
4266 	    {
4267 	      ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad name value \"%s\" - bad control character (PWG 5100.14 section 8.1)."), attr->name, attr->values[i].string.text);
4268 	      return (0);
4269 	    }
4270 	    else
4271 	    {
4272 	      ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad name value \"%s\" - bad UTF-8 sequence (RFC 8011 section 5.1.3)."), attr->name, attr->values[i].string.text);
4273 	      return (0);
4274 	    }
4275           }
4276 
4277 	  if ((ptr - attr->values[i].string.text) > (IPP_MAX_NAME - 1))
4278 	  {
4279 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad name value \"%s\" - bad length %d (RFC 8011 section 5.1.3)."), attr->name, attr->values[i].string.text, (int)(ptr - attr->values[i].string.text));
4280 	    return (0);
4281 	  }
4282 	}
4283         break;
4284 
4285     case IPP_TAG_KEYWORD :
4286         for (i = 0; i < attr->num_values; i ++)
4287 	{
4288 	  for (ptr = attr->values[i].string.text; *ptr; ptr ++)
4289 	  {
4290 	    if (!isalnum(*ptr & 255) && *ptr != '-' && *ptr != '.' &&
4291 	        *ptr != '_')
4292 	      break;
4293 	  }
4294 
4295 	  if (*ptr || ptr == attr->values[i].string.text)
4296 	  {
4297 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad keyword value \"%s\" - invalid character (RFC 8011 section 5.1.4)."), attr->name, attr->values[i].string.text);
4298 	    return (0);
4299 	  }
4300 
4301 	  if ((ptr - attr->values[i].string.text) > (IPP_MAX_KEYWORD - 1))
4302 	  {
4303 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad keyword value \"%s\" - bad length %d (RFC 8011 section 5.1.4)."), attr->name, attr->values[i].string.text, (int)(ptr - attr->values[i].string.text));
4304 	    return (0);
4305 	  }
4306 	}
4307         break;
4308 
4309     case IPP_TAG_URI :
4310         for (i = 0; i < attr->num_values; i ++)
4311 	{
4312 	  uri_status = httpSeparateURI(HTTP_URI_CODING_ALL, attr->values[i].string.text, scheme, sizeof(scheme), userpass, sizeof(userpass), hostname, sizeof(hostname), &port, resource, sizeof(resource));
4313 
4314 	  if (uri_status < HTTP_URI_STATUS_OK)
4315 	  {
4316 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad URI value \"%s\" - %s (RFC 8011 section 5.1.6)."), attr->name, attr->values[i].string.text, httpURIStatusString(uri_status));
4317 	    return (0);
4318 	  }
4319 
4320 	  if (strlen(attr->values[i].string.text) > (IPP_MAX_URI - 1))
4321 	  {
4322 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad URI value \"%s\" - bad length %d (RFC 8011 section 5.1.6)."), attr->name, attr->values[i].string.text, (int)strlen(attr->values[i].string.text));
4323 	  }
4324 	}
4325         break;
4326 
4327     case IPP_TAG_URISCHEME :
4328         for (i = 0; i < attr->num_values; i ++)
4329 	{
4330 	  ptr = attr->values[i].string.text;
4331 	  if (islower(*ptr & 255))
4332 	  {
4333 	    for (ptr ++; *ptr; ptr ++)
4334 	    {
4335 	      if (!islower(*ptr & 255) && !isdigit(*ptr & 255) &&
4336 	          *ptr != '+' && *ptr != '-' && *ptr != '.')
4337                 break;
4338 	    }
4339 	  }
4340 
4341 	  if (*ptr || ptr == attr->values[i].string.text)
4342 	  {
4343 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad uriScheme value \"%s\" - bad characters (RFC 8011 section 5.1.7)."), attr->name, attr->values[i].string.text);
4344 	    return (0);
4345 	  }
4346 
4347 	  if ((ptr - attr->values[i].string.text) > (IPP_MAX_URISCHEME - 1))
4348 	  {
4349 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad uriScheme value \"%s\" - bad length %d (RFC 8011 section 5.1.7)."), attr->name, attr->values[i].string.text, (int)(ptr - attr->values[i].string.text));
4350 	    return (0);
4351 	  }
4352 	}
4353         break;
4354 
4355     case IPP_TAG_CHARSET :
4356         for (i = 0; i < attr->num_values; i ++)
4357 	{
4358 	  for (ptr = attr->values[i].string.text; *ptr; ptr ++)
4359 	  {
4360 	    if (!isprint(*ptr & 255) || isupper(*ptr & 255) ||
4361 	        isspace(*ptr & 255))
4362 	      break;
4363 	  }
4364 
4365 	  if (*ptr || ptr == attr->values[i].string.text)
4366 	  {
4367 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad charset value \"%s\" - bad characters (RFC 8011 section 5.1.8)."), attr->name, attr->values[i].string.text);
4368 	    return (0);
4369 	  }
4370 
4371 	  if ((ptr - attr->values[i].string.text) > (IPP_MAX_CHARSET - 1))
4372 	  {
4373 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad charset value \"%s\" - bad length %d (RFC 8011 section 5.1.8)."), attr->name, attr->values[i].string.text, (int)(ptr - attr->values[i].string.text));
4374 	    return (0);
4375 	  }
4376 	}
4377         break;
4378 
4379     case IPP_TAG_LANGUAGE :
4380        /*
4381         * The following regular expression is derived from the ABNF for
4382 	* language tags in RFC 4646.  All I can say is that this is the
4383 	* easiest way to check the values...
4384 	*/
4385 
4386         if ((i = regcomp(&re,
4387 			 "^("
4388 			 "(([a-z]{2,3}(-[a-z][a-z][a-z]){0,3})|[a-z]{4,8})"
4389 								/* language */
4390 			 "(-[a-z][a-z][a-z][a-z]){0,1}"		/* script */
4391 			 "(-([a-z][a-z]|[0-9][0-9][0-9])){0,1}"	/* region */
4392 			 "(-([a-z]{5,8}|[0-9][0-9][0-9]))*"	/* variant */
4393 			 "(-[a-wy-z](-[a-z0-9]{2,8})+)*"	/* extension */
4394 			 "(-x(-[a-z0-9]{1,8})+)*"		/* privateuse */
4395 			 "|"
4396 			 "x(-[a-z0-9]{1,8})+"			/* privateuse */
4397 			 "|"
4398 			 "[a-z]{1,3}(-[a-z][0-9]{2,8}){1,2}"	/* grandfathered */
4399 			 ")$",
4400 			 REG_NOSUB | REG_EXTENDED)) != 0)
4401         {
4402           char	temp[256];		/* Temporary error string */
4403 
4404           regerror(i, &re, temp, sizeof(temp));
4405 	  ipp_set_error(IPP_STATUS_ERROR_INTERNAL, _("Unable to compile naturalLanguage regular expression: %s."), temp);
4406 	  return (0);
4407         }
4408 
4409         for (i = 0; i < attr->num_values; i ++)
4410 	{
4411 	  if (regexec(&re, attr->values[i].string.text, 0, NULL, 0))
4412 	  {
4413 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad naturalLanguage value \"%s\" - bad characters (RFC 8011 section 5.1.9)."), attr->name, attr->values[i].string.text);
4414 	    regfree(&re);
4415 	    return (0);
4416 	  }
4417 
4418 	  if (strlen(attr->values[i].string.text) > (IPP_MAX_LANGUAGE - 1))
4419 	  {
4420 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad naturalLanguage value \"%s\" - bad length %d (RFC 8011 section 5.1.9)."), attr->name, attr->values[i].string.text, (int)strlen(attr->values[i].string.text));
4421 	    regfree(&re);
4422 	    return (0);
4423 	  }
4424 	}
4425 
4426 	regfree(&re);
4427         break;
4428 
4429     case IPP_TAG_MIMETYPE :
4430        /*
4431         * The following regular expression is derived from the ABNF for
4432 	* MIME media types in RFC 2045 and 4288.  All I can say is that this is
4433 	* the easiest way to check the values...
4434 	*/
4435 
4436         if ((i = regcomp(&re,
4437 			 "^"
4438 			 "[-a-zA-Z0-9!#$&.+^_]{1,127}"		/* type-name */
4439 			 "/"
4440 			 "[-a-zA-Z0-9!#$&.+^_]{1,127}"		/* subtype-name */
4441 			 "(;[-a-zA-Z0-9!#$&.+^_]{1,127}="	/* parameter= */
4442 			 "([-a-zA-Z0-9!#$&.+^_]{1,127}|\"[^\"]*\"))*"
4443 			 					/* value */
4444 			 "$",
4445 			 REG_NOSUB | REG_EXTENDED)) != 0)
4446         {
4447           char	temp[256];		/* Temporary error string */
4448 
4449           regerror(i, &re, temp, sizeof(temp));
4450 	  ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("Unable to compile mimeMediaType regular expression: %s."), temp);
4451 	  return (0);
4452         }
4453 
4454         for (i = 0; i < attr->num_values; i ++)
4455 	{
4456 	  if (regexec(&re, attr->values[i].string.text, 0, NULL, 0))
4457 	  {
4458 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad mimeMediaType value \"%s\" - bad characters (RFC 8011 section 5.1.10)."), attr->name, attr->values[i].string.text);
4459 	    regfree(&re);
4460 	    return (0);
4461 	  }
4462 
4463 	  if (strlen(attr->values[i].string.text) > (IPP_MAX_MIMETYPE - 1))
4464 	  {
4465 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad mimeMediaType value \"%s\" - bad length %d (RFC 8011 section 5.1.10)."), attr->name, attr->values[i].string.text, (int)strlen(attr->values[i].string.text));
4466 	    regfree(&re);
4467 	    return (0);
4468 	  }
4469 	}
4470 
4471 	regfree(&re);
4472         break;
4473 
4474     default :
4475         break;
4476   }
4477 
4478   return (1);
4479 }
4480 
4481 
4482 /*
4483  * 'ippValidateAttributes()' - Validate all attributes in an IPP message.
4484  *
4485  * This function validates the contents of the IPP message, including each
4486  * attribute.  Like @link ippValidateAttribute@, @link cupsLastErrorString@ is
4487  * set to a human-readable message on failure.
4488  *
4489  * @since CUPS 1.7/macOS 10.9@
4490  */
4491 
4492 int					/* O - 1 if valid, 0 otherwise */
ippValidateAttributes(ipp_t * ipp)4493 ippValidateAttributes(ipp_t *ipp)	/* I - IPP message */
4494 {
4495   ipp_attribute_t	*attr;		/* Current attribute */
4496 
4497 
4498   if (!ipp)
4499     return (1);
4500 
4501   for (attr = ipp->attrs; attr; attr = attr->next)
4502     if (!ippValidateAttribute(attr))
4503       return (0);
4504 
4505   return (1);
4506 }
4507 
4508 
4509 /*
4510  * 'ippWrite()' - Write data for an IPP message to a HTTP connection.
4511  */
4512 
4513 ipp_state_t				/* O - Current state */
ippWrite(http_t * http,ipp_t * ipp)4514 ippWrite(http_t *http,			/* I - HTTP connection */
4515          ipp_t  *ipp)			/* I - IPP data */
4516 {
4517   DEBUG_printf(("ippWrite(http=%p, ipp=%p)", (void *)http, (void *)ipp));
4518 
4519   if (!http)
4520     return (IPP_STATE_ERROR);
4521 
4522   return (ippWriteIO(http, (ipp_iocb_t)httpWrite2, http->blocking, NULL, ipp));
4523 }
4524 
4525 
4526 /*
4527  * 'ippWriteFile()' - Write data for an IPP message to a file.
4528  *
4529  * @since CUPS 1.1.19/macOS 10.3@
4530  */
4531 
4532 ipp_state_t				/* O - Current state */
ippWriteFile(int fd,ipp_t * ipp)4533 ippWriteFile(int   fd,			/* I - HTTP data */
4534              ipp_t *ipp)		/* I - IPP data */
4535 {
4536   DEBUG_printf(("ippWriteFile(fd=%d, ipp=%p)", fd, (void *)ipp));
4537 
4538   ipp->state = IPP_STATE_IDLE;
4539 
4540   return (ippWriteIO(&fd, (ipp_iocb_t)ipp_write_file, 1, NULL, ipp));
4541 }
4542 
4543 
4544 /*
4545  * 'ippWriteIO()' - Write data for an IPP message.
4546  *
4547  * @since CUPS 1.2/macOS 10.5@
4548  */
4549 
4550 ipp_state_t				/* O - Current state */
ippWriteIO(void * dst,ipp_iocb_t cb,int blocking,ipp_t * parent,ipp_t * ipp)4551 ippWriteIO(void       *dst,		/* I - Destination */
4552            ipp_iocb_t cb,		/* I - Write callback function */
4553 	   int        blocking,		/* I - Use blocking IO? */
4554 	   ipp_t      *parent,		/* I - Parent IPP message */
4555            ipp_t      *ipp)		/* I - IPP data */
4556 {
4557   int			i;		/* Looping var */
4558   int			n;		/* Length of data */
4559   unsigned char		*buffer,	/* Data buffer */
4560 			*bufptr;	/* Pointer into buffer */
4561   ipp_attribute_t	*attr;		/* Current attribute */
4562   _ipp_value_t		*value;		/* Current value */
4563 
4564 
4565   DEBUG_printf(("ippWriteIO(dst=%p, cb=%p, blocking=%d, parent=%p, ipp=%p)", (void *)dst, (void *)cb, blocking, (void *)parent, (void *)ipp));
4566 
4567   if (!dst || !ipp)
4568     return (IPP_STATE_ERROR);
4569 
4570   if ((buffer = (unsigned char *)_cupsBufferGet(IPP_BUF_SIZE)) == NULL)
4571   {
4572     DEBUG_puts("1ippWriteIO: Unable to get write buffer");
4573     return (IPP_STATE_ERROR);
4574   }
4575 
4576   switch (ipp->state)
4577   {
4578     case IPP_STATE_IDLE :
4579         ipp->state ++; /* Avoid common problem... */
4580 
4581     case IPP_STATE_HEADER :
4582         if (parent == NULL)
4583 	{
4584 	 /*
4585 	  * Send the request header:
4586 	  *
4587 	  *                 Version = 2 bytes
4588 	  *   Operation/Status Code = 2 bytes
4589 	  *              Request ID = 4 bytes
4590 	  *                   Total = 8 bytes
4591 	  */
4592 
4593           bufptr = buffer;
4594 
4595 	  *bufptr++ = ipp->request.any.version[0];
4596 	  *bufptr++ = ipp->request.any.version[1];
4597 	  *bufptr++ = (ipp_uchar_t)(ipp->request.any.op_status >> 8);
4598 	  *bufptr++ = (ipp_uchar_t)ipp->request.any.op_status;
4599 	  *bufptr++ = (ipp_uchar_t)(ipp->request.any.request_id >> 24);
4600 	  *bufptr++ = (ipp_uchar_t)(ipp->request.any.request_id >> 16);
4601 	  *bufptr++ = (ipp_uchar_t)(ipp->request.any.request_id >> 8);
4602 	  *bufptr++ = (ipp_uchar_t)ipp->request.any.request_id;
4603 
4604 	  DEBUG_printf(("2ippWriteIO: version=%d.%d", buffer[0], buffer[1]));
4605 	  DEBUG_printf(("2ippWriteIO: op_status=%04x",
4606 			ipp->request.any.op_status));
4607 	  DEBUG_printf(("2ippWriteIO: request_id=%d",
4608 			ipp->request.any.request_id));
4609 
4610           if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
4611 	  {
4612 	    DEBUG_puts("1ippWriteIO: Could not write IPP header...");
4613 	    _cupsBufferRelease((char *)buffer);
4614 	    return (IPP_STATE_ERROR);
4615 	  }
4616 	}
4617 
4618        /*
4619 	* Reset the state engine to point to the first attribute
4620 	* in the request/response, with no current group.
4621 	*/
4622 
4623         ipp->state   = IPP_STATE_ATTRIBUTE;
4624 	ipp->current = ipp->attrs;
4625 	ipp->curtag  = IPP_TAG_ZERO;
4626 
4627 	DEBUG_printf(("1ippWriteIO: ipp->current=%p", (void *)ipp->current));
4628 
4629        /*
4630         * If blocking is disabled, stop here...
4631 	*/
4632 
4633         if (!blocking)
4634 	  break;
4635 
4636     case IPP_STATE_ATTRIBUTE :
4637         while (ipp->current != NULL)
4638 	{
4639 	 /*
4640 	  * Write this attribute...
4641 	  */
4642 
4643 	  bufptr = buffer;
4644 	  attr   = ipp->current;
4645 
4646 	  ipp->current = ipp->current->next;
4647 
4648           if (!parent)
4649 	  {
4650 	    if (ipp->curtag != attr->group_tag)
4651 	    {
4652 	     /*
4653 	      * Send a group tag byte...
4654 	      */
4655 
4656 	      ipp->curtag = attr->group_tag;
4657 
4658 	      if (attr->group_tag == IPP_TAG_ZERO)
4659 		continue;
4660 
4661 	      DEBUG_printf(("2ippWriteIO: wrote group tag=%x(%s)",
4662 			    attr->group_tag, ippTagString(attr->group_tag)));
4663 	      *bufptr++ = (ipp_uchar_t)attr->group_tag;
4664 	    }
4665 	    else if (attr->group_tag == IPP_TAG_ZERO)
4666 	      continue;
4667 	  }
4668 
4669 	  DEBUG_printf(("1ippWriteIO: %s (%s%s)", attr->name,
4670 	                attr->num_values > 1 ? "1setOf " : "",
4671 			ippTagString(attr->value_tag)));
4672 
4673          /*
4674 	  * Write the attribute tag and name.
4675 	  *
4676 	  * The attribute name length does not include the trailing nul
4677 	  * character in the source string.
4678 	  *
4679 	  * Collection values (parent != NULL) are written differently...
4680 	  */
4681 
4682           if (parent == NULL)
4683 	  {
4684            /*
4685 	    * Get the length of the attribute name, and make sure it won't
4686 	    * overflow the buffer...
4687 	    */
4688 
4689             if ((n = (int)strlen(attr->name)) > (IPP_BUF_SIZE - 8))
4690 	    {
4691 	      DEBUG_printf(("1ippWriteIO: Attribute name too long (%d)", n));
4692 	      _cupsBufferRelease((char *)buffer);
4693 	      return (IPP_STATE_ERROR);
4694 	    }
4695 
4696            /*
4697 	    * Write the value tag, name length, and name string...
4698 	    */
4699 
4700             DEBUG_printf(("2ippWriteIO: writing value tag=%x(%s)",
4701 	                  attr->value_tag, ippTagString(attr->value_tag)));
4702             DEBUG_printf(("2ippWriteIO: writing name=%d,\"%s\"", n,
4703 	                  attr->name));
4704 
4705             if (attr->value_tag > 0xff)
4706             {
4707               *bufptr++ = IPP_TAG_EXTENSION;
4708 	      *bufptr++ = (ipp_uchar_t)(attr->value_tag >> 24);
4709 	      *bufptr++ = (ipp_uchar_t)(attr->value_tag >> 16);
4710 	      *bufptr++ = (ipp_uchar_t)(attr->value_tag >> 8);
4711 	      *bufptr++ = (ipp_uchar_t)attr->value_tag;
4712             }
4713             else
4714 	      *bufptr++ = (ipp_uchar_t)attr->value_tag;
4715 
4716 	    *bufptr++ = (ipp_uchar_t)(n >> 8);
4717 	    *bufptr++ = (ipp_uchar_t)n;
4718 	    memcpy(bufptr, attr->name, (size_t)n);
4719 	    bufptr += n;
4720           }
4721 	  else
4722 	  {
4723            /*
4724 	    * Get the length of the attribute name, and make sure it won't
4725 	    * overflow the buffer...
4726 	    */
4727 
4728             if ((n = (int)strlen(attr->name)) > (IPP_BUF_SIZE - 12))
4729 	    {
4730 	      DEBUG_printf(("1ippWriteIO: Attribute name too long (%d)", n));
4731 	      _cupsBufferRelease((char *)buffer);
4732 	      return (IPP_STATE_ERROR);
4733 	    }
4734 
4735            /*
4736 	    * Write the member name tag, name length, name string, value tag,
4737 	    * and empty name for the collection member attribute...
4738 	    */
4739 
4740             DEBUG_printf(("2ippWriteIO: writing value tag=%x(memberName)",
4741 	                  IPP_TAG_MEMBERNAME));
4742             DEBUG_printf(("2ippWriteIO: writing name=%d,\"%s\"", n,
4743 	                  attr->name));
4744             DEBUG_printf(("2ippWriteIO: writing value tag=%x(%s)",
4745 	                  attr->value_tag, ippTagString(attr->value_tag)));
4746             DEBUG_puts("2ippWriteIO: writing name=0,\"\"");
4747 
4748             *bufptr++ = IPP_TAG_MEMBERNAME;
4749 	    *bufptr++ = 0;
4750 	    *bufptr++ = 0;
4751 	    *bufptr++ = (ipp_uchar_t)(n >> 8);
4752 	    *bufptr++ = (ipp_uchar_t)n;
4753 	    memcpy(bufptr, attr->name, (size_t)n);
4754 	    bufptr += n;
4755 
4756             if (attr->value_tag > 0xff)
4757             {
4758               *bufptr++ = IPP_TAG_EXTENSION;
4759 	      *bufptr++ = (ipp_uchar_t)(attr->value_tag >> 24);
4760 	      *bufptr++ = (ipp_uchar_t)(attr->value_tag >> 16);
4761 	      *bufptr++ = (ipp_uchar_t)(attr->value_tag >> 8);
4762 	      *bufptr++ = (ipp_uchar_t)attr->value_tag;
4763             }
4764             else
4765 	      *bufptr++ = (ipp_uchar_t)attr->value_tag;
4766 
4767             *bufptr++ = 0;
4768             *bufptr++ = 0;
4769 	  }
4770 
4771          /*
4772 	  * Now write the attribute value(s)...
4773 	  */
4774 
4775 	  switch (attr->value_tag & ~IPP_TAG_CUPS_CONST)
4776 	  {
4777 	    case IPP_TAG_UNSUPPORTED_VALUE :
4778 	    case IPP_TAG_DEFAULT :
4779 	    case IPP_TAG_UNKNOWN :
4780 	    case IPP_TAG_NOVALUE :
4781 	    case IPP_TAG_NOTSETTABLE :
4782 	    case IPP_TAG_DELETEATTR :
4783 	    case IPP_TAG_ADMINDEFINE :
4784 		*bufptr++ = 0;
4785 		*bufptr++ = 0;
4786 	        break;
4787 
4788 	    case IPP_TAG_INTEGER :
4789 	    case IPP_TAG_ENUM :
4790 	        for (i = 0, value = attr->values;
4791 		     i < attr->num_values;
4792 		     i ++, value ++)
4793 		{
4794                   if ((IPP_BUF_SIZE - (bufptr - buffer)) < 9)
4795 		  {
4796                     if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
4797 	            {
4798 	              DEBUG_puts("1ippWriteIO: Could not write IPP "
4799 		                 "attribute...");
4800 		      _cupsBufferRelease((char *)buffer);
4801 	              return (IPP_STATE_ERROR);
4802 	            }
4803 
4804 		    bufptr = buffer;
4805 		  }
4806 
4807 		  if (i)
4808 		  {
4809 		   /*
4810 		    * Arrays and sets are done by sending additional
4811 		    * values with a zero-length name...
4812 		    */
4813 
4814                     *bufptr++ = (ipp_uchar_t)attr->value_tag;
4815 		    *bufptr++ = 0;
4816 		    *bufptr++ = 0;
4817 		  }
4818 
4819 		 /*
4820 	          * Integers and enumerations are both 4-byte signed
4821 		  * (twos-complement) values.
4822 		  *
4823 		  * Put the 2-byte length and 4-byte value into the buffer...
4824 		  */
4825 
4826 	          *bufptr++ = 0;
4827 		  *bufptr++ = 4;
4828 		  *bufptr++ = (ipp_uchar_t)(value->integer >> 24);
4829 		  *bufptr++ = (ipp_uchar_t)(value->integer >> 16);
4830 		  *bufptr++ = (ipp_uchar_t)(value->integer >> 8);
4831 		  *bufptr++ = (ipp_uchar_t)value->integer;
4832 		}
4833 		break;
4834 
4835 	    case IPP_TAG_BOOLEAN :
4836 	        for (i = 0, value = attr->values;
4837 		     i < attr->num_values;
4838 		     i ++, value ++)
4839 		{
4840                   if ((IPP_BUF_SIZE - (bufptr - buffer)) < 6)
4841 		  {
4842                     if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
4843 	            {
4844 	              DEBUG_puts("1ippWriteIO: Could not write IPP "
4845 		                 "attribute...");
4846 		      _cupsBufferRelease((char *)buffer);
4847 	              return (IPP_STATE_ERROR);
4848 	            }
4849 
4850 		    bufptr = buffer;
4851 		  }
4852 
4853 		  if (i)
4854 		  {
4855 		   /*
4856 		    * Arrays and sets are done by sending additional
4857 		    * values with a zero-length name...
4858 		    */
4859 
4860                     *bufptr++ = (ipp_uchar_t)attr->value_tag;
4861 		    *bufptr++ = 0;
4862 		    *bufptr++ = 0;
4863 		  }
4864 
4865                  /*
4866 		  * Boolean values are 1-byte; 0 = false, 1 = true.
4867 		  *
4868 		  * Put the 2-byte length and 1-byte value into the buffer...
4869 		  */
4870 
4871 	          *bufptr++ = 0;
4872 		  *bufptr++ = 1;
4873 		  *bufptr++ = (ipp_uchar_t)value->boolean;
4874 		}
4875 		break;
4876 
4877 	    case IPP_TAG_TEXT :
4878 	    case IPP_TAG_NAME :
4879 	    case IPP_TAG_KEYWORD :
4880 	    case IPP_TAG_URI :
4881 	    case IPP_TAG_URISCHEME :
4882 	    case IPP_TAG_CHARSET :
4883 	    case IPP_TAG_LANGUAGE :
4884 	    case IPP_TAG_MIMETYPE :
4885 	        for (i = 0, value = attr->values;
4886 		     i < attr->num_values;
4887 		     i ++, value ++)
4888 		{
4889 		  if (i)
4890 		  {
4891 		   /*
4892 		    * Arrays and sets are done by sending additional
4893 		    * values with a zero-length name...
4894 		    */
4895 
4896         	    DEBUG_printf(("2ippWriteIO: writing value tag=%x(%s)",
4897 		                  attr->value_tag,
4898 				  ippTagString(attr->value_tag)));
4899         	    DEBUG_printf(("2ippWriteIO: writing name=0,\"\""));
4900 
4901                     if ((IPP_BUF_SIZE - (bufptr - buffer)) < 3)
4902 		    {
4903                       if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
4904 	              {
4905 	        	DEBUG_puts("1ippWriteIO: Could not write IPP "
4906 			           "attribute...");
4907 			_cupsBufferRelease((char *)buffer);
4908 	        	return (IPP_STATE_ERROR);
4909 	              }
4910 
4911 		      bufptr = buffer;
4912 		    }
4913 
4914                     *bufptr++ = (ipp_uchar_t)attr->value_tag;
4915 		    *bufptr++ = 0;
4916 		    *bufptr++ = 0;
4917 		  }
4918 
4919                   if (value->string.text != NULL)
4920                     n = (int)strlen(value->string.text);
4921 		  else
4922 		    n = 0;
4923 
4924                   if (n > (IPP_BUF_SIZE - 2))
4925 		  {
4926 		    DEBUG_printf(("1ippWriteIO: String too long (%d)", n));
4927 		    _cupsBufferRelease((char *)buffer);
4928 		    return (IPP_STATE_ERROR);
4929 		  }
4930 
4931                   DEBUG_printf(("2ippWriteIO: writing string=%d,\"%s\"", n,
4932 		                value->string.text));
4933 
4934                   if ((int)(IPP_BUF_SIZE - (bufptr - buffer)) < (n + 2))
4935 		  {
4936                     if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
4937 	            {
4938 	              DEBUG_puts("1ippWriteIO: Could not write IPP "
4939 		                 "attribute...");
4940 		      _cupsBufferRelease((char *)buffer);
4941 	              return (IPP_STATE_ERROR);
4942 	            }
4943 
4944 		    bufptr = buffer;
4945 		  }
4946 
4947 		 /*
4948 		  * All simple strings consist of the 2-byte length and
4949 		  * character data without the trailing nul normally found
4950 		  * in C strings.  Also, strings cannot be longer than IPP_MAX_LENGTH
4951 		  * bytes since the 2-byte length is a signed (twos-complement)
4952 		  * value.
4953 		  *
4954 		  * Put the 2-byte length and string characters in the buffer.
4955 		  */
4956 
4957 	          *bufptr++ = (ipp_uchar_t)(n >> 8);
4958 		  *bufptr++ = (ipp_uchar_t)n;
4959 
4960 		  if (n > 0)
4961 		  {
4962 		    memcpy(bufptr, value->string.text, (size_t)n);
4963 		    bufptr += n;
4964 		  }
4965 		}
4966 		break;
4967 
4968 	    case IPP_TAG_DATE :
4969 	        for (i = 0, value = attr->values;
4970 		     i < attr->num_values;
4971 		     i ++, value ++)
4972 		{
4973                   if ((IPP_BUF_SIZE - (bufptr - buffer)) < 16)
4974 		  {
4975                     if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
4976 	            {
4977 	              DEBUG_puts("1ippWriteIO: Could not write IPP "
4978 		                 "attribute...");
4979 		      _cupsBufferRelease((char *)buffer);
4980 	              return (IPP_STATE_ERROR);
4981 	            }
4982 
4983 		    bufptr = buffer;
4984 		  }
4985 
4986 		  if (i)
4987 		  {
4988 		   /*
4989 		    * Arrays and sets are done by sending additional
4990 		    * values with a zero-length name...
4991 		    */
4992 
4993                     *bufptr++ = (ipp_uchar_t)attr->value_tag;
4994 		    *bufptr++ = 0;
4995 		    *bufptr++ = 0;
4996 		  }
4997 
4998                  /*
4999 		  * Date values consist of a 2-byte length and an
5000 		  * 11-byte date/time structure defined by RFC 1903.
5001 		  *
5002 		  * Put the 2-byte length and 11-byte date/time
5003 		  * structure in the buffer.
5004 		  */
5005 
5006 	          *bufptr++ = 0;
5007 		  *bufptr++ = 11;
5008 		  memcpy(bufptr, value->date, 11);
5009 		  bufptr += 11;
5010 		}
5011 		break;
5012 
5013 	    case IPP_TAG_RESOLUTION :
5014 	        for (i = 0, value = attr->values;
5015 		     i < attr->num_values;
5016 		     i ++, value ++)
5017 		{
5018                   if ((IPP_BUF_SIZE - (bufptr - buffer)) < 14)
5019 		  {
5020                     if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
5021 	            {
5022 	              DEBUG_puts("1ippWriteIO: Could not write IPP "
5023 		                 "attribute...");
5024 		      _cupsBufferRelease((char *)buffer);
5025 		      return (IPP_STATE_ERROR);
5026 	            }
5027 
5028 		    bufptr = buffer;
5029 		  }
5030 
5031 		  if (i)
5032 		  {
5033 		   /*
5034 		    * Arrays and sets are done by sending additional
5035 		    * values with a zero-length name...
5036 		    */
5037 
5038                     *bufptr++ = (ipp_uchar_t)attr->value_tag;
5039 		    *bufptr++ = 0;
5040 		    *bufptr++ = 0;
5041 		  }
5042 
5043                  /*
5044 		  * Resolution values consist of a 2-byte length,
5045 		  * 4-byte horizontal resolution value, 4-byte vertical
5046 		  * resolution value, and a 1-byte units value.
5047 		  *
5048 		  * Put the 2-byte length and resolution value data
5049 		  * into the buffer.
5050 		  */
5051 
5052 	          *bufptr++ = 0;
5053 		  *bufptr++ = 9;
5054 		  *bufptr++ = (ipp_uchar_t)(value->resolution.xres >> 24);
5055 		  *bufptr++ = (ipp_uchar_t)(value->resolution.xres >> 16);
5056 		  *bufptr++ = (ipp_uchar_t)(value->resolution.xres >> 8);
5057 		  *bufptr++ = (ipp_uchar_t)value->resolution.xres;
5058 		  *bufptr++ = (ipp_uchar_t)(value->resolution.yres >> 24);
5059 		  *bufptr++ = (ipp_uchar_t)(value->resolution.yres >> 16);
5060 		  *bufptr++ = (ipp_uchar_t)(value->resolution.yres >> 8);
5061 		  *bufptr++ = (ipp_uchar_t)value->resolution.yres;
5062 		  *bufptr++ = (ipp_uchar_t)value->resolution.units;
5063 		}
5064 		break;
5065 
5066 	    case IPP_TAG_RANGE :
5067 	        for (i = 0, value = attr->values;
5068 		     i < attr->num_values;
5069 		     i ++, value ++)
5070 		{
5071                   if ((IPP_BUF_SIZE - (bufptr - buffer)) < 13)
5072 		  {
5073                     if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
5074 	            {
5075 	              DEBUG_puts("1ippWriteIO: Could not write IPP "
5076 		                 "attribute...");
5077 		      _cupsBufferRelease((char *)buffer);
5078 	              return (IPP_STATE_ERROR);
5079 	            }
5080 
5081 		    bufptr = buffer;
5082 		  }
5083 
5084 		  if (i)
5085 		  {
5086 		   /*
5087 		    * Arrays and sets are done by sending additional
5088 		    * values with a zero-length name...
5089 		    */
5090 
5091                     *bufptr++ = (ipp_uchar_t)attr->value_tag;
5092 		    *bufptr++ = 0;
5093 		    *bufptr++ = 0;
5094 		  }
5095 
5096                  /*
5097 		  * Range values consist of a 2-byte length,
5098 		  * 4-byte lower value, and 4-byte upper value.
5099 		  *
5100 		  * Put the 2-byte length and range value data
5101 		  * into the buffer.
5102 		  */
5103 
5104 	          *bufptr++ = 0;
5105 		  *bufptr++ = 8;
5106 		  *bufptr++ = (ipp_uchar_t)(value->range.lower >> 24);
5107 		  *bufptr++ = (ipp_uchar_t)(value->range.lower >> 16);
5108 		  *bufptr++ = (ipp_uchar_t)(value->range.lower >> 8);
5109 		  *bufptr++ = (ipp_uchar_t)value->range.lower;
5110 		  *bufptr++ = (ipp_uchar_t)(value->range.upper >> 24);
5111 		  *bufptr++ = (ipp_uchar_t)(value->range.upper >> 16);
5112 		  *bufptr++ = (ipp_uchar_t)(value->range.upper >> 8);
5113 		  *bufptr++ = (ipp_uchar_t)value->range.upper;
5114 		}
5115 		break;
5116 
5117 	    case IPP_TAG_TEXTLANG :
5118 	    case IPP_TAG_NAMELANG :
5119 	        for (i = 0, value = attr->values;
5120 		     i < attr->num_values;
5121 		     i ++, value ++)
5122 		{
5123 		  if (i)
5124 		  {
5125 		   /*
5126 		    * Arrays and sets are done by sending additional
5127 		    * values with a zero-length name...
5128 		    */
5129 
5130                     if ((IPP_BUF_SIZE - (bufptr - buffer)) < 3)
5131 		    {
5132                       if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
5133 	              {
5134 	        	DEBUG_puts("1ippWriteIO: Could not write IPP "
5135 		                   "attribute...");
5136 			_cupsBufferRelease((char *)buffer);
5137 	        	return (IPP_STATE_ERROR);
5138 	              }
5139 
5140 		      bufptr = buffer;
5141 		    }
5142 
5143                     *bufptr++ = (ipp_uchar_t)attr->value_tag;
5144 		    *bufptr++ = 0;
5145 		    *bufptr++ = 0;
5146 		  }
5147 
5148                  /*
5149 		  * textWithLanguage and nameWithLanguage values consist
5150 		  * of a 2-byte length for both strings and their
5151 		  * individual lengths, a 2-byte length for the
5152 		  * character string, the character string without the
5153 		  * trailing nul, a 2-byte length for the character
5154 		  * set string, and the character set string without
5155 		  * the trailing nul.
5156 		  */
5157 
5158                   n = 4;
5159 
5160 		  if (value->string.language != NULL)
5161                     n += (int)strlen(value->string.language);
5162 
5163 		  if (value->string.text != NULL)
5164                     n += (int)strlen(value->string.text);
5165 
5166                   if (n > (IPP_BUF_SIZE - 2))
5167 		  {
5168 		    DEBUG_printf(("1ippWriteIO: text/nameWithLanguage value "
5169 		                  "too long (%d)", n));
5170 		    _cupsBufferRelease((char *)buffer);
5171 		    return (IPP_STATE_ERROR);
5172                   }
5173 
5174                   if ((int)(IPP_BUF_SIZE - (bufptr - buffer)) < (n + 2))
5175 		  {
5176                     if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
5177 	            {
5178 	              DEBUG_puts("1ippWriteIO: Could not write IPP "
5179 		                 "attribute...");
5180 		      _cupsBufferRelease((char *)buffer);
5181 	              return (IPP_STATE_ERROR);
5182 	            }
5183 
5184 		    bufptr = buffer;
5185 		  }
5186 
5187                  /* Length of entire value */
5188 	          *bufptr++ = (ipp_uchar_t)(n >> 8);
5189 		  *bufptr++ = (ipp_uchar_t)n;
5190 
5191                  /* Length of language */
5192 		  if (value->string.language != NULL)
5193 		    n = (int)strlen(value->string.language);
5194 		  else
5195 		    n = 0;
5196 
5197 	          *bufptr++ = (ipp_uchar_t)(n >> 8);
5198 		  *bufptr++ = (ipp_uchar_t)n;
5199 
5200                  /* Language */
5201 		  if (n > 0)
5202 		  {
5203 		    memcpy(bufptr, value->string.language, (size_t)n);
5204 		    bufptr += n;
5205 		  }
5206 
5207                  /* Length of text */
5208                   if (value->string.text != NULL)
5209 		    n = (int)strlen(value->string.text);
5210 		  else
5211 		    n = 0;
5212 
5213 	          *bufptr++ = (ipp_uchar_t)(n >> 8);
5214 		  *bufptr++ = (ipp_uchar_t)n;
5215 
5216                  /* Text */
5217 		  if (n > 0)
5218 		  {
5219 		    memcpy(bufptr, value->string.text, (size_t)n);
5220 		    bufptr += n;
5221 		  }
5222 		}
5223 		break;
5224 
5225             case IPP_TAG_BEGIN_COLLECTION :
5226 	        for (i = 0, value = attr->values;
5227 		     i < attr->num_values;
5228 		     i ++, value ++)
5229 		{
5230 		 /*
5231 		  * Collections are written with the begin-collection
5232 		  * tag first with a value of 0 length, followed by the
5233 		  * attributes in the collection, then the end-collection
5234 		  * value...
5235 		  */
5236 
5237                   if ((IPP_BUF_SIZE - (bufptr - buffer)) < 5)
5238 		  {
5239                     if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
5240 	            {
5241 	              DEBUG_puts("1ippWriteIO: Could not write IPP "
5242 		                 "attribute...");
5243 		      _cupsBufferRelease((char *)buffer);
5244 	              return (IPP_STATE_ERROR);
5245 	            }
5246 
5247 		    bufptr = buffer;
5248 		  }
5249 
5250 		  if (i)
5251 		  {
5252 		   /*
5253 		    * Arrays and sets are done by sending additional
5254 		    * values with a zero-length name...
5255 		    */
5256 
5257                     *bufptr++ = (ipp_uchar_t)attr->value_tag;
5258 		    *bufptr++ = 0;
5259 		    *bufptr++ = 0;
5260 		  }
5261 
5262                  /*
5263 		  * Write a data length of 0 and flush the buffer...
5264 		  */
5265 
5266 	          *bufptr++ = 0;
5267 		  *bufptr++ = 0;
5268 
5269                   if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
5270 	          {
5271 	            DEBUG_puts("1ippWriteIO: Could not write IPP "
5272 		               "attribute...");
5273 		    _cupsBufferRelease((char *)buffer);
5274 	            return (IPP_STATE_ERROR);
5275 	          }
5276 
5277 		  bufptr = buffer;
5278 
5279                  /*
5280 		  * Then write the collection attribute...
5281 		  */
5282 
5283                   value->collection->state = IPP_STATE_IDLE;
5284 
5285 		  if (ippWriteIO(dst, cb, 1, ipp,
5286 		                 value->collection) == IPP_STATE_ERROR)
5287 		  {
5288 		    DEBUG_puts("1ippWriteIO: Unable to write collection value");
5289 		    _cupsBufferRelease((char *)buffer);
5290 		    return (IPP_STATE_ERROR);
5291 		  }
5292 		}
5293 		break;
5294 
5295             default :
5296 	        for (i = 0, value = attr->values;
5297 		     i < attr->num_values;
5298 		     i ++, value ++)
5299 		{
5300 		  if (i)
5301 		  {
5302 		   /*
5303 		    * Arrays and sets are done by sending additional
5304 		    * values with a zero-length name...
5305 		    */
5306 
5307                     if ((IPP_BUF_SIZE - (bufptr - buffer)) < 3)
5308 		    {
5309                       if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
5310 	              {
5311 	        	DEBUG_puts("1ippWriteIO: Could not write IPP "
5312 		                   "attribute...");
5313 			_cupsBufferRelease((char *)buffer);
5314 	        	return (IPP_STATE_ERROR);
5315 	              }
5316 
5317 		      bufptr = buffer;
5318 		    }
5319 
5320                     *bufptr++ = (ipp_uchar_t)attr->value_tag;
5321 		    *bufptr++ = 0;
5322 		    *bufptr++ = 0;
5323 		  }
5324 
5325                  /*
5326 		  * An unknown value might some new value that a
5327 		  * vendor has come up with. It consists of a
5328 		  * 2-byte length and the bytes in the unknown
5329 		  * value buffer.
5330 		  */
5331 
5332                   n = value->unknown.length;
5333 
5334                   if (n > (IPP_BUF_SIZE - 2))
5335 		  {
5336 		    DEBUG_printf(("1ippWriteIO: Data length too long (%d)",
5337 		                  n));
5338 		    _cupsBufferRelease((char *)buffer);
5339 		    return (IPP_STATE_ERROR);
5340 		  }
5341 
5342                   if ((int)(IPP_BUF_SIZE - (bufptr - buffer)) < (n + 2))
5343 		  {
5344                     if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
5345 	            {
5346 	              DEBUG_puts("1ippWriteIO: Could not write IPP "
5347 		                 "attribute...");
5348 		      _cupsBufferRelease((char *)buffer);
5349 	              return (IPP_STATE_ERROR);
5350 	            }
5351 
5352 		    bufptr = buffer;
5353 		  }
5354 
5355                  /* Length of unknown value */
5356 	          *bufptr++ = (ipp_uchar_t)(n >> 8);
5357 		  *bufptr++ = (ipp_uchar_t)n;
5358 
5359                  /* Value */
5360 		  if (n > 0)
5361 		  {
5362 		    memcpy(bufptr, value->unknown.data, (size_t)n);
5363 		    bufptr += n;
5364 		  }
5365 		}
5366 		break;
5367 	  }
5368 
5369          /*
5370 	  * Write the data out...
5371 	  */
5372 
5373 	  if (bufptr > buffer)
5374 	  {
5375 	    if ((*cb)(dst, buffer, (size_t)(bufptr - buffer)) < 0)
5376 	    {
5377 	      DEBUG_puts("1ippWriteIO: Could not write IPP attribute...");
5378 	      _cupsBufferRelease((char *)buffer);
5379 	      return (IPP_STATE_ERROR);
5380 	    }
5381 
5382 	    DEBUG_printf(("2ippWriteIO: wrote %d bytes",
5383 			  (int)(bufptr - buffer)));
5384 	  }
5385 
5386 	 /*
5387           * If blocking is disabled and we aren't at the end of the attribute
5388           * list, stop here...
5389 	  */
5390 
5391           if (!blocking && ipp->current)
5392 	    break;
5393 	}
5394 
5395 	if (ipp->current == NULL)
5396 	{
5397          /*
5398 	  * Done with all of the attributes; add the end-of-attributes
5399 	  * tag or end-collection attribute...
5400 	  */
5401 
5402           if (parent == NULL)
5403 	  {
5404             buffer[0] = IPP_TAG_END;
5405 	    n         = 1;
5406 	  }
5407 	  else
5408 	  {
5409             buffer[0] = IPP_TAG_END_COLLECTION;
5410 	    buffer[1] = 0; /* empty name */
5411 	    buffer[2] = 0;
5412 	    buffer[3] = 0; /* empty value */
5413 	    buffer[4] = 0;
5414 	    n         = 5;
5415 	  }
5416 
5417 	  if ((*cb)(dst, buffer, (size_t)n) < 0)
5418 	  {
5419 	    DEBUG_puts("1ippWriteIO: Could not write IPP end-tag...");
5420 	    _cupsBufferRelease((char *)buffer);
5421 	    return (IPP_STATE_ERROR);
5422 	  }
5423 
5424 	  ipp->state = IPP_STATE_DATA;
5425 	}
5426         break;
5427 
5428     case IPP_STATE_DATA :
5429         break;
5430 
5431     default :
5432         break; /* anti-compiler-warning-code */
5433   }
5434 
5435   _cupsBufferRelease((char *)buffer);
5436 
5437   return (ipp->state);
5438 }
5439 
5440 
5441 /*
5442  * 'ipp_add_attr()' - Add a new attribute to the message.
5443  */
5444 
5445 static ipp_attribute_t *		/* O - New attribute */
ipp_add_attr(ipp_t * ipp,const char * name,ipp_tag_t group_tag,ipp_tag_t value_tag,int num_values)5446 ipp_add_attr(ipp_t      *ipp,		/* I - IPP message */
5447              const char *name,		/* I - Attribute name or NULL */
5448              ipp_tag_t  group_tag,	/* I - Group tag or IPP_TAG_ZERO */
5449              ipp_tag_t  value_tag,	/* I - Value tag or IPP_TAG_ZERO */
5450              int        num_values)	/* I - Number of values */
5451 {
5452   int			alloc_values;	/* Number of values to allocate */
5453   ipp_attribute_t	*attr;		/* New attribute */
5454 
5455 
5456   DEBUG_printf(("4ipp_add_attr(ipp=%p, name=\"%s\", group_tag=0x%x, value_tag=0x%x, num_values=%d)", (void *)ipp, name, group_tag, value_tag, num_values));
5457 
5458  /*
5459   * Range check input...
5460   */
5461 
5462   if (!ipp || num_values < 0)
5463     return (NULL);
5464 
5465  /*
5466   * Allocate memory, rounding the allocation up as needed...
5467   */
5468 
5469   if (num_values <= 1)
5470     alloc_values = 1;
5471   else
5472     alloc_values = (num_values + IPP_MAX_VALUES - 1) & ~(IPP_MAX_VALUES - 1);
5473 
5474   attr = calloc(1, sizeof(ipp_attribute_t) +
5475                 (size_t)(alloc_values - 1) * sizeof(_ipp_value_t));
5476 
5477   if (attr)
5478   {
5479    /*
5480     * Initialize attribute...
5481     */
5482 
5483     DEBUG_printf(("4debug_alloc: %p %s %s%s (%d values)", (void *)attr, name, num_values > 1 ? "1setOf " : "", ippTagString(value_tag), num_values));
5484 
5485     if (name)
5486       attr->name = _cupsStrAlloc(name);
5487 
5488     attr->group_tag  = group_tag;
5489     attr->value_tag  = value_tag;
5490     attr->num_values = num_values;
5491 
5492    /*
5493     * Add it to the end of the linked list...
5494     */
5495 
5496     if (ipp->last)
5497       ipp->last->next = attr;
5498     else
5499       ipp->attrs = attr;
5500 
5501     ipp->prev = ipp->last;
5502     ipp->last = ipp->current = attr;
5503   }
5504 
5505   DEBUG_printf(("5ipp_add_attr: Returning %p", (void *)attr));
5506 
5507   return (attr);
5508 }
5509 
5510 
5511 /*
5512  * 'ipp_free_values()' - Free attribute values.
5513  */
5514 
5515 static void
ipp_free_values(ipp_attribute_t * attr,int element,int count)5516 ipp_free_values(ipp_attribute_t *attr,	/* I - Attribute to free values from */
5517                 int             element,/* I - First value to free */
5518                 int             count)	/* I - Number of values to free */
5519 {
5520   int		i;			/* Looping var */
5521   _ipp_value_t	*value;			/* Current value */
5522 
5523 
5524   DEBUG_printf(("4ipp_free_values(attr=%p, element=%d, count=%d)", (void *)attr, element, count));
5525 
5526   if (!(attr->value_tag & IPP_TAG_CUPS_CONST))
5527   {
5528    /*
5529     * Free values as needed...
5530     */
5531 
5532     switch (attr->value_tag)
5533     {
5534       case IPP_TAG_TEXTLANG :
5535       case IPP_TAG_NAMELANG :
5536 	  if (element == 0 && count == attr->num_values &&
5537 	      attr->values[0].string.language)
5538 	  {
5539 	    _cupsStrFree(attr->values[0].string.language);
5540 	    attr->values[0].string.language = NULL;
5541 	  }
5542 	  /* Fall through to other string values */
5543 
5544       case IPP_TAG_TEXT :
5545       case IPP_TAG_NAME :
5546       case IPP_TAG_RESERVED_STRING :
5547       case IPP_TAG_KEYWORD :
5548       case IPP_TAG_URI :
5549       case IPP_TAG_URISCHEME :
5550       case IPP_TAG_CHARSET :
5551       case IPP_TAG_LANGUAGE :
5552       case IPP_TAG_MIMETYPE :
5553 	  for (i = count, value = attr->values + element;
5554 	       i > 0;
5555 	       i --, value ++)
5556 	  {
5557 	    _cupsStrFree(value->string.text);
5558 	    value->string.text = NULL;
5559 	  }
5560 	  break;
5561 
5562       case IPP_TAG_UNSUPPORTED_VALUE :
5563       case IPP_TAG_DEFAULT :
5564       case IPP_TAG_UNKNOWN :
5565       case IPP_TAG_NOVALUE :
5566       case IPP_TAG_NOTSETTABLE :
5567       case IPP_TAG_DELETEATTR :
5568       case IPP_TAG_ADMINDEFINE :
5569       case IPP_TAG_INTEGER :
5570       case IPP_TAG_ENUM :
5571       case IPP_TAG_BOOLEAN :
5572       case IPP_TAG_DATE :
5573       case IPP_TAG_RESOLUTION :
5574       case IPP_TAG_RANGE :
5575 	  break;
5576 
5577       case IPP_TAG_BEGIN_COLLECTION :
5578 	  for (i = count, value = attr->values + element;
5579 	       i > 0;
5580 	       i --, value ++)
5581 	  {
5582 	    ippDelete(value->collection);
5583 	    value->collection = NULL;
5584 	  }
5585 	  break;
5586 
5587       case IPP_TAG_STRING :
5588       default :
5589 	  for (i = count, value = attr->values + element;
5590 	       i > 0;
5591 	       i --, value ++)
5592 	  {
5593 	    if (value->unknown.data)
5594 	    {
5595 	      free(value->unknown.data);
5596 	      value->unknown.data = NULL;
5597 	    }
5598 	  }
5599 	  break;
5600     }
5601   }
5602 
5603  /*
5604   * If we are not freeing values from the end, move the remaining values up...
5605   */
5606 
5607   if ((element + count) < attr->num_values)
5608     memmove(attr->values + element, attr->values + element + count,
5609             (size_t)(attr->num_values - count - element) * sizeof(_ipp_value_t));
5610 
5611   attr->num_values -= count;
5612 }
5613 
5614 
5615 /*
5616  * 'ipp_get_code()' - Convert a C locale/charset name into an IPP language/charset code.
5617  *
5618  * This typically converts strings of the form "ll_CC", "ll-REGION", and "CHARSET_NUMBER"
5619  * to "ll-cc", "ll-region", and "charset-number", respectively.
5620  */
5621 
5622 static char *				/* O - Language code string */
ipp_get_code(const char * value,char * buffer,size_t bufsize)5623 ipp_get_code(const char *value,		/* I - Locale/charset string */
5624              char       *buffer,	/* I - String buffer */
5625              size_t     bufsize)	/* I - Size of string buffer */
5626 {
5627   char	*bufptr,			/* Pointer into buffer */
5628 	*bufend;			/* End of buffer */
5629 
5630 
5631  /*
5632   * Convert values to lowercase and change _ to - as needed...
5633   */
5634 
5635   for (bufptr = buffer, bufend = buffer + bufsize - 1;
5636        *value && bufptr < bufend;
5637        value ++)
5638     if (*value == '_')
5639       *bufptr++ = '-';
5640     else
5641       *bufptr++ = (char)_cups_tolower(*value);
5642 
5643   *bufptr = '\0';
5644 
5645  /*
5646   * Return the converted string...
5647   */
5648 
5649   return (buffer);
5650 }
5651 
5652 
5653 /*
5654  * 'ipp_lang_code()' - Convert a C locale name into an IPP language code.
5655  *
5656  * This typically converts strings of the form "ll_CC" and "ll-REGION" to "ll-cc" and
5657  * "ll-region", respectively.  It also converts the "C" (POSIX) locale to "en".
5658  */
5659 
5660 static char *				/* O - Language code string */
ipp_lang_code(const char * locale,char * buffer,size_t bufsize)5661 ipp_lang_code(const char *locale,	/* I - Locale string */
5662               char       *buffer,	/* I - String buffer */
5663               size_t     bufsize)	/* I - Size of string buffer */
5664 {
5665  /*
5666   * Map POSIX ("C") locale to generic English, otherwise convert the locale string as-is.
5667   */
5668 
5669   if (!_cups_strcasecmp(locale, "c"))
5670   {
5671     strlcpy(buffer, "en", bufsize);
5672     return (buffer);
5673   }
5674   else
5675     return (ipp_get_code(locale, buffer, bufsize));
5676 }
5677 
5678 
5679 /*
5680  * 'ipp_length()' - Compute the length of an IPP message or collection value.
5681  */
5682 
5683 static size_t				/* O - Size of IPP message */
ipp_length(ipp_t * ipp,int collection)5684 ipp_length(ipp_t *ipp,			/* I - IPP message or collection */
5685            int   collection)		/* I - 1 if a collection, 0 otherwise */
5686 {
5687   int			i;		/* Looping var */
5688   size_t		bytes;		/* Number of bytes */
5689   ipp_attribute_t	*attr;		/* Current attribute */
5690   ipp_tag_t		group;		/* Current group */
5691   _ipp_value_t		*value;		/* Current value */
5692 
5693 
5694   DEBUG_printf(("3ipp_length(ipp=%p, collection=%d)", (void *)ipp, collection));
5695 
5696   if (!ipp)
5697   {
5698     DEBUG_puts("4ipp_length: Returning 0 bytes");
5699     return (0);
5700   }
5701 
5702  /*
5703   * Start with 8 bytes for the IPP message header...
5704   */
5705 
5706   bytes = collection ? 0 : 8;
5707 
5708  /*
5709   * Then add the lengths of each attribute...
5710   */
5711 
5712   group = IPP_TAG_ZERO;
5713 
5714   for (attr = ipp->attrs; attr != NULL; attr = attr->next)
5715   {
5716     if (attr->group_tag != group && !collection)
5717     {
5718       group = attr->group_tag;
5719       if (group == IPP_TAG_ZERO)
5720 	continue;
5721 
5722       bytes ++;	/* Group tag */
5723     }
5724 
5725     if (!attr->name)
5726       continue;
5727 
5728     DEBUG_printf(("5ipp_length: attr->name=\"%s\", attr->num_values=%d, "
5729                   "bytes=" CUPS_LLFMT, attr->name, attr->num_values, CUPS_LLCAST bytes));
5730 
5731     if ((attr->value_tag & ~IPP_TAG_CUPS_CONST) < IPP_TAG_EXTENSION)
5732       bytes += (size_t)attr->num_values;/* Value tag for each value */
5733     else
5734       bytes += (size_t)(5 * attr->num_values);
5735 					/* Value tag for each value */
5736     bytes += (size_t)(2 * attr->num_values);
5737 					/* Name lengths */
5738     bytes += strlen(attr->name);	/* Name */
5739     bytes += (size_t)(2 * attr->num_values);
5740 					/* Value lengths */
5741 
5742     if (collection)
5743       bytes += 5;			/* Add membername overhead */
5744 
5745     switch (attr->value_tag & ~IPP_TAG_CUPS_CONST)
5746     {
5747       case IPP_TAG_UNSUPPORTED_VALUE :
5748       case IPP_TAG_DEFAULT :
5749       case IPP_TAG_UNKNOWN :
5750       case IPP_TAG_NOVALUE :
5751       case IPP_TAG_NOTSETTABLE :
5752       case IPP_TAG_DELETEATTR :
5753       case IPP_TAG_ADMINDEFINE :
5754           break;
5755 
5756       case IPP_TAG_INTEGER :
5757       case IPP_TAG_ENUM :
5758           bytes += (size_t)(4 * attr->num_values);
5759 	  break;
5760 
5761       case IPP_TAG_BOOLEAN :
5762           bytes += (size_t)attr->num_values;
5763 	  break;
5764 
5765       case IPP_TAG_TEXT :
5766       case IPP_TAG_NAME :
5767       case IPP_TAG_KEYWORD :
5768       case IPP_TAG_URI :
5769       case IPP_TAG_URISCHEME :
5770       case IPP_TAG_CHARSET :
5771       case IPP_TAG_LANGUAGE :
5772       case IPP_TAG_MIMETYPE :
5773 	  for (i = 0, value = attr->values;
5774 	       i < attr->num_values;
5775 	       i ++, value ++)
5776 	    if (value->string.text)
5777 	      bytes += strlen(value->string.text);
5778 	  break;
5779 
5780       case IPP_TAG_DATE :
5781           bytes += (size_t)(11 * attr->num_values);
5782 	  break;
5783 
5784       case IPP_TAG_RESOLUTION :
5785           bytes += (size_t)(9 * attr->num_values);
5786 	  break;
5787 
5788       case IPP_TAG_RANGE :
5789           bytes += (size_t)(8 * attr->num_values);
5790 	  break;
5791 
5792       case IPP_TAG_TEXTLANG :
5793       case IPP_TAG_NAMELANG :
5794           bytes += (size_t)(4 * attr->num_values);
5795 					/* Charset + text length */
5796 
5797 	  for (i = 0, value = attr->values;
5798 	       i < attr->num_values;
5799 	       i ++, value ++)
5800 	  {
5801 	    if (value->string.language)
5802 	      bytes += strlen(value->string.language);
5803 
5804 	    if (value->string.text)
5805 	      bytes += strlen(value->string.text);
5806 	  }
5807 	  break;
5808 
5809       case IPP_TAG_BEGIN_COLLECTION :
5810 	  for (i = 0, value = attr->values;
5811 	       i < attr->num_values;
5812 	       i ++, value ++)
5813             bytes += ipp_length(value->collection, 1);
5814 	  break;
5815 
5816       default :
5817 	  for (i = 0, value = attr->values;
5818 	       i < attr->num_values;
5819 	       i ++, value ++)
5820             bytes += (size_t)value->unknown.length;
5821 	  break;
5822     }
5823   }
5824 
5825  /*
5826   * Finally, add 1 byte for the "end of attributes" tag or 5 bytes
5827   * for the "end of collection" tag and return...
5828   */
5829 
5830   if (collection)
5831     bytes += 5;
5832   else
5833     bytes ++;
5834 
5835   DEBUG_printf(("4ipp_length: Returning " CUPS_LLFMT " bytes", CUPS_LLCAST bytes));
5836 
5837   return (bytes);
5838 }
5839 
5840 
5841 /*
5842  * 'ipp_read_file()' - Read IPP data from a file.
5843  */
5844 
5845 static ssize_t				/* O - Number of bytes read */
ipp_read_file(int * fd,ipp_uchar_t * buffer,size_t length)5846 ipp_read_file(int         *fd,		/* I - File descriptor */
5847               ipp_uchar_t *buffer,	/* O - Read buffer */
5848 	      size_t      length)	/* I - Number of bytes to read */
5849 {
5850 #ifdef _WIN32
5851   return ((ssize_t)read(*fd, buffer, (unsigned)length));
5852 #else
5853   return (read(*fd, buffer, length));
5854 #endif /* _WIN32 */
5855 }
5856 
5857 
5858 /*
5859  * 'ipp_read_http()' - Semi-blocking read on a HTTP connection...
5860  */
5861 
5862 static ssize_t				/* O - Number of bytes read */
ipp_read_http(http_t * http,ipp_uchar_t * buffer,size_t length)5863 ipp_read_http(http_t      *http,	/* I - Client connection */
5864               ipp_uchar_t *buffer,	/* O - Buffer for data */
5865 	      size_t      length)	/* I - Total length */
5866 {
5867   ssize_t	tbytes,			/* Total bytes read */
5868 		bytes;			/* Bytes read this pass */
5869 
5870 
5871   DEBUG_printf(("7ipp_read_http(http=%p, buffer=%p, length=%d)", (void *)http, (void *)buffer, (int)length));
5872 
5873  /*
5874   * Loop until all bytes are read...
5875   */
5876 
5877   for (tbytes = 0, bytes = 0;
5878        tbytes < (int)length;
5879        tbytes += bytes, buffer += bytes)
5880   {
5881     DEBUG_printf(("9ipp_read_http: tbytes=" CUPS_LLFMT ", http->state=%d", CUPS_LLCAST tbytes, http->state));
5882 
5883     if (http->state == HTTP_STATE_WAITING)
5884       break;
5885 
5886     if (http->used == 0 && !http->blocking)
5887     {
5888      /*
5889       * Wait up to 10 seconds for more data on non-blocking sockets...
5890       */
5891 
5892       if (!httpWait(http, 10000))
5893       {
5894        /*
5895 	* Signal no data...
5896 	*/
5897 
5898 	bytes = -1;
5899 	break;
5900       }
5901     }
5902     else if (http->used == 0 && http->timeout_value > 0)
5903     {
5904      /*
5905       * Wait up to timeout seconds for more data on blocking sockets...
5906       */
5907 
5908       if (!httpWait(http, (int)(1000 * http->timeout_value)))
5909       {
5910        /*
5911 	* Signal no data...
5912 	*/
5913 
5914 	bytes = -1;
5915 	break;
5916       }
5917     }
5918 
5919     if ((bytes = httpRead2(http, (char *)buffer, length - (size_t)tbytes)) < 0)
5920     {
5921 #ifdef _WIN32
5922       break;
5923 #else
5924       if (errno != EAGAIN && errno != EINTR)
5925 	break;
5926 
5927       bytes = 0;
5928 #endif /* _WIN32 */
5929     }
5930     else if (bytes == 0)
5931       break;
5932   }
5933 
5934  /*
5935   * Return the number of bytes read...
5936   */
5937 
5938   if (tbytes == 0 && bytes < 0)
5939     tbytes = -1;
5940 
5941   DEBUG_printf(("8ipp_read_http: Returning " CUPS_LLFMT " bytes", CUPS_LLCAST tbytes));
5942 
5943   return (tbytes);
5944 }
5945 
5946 
5947 /*
5948  * 'ipp_read_io()' - Read data for an IPP message.
5949  */
5950 
5951 static ipp_state_t			/* O - Current state */
ipp_read_io(void * src,ipp_iocb_t cb,int blocking,ipp_t * parent,ipp_t * ipp,int depth)5952 ipp_read_io(void       *src,		/* I - Data source */
5953 	    ipp_iocb_t cb,		/* I - Read callback function */
5954 	    int        blocking,	/* I - Use blocking IO? */
5955 	    ipp_t      *parent,		/* I - Parent request, if any */
5956 	    ipp_t      *ipp,		/* I - IPP data */
5957 	    int        depth)		/* I - Depth of collection */
5958 {
5959   int			n;		/* Length of data */
5960   unsigned char		*buffer,	/* Data buffer */
5961 			string[IPP_MAX_TEXT],
5962 					/* Small string buffer */
5963 			*bufptr,	/* Pointer into buffer */
5964 			*bufend;	/* End of buffer */
5965   ipp_attribute_t	*attr = NULL;	/* Current attribute */
5966   ipp_tag_t		tag;		/* Current tag */
5967   ipp_tag_t		value_tag;	/* Current value tag */
5968   _ipp_value_t		*value;		/* Current value */
5969 
5970 
5971   DEBUG_printf(("ipp_read_io(src=%p, cb=%p, blocking=%d, parent=%p, ipp=%p, depth=%d)", (void *)src, (void *)cb, blocking, (void *)parent, (void *)ipp, depth));
5972   DEBUG_printf(("2ipp_read_io: ipp->state=%d", ipp->state));
5973 
5974   if (depth > 10)
5975   {
5976     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP message nested too deeply."), 1);
5977     return (IPP_STATE_ERROR);
5978   }
5979 
5980   if ((buffer = (unsigned char *)_cupsBufferGet(IPP_BUF_SIZE)) == NULL)
5981   {
5982     DEBUG_puts("1ipp_read_io: Unable to get read buffer.");
5983     return (IPP_STATE_ERROR);
5984   }
5985 
5986   switch (ipp->state)
5987   {
5988     case IPP_STATE_IDLE :
5989         ipp->state ++; /* Avoid common problem... */
5990 
5991     case IPP_STATE_HEADER :
5992         if (parent == NULL)
5993 	{
5994 	 /*
5995           * Get the request header...
5996 	  */
5997 
5998           if ((*cb)(src, buffer, 8) < 8)
5999 	  {
6000 	    DEBUG_puts("1ipp_read_io: Unable to read header.");
6001 	    goto rollback;
6002 	  }
6003 
6004 	 /*
6005           * Then copy the request header over...
6006 	  */
6007 
6008           ipp->request.any.version[0]  = buffer[0];
6009           ipp->request.any.version[1]  = buffer[1];
6010           ipp->request.any.op_status = (buffer[2] << 8) | buffer[3];
6011           ipp->request.any.request_id = (buffer[4] << 24) | (buffer[5] << 16) | (buffer[6] << 8) | buffer[7];
6012 
6013           DEBUG_printf(("2ipp_read_io: version=%d.%d", buffer[0], buffer[1]));
6014 	  DEBUG_printf(("2ipp_read_io: op_status=%04x",
6015 	                ipp->request.any.op_status));
6016 	  DEBUG_printf(("2ipp_read_io: request_id=%d",
6017 	                ipp->request.any.request_id));
6018         }
6019 
6020         ipp->state   = IPP_STATE_ATTRIBUTE;
6021 	ipp->current = NULL;
6022 	ipp->curtag  = IPP_TAG_ZERO;
6023 	ipp->prev    = ipp->last;
6024 
6025        /*
6026         * If blocking is disabled, stop here...
6027 	*/
6028 
6029         if (!blocking)
6030 	  break;
6031 
6032     case IPP_STATE_ATTRIBUTE :
6033         for (;;)
6034 	{
6035 	  if ((*cb)(src, buffer, 1) < 1)
6036 	  {
6037 	    DEBUG_puts("1ipp_read_io: Callback returned EOF/error");
6038 	    goto rollback;
6039 	  }
6040 
6041 	  DEBUG_printf(("2ipp_read_io: ipp->current=%p, ipp->prev=%p", (void *)ipp->current, (void *)ipp->prev));
6042 
6043 	 /*
6044 	  * Read this attribute...
6045 	  */
6046 
6047           tag = (ipp_tag_t)buffer[0];
6048           if (tag == IPP_TAG_EXTENSION)
6049           {
6050            /*
6051             * Read 32-bit "extension" tag...
6052             */
6053 
6054 	    if ((*cb)(src, buffer, 4) < 4)
6055 	    {
6056 	      DEBUG_puts("1ipp_read_io: Callback returned EOF/error");
6057 	      goto rollback;
6058 	    }
6059 
6060 	    tag = (ipp_tag_t)((buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]);
6061 
6062             if (tag & IPP_TAG_CUPS_CONST)
6063             {
6064              /*
6065               * Fail if the high bit is set in the tag...
6066               */
6067 
6068 	      _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP extension tag larger than 0x7FFFFFFF."), 1);
6069 	      DEBUG_printf(("1ipp_read_io: bad tag 0x%x.", tag));
6070 	      goto rollback;
6071             }
6072           }
6073 
6074 	  if (tag == IPP_TAG_END)
6075 	  {
6076 	   /*
6077 	    * No more attributes left...
6078 	    */
6079 
6080             DEBUG_puts("2ipp_read_io: IPP_TAG_END.");
6081 
6082 	    ipp->state = IPP_STATE_DATA;
6083 	    break;
6084 	  }
6085 	  else if (tag == IPP_TAG_ZERO || (tag == IPP_TAG_OPERATION && ipp->curtag != IPP_TAG_ZERO))
6086 	  {
6087 	    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Invalid group tag."), 1);
6088 	    DEBUG_printf(("1ipp_read_io: bad tag 0x%02x.", tag));
6089 	    goto rollback;
6090 	  }
6091           else if (tag < IPP_TAG_UNSUPPORTED_VALUE)
6092 	  {
6093 	   /*
6094 	    * Group tag...  Set the current group and continue...
6095 	    */
6096 
6097             if (parent)
6098             {
6099 	      _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Invalid group tag."), 1);
6100 	      DEBUG_printf(("1ipp_read_io: bad tag 0x%02x.", tag));
6101 	      goto rollback;
6102             }
6103             else if (ipp->curtag == tag)
6104 	      ipp->prev = ippAddSeparator(ipp);
6105             else if (ipp->current)
6106 	      ipp->prev = ipp->current;
6107 
6108 	    ipp->curtag  = tag;
6109 	    ipp->current = NULL;
6110 	    attr         = NULL;
6111 	    DEBUG_printf(("2ipp_read_io: group tag=%x(%s), ipp->prev=%p", tag, ippTagString(tag), (void *)ipp->prev));
6112 	    continue;
6113 	  }
6114 
6115           DEBUG_printf(("2ipp_read_io: value tag=%x(%s)", tag,
6116 	                ippTagString(tag)));
6117 
6118          /*
6119 	  * Get the name...
6120 	  */
6121 
6122           if ((*cb)(src, buffer, 2) < 2)
6123 	  {
6124 	    DEBUG_puts("1ipp_read_io: unable to read name length.");
6125 	    goto rollback;
6126 	  }
6127 
6128           n = (buffer[0] << 8) | buffer[1];
6129 
6130           if (n >= IPP_BUF_SIZE)
6131 	  {
6132 	    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP name larger than 32767 bytes."), 1);
6133 	    DEBUG_printf(("1ipp_read_io: bad name length %d.", n));
6134 	    goto rollback;
6135 	  }
6136 
6137           DEBUG_printf(("2ipp_read_io: name length=%d", n));
6138 
6139           if (n && parent)
6140           {
6141             _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Invalid named IPP attribute in collection."), 1);
6142             DEBUG_puts("1ipp_read_io: bad attribute name in collection.");
6143 	    goto rollback;
6144           }
6145           else if (n == 0 && tag != IPP_TAG_MEMBERNAME && tag != IPP_TAG_END_COLLECTION)
6146 	  {
6147 	   /*
6148 	    * More values for current attribute...
6149 	    */
6150 
6151             if (ipp->current == NULL)
6152 	    {
6153 	      _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP attribute has no name."), 1);
6154 	      DEBUG_puts("1ipp_read_io: Attribute without name and no current.");
6155 	      goto rollback;
6156 	    }
6157 
6158             attr      = ipp->current;
6159 	    value_tag = (ipp_tag_t)(attr->value_tag & IPP_TAG_CUPS_MASK);
6160 
6161 	   /*
6162 	    * Make sure we aren't adding a new value of a different
6163 	    * type...
6164 	    */
6165 
6166 	    if (value_tag == IPP_TAG_ZERO)
6167 	    {
6168 	     /*
6169 	      * Setting the value of a collection member...
6170 	      */
6171 
6172 	      attr->value_tag = tag;
6173 	    }
6174 	    else if (value_tag == IPP_TAG_TEXTLANG ||
6175 	             value_tag == IPP_TAG_NAMELANG ||
6176 		     (value_tag >= IPP_TAG_TEXT &&
6177 		      value_tag <= IPP_TAG_MIMETYPE))
6178             {
6179 	     /*
6180 	      * String values can sometimes come across in different
6181 	      * forms; accept sets of differing values...
6182 	      */
6183 
6184 	      if (tag != IPP_TAG_TEXTLANG && tag != IPP_TAG_NAMELANG &&
6185 	          (tag < IPP_TAG_TEXT || tag > IPP_TAG_MIMETYPE) &&
6186 		  tag != IPP_TAG_NOVALUE)
6187 	      {
6188 		_cupsSetError(IPP_STATUS_ERROR_INTERNAL,
6189 		              _("IPP 1setOf attribute with incompatible value "
6190 		                "tags."), 1);
6191 		DEBUG_printf(("1ipp_read_io: 1setOf value tag %x(%s) != %x(%s)",
6192 			      value_tag, ippTagString(value_tag), tag,
6193 			      ippTagString(tag)));
6194 		goto rollback;
6195 	      }
6196 
6197               if (value_tag != tag)
6198               {
6199                 DEBUG_printf(("1ipp_read_io: Converting %s attribute from %s to %s.",
6200                               attr->name, ippTagString(value_tag), ippTagString(tag)));
6201 		if (!ippSetValueTag(ipp, &attr, tag))
6202 		  goto rollback;
6203 	      }
6204             }
6205 	    else if (value_tag == IPP_TAG_INTEGER ||
6206 	             value_tag == IPP_TAG_RANGE)
6207             {
6208 	     /*
6209 	      * Integer and rangeOfInteger values can sometimes be mixed; accept
6210 	      * sets of differing values...
6211 	      */
6212 
6213 	      if (tag != IPP_TAG_INTEGER && tag != IPP_TAG_RANGE)
6214 	      {
6215 		_cupsSetError(IPP_STATUS_ERROR_INTERNAL,
6216 		              _("IPP 1setOf attribute with incompatible value "
6217 		                "tags."), 1);
6218 		DEBUG_printf(("1ipp_read_io: 1setOf value tag %x(%s) != %x(%s)",
6219 			      value_tag, ippTagString(value_tag), tag,
6220 			      ippTagString(tag)));
6221 		goto rollback;
6222 	      }
6223 
6224               if (value_tag == IPP_TAG_INTEGER && tag == IPP_TAG_RANGE)
6225               {
6226                /*
6227                 * Convert integer values to rangeOfInteger values...
6228                 */
6229 
6230 		DEBUG_printf(("1ipp_read_io: Converting %s attribute to "
6231 		              "rangeOfInteger.", attr->name));
6232                 ippSetValueTag(ipp, &attr, IPP_TAG_RANGE);
6233               }
6234             }
6235 	    else if (value_tag != tag)
6236 	    {
6237 	      _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
6238 			    _("IPP 1setOf attribute with incompatible value "
6239 			      "tags."), 1);
6240 	      DEBUG_printf(("1ipp_read_io: value tag %x(%s) != %x(%s)",
6241 	                    value_tag, ippTagString(value_tag), tag,
6242 			    ippTagString(tag)));
6243 	      goto rollback;
6244             }
6245 
6246            /*
6247 	    * Finally, reallocate the attribute array as needed...
6248 	    */
6249 
6250 	    if ((value = ipp_set_value(ipp, &attr, attr->num_values)) == NULL)
6251 	      goto rollback;
6252 	  }
6253 	  else if (tag == IPP_TAG_MEMBERNAME)
6254 	  {
6255 	   /*
6256 	    * Name must be length 0!
6257 	    */
6258 
6259 	    if (n)
6260 	    {
6261 	      _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP member name is not empty."), 1);
6262 	      DEBUG_puts("1ipp_read_io: member name not empty.");
6263 	      goto rollback;
6264 	    }
6265 	    else if (!parent)
6266 	    {
6267 	      _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP member attribute outside of collection."), 1);
6268 	      DEBUG_puts("1ipp_read_io: member attribute outside of collection.");
6269 	      goto rollback;
6270 	    }
6271 
6272             if (ipp->current)
6273 	      ipp->prev = ipp->current;
6274 
6275 	    attr = ipp->current = ipp_add_attr(ipp, NULL, ipp->curtag, IPP_TAG_ZERO, 1);
6276 	    if (!attr)
6277 	    {
6278 	      _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to allocate IPP attribute."), 1);
6279 	      DEBUG_puts("1ipp_read_io: unable to allocate attribute.");
6280 	      goto rollback;
6281 	    }
6282 
6283 	    DEBUG_printf(("2ipp_read_io: membername, ipp->current=%p, ipp->prev=%p", (void *)ipp->current, (void *)ipp->prev));
6284 
6285 	    value = attr->values;
6286 	  }
6287 	  else if (tag != IPP_TAG_END_COLLECTION)
6288 	  {
6289 	   /*
6290 	    * New attribute; read the name and add it...
6291 	    */
6292 
6293 	    if ((*cb)(src, buffer, (size_t)n) < n)
6294 	    {
6295 	      DEBUG_puts("1ipp_read_io: unable to read name.");
6296 	      goto rollback;
6297 	    }
6298 
6299 	    buffer[n] = '\0';
6300 
6301             if (ipp->current)
6302 	      ipp->prev = ipp->current;
6303 
6304 	    if ((attr = ipp->current = ipp_add_attr(ipp, (char *)buffer, ipp->curtag, tag,
6305 	                                            1)) == NULL)
6306 	    {
6307 	      _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to allocate IPP attribute."), 1);
6308 	      DEBUG_puts("1ipp_read_io: unable to allocate attribute.");
6309 	      goto rollback;
6310 	    }
6311 
6312 	    DEBUG_printf(("2ipp_read_io: name=\"%s\", ipp->current=%p, ipp->prev=%p", buffer, (void *)ipp->current, (void *)ipp->prev));
6313 
6314 	    value = attr->values;
6315 	  }
6316 	  else
6317 	  {
6318 	    attr  = NULL;
6319 	    value = NULL;
6320 	  }
6321 
6322 	  if ((*cb)(src, buffer, 2) < 2)
6323 	  {
6324 	    DEBUG_puts("1ipp_read_io: unable to read value length.");
6325 	    goto rollback;
6326 	  }
6327 
6328 	  n = (buffer[0] << 8) | buffer[1];
6329           DEBUG_printf(("2ipp_read_io: value length=%d", n));
6330 
6331 	  if (n >= IPP_BUF_SIZE)
6332 	  {
6333 	    _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
6334 			  _("IPP value larger than 32767 bytes."), 1);
6335 	    DEBUG_printf(("1ipp_read_io: bad value length %d.", n));
6336 	    goto rollback;
6337 	  }
6338 
6339 	  switch (tag)
6340 	  {
6341 	    case IPP_TAG_INTEGER :
6342 	    case IPP_TAG_ENUM :
6343 		if (n != 4)
6344 		{
6345 		  if (tag == IPP_TAG_INTEGER)
6346 		    _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
6347 				  _("IPP integer value not 4 bytes."), 1);
6348 		  else
6349 		    _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
6350 				  _("IPP enum value not 4 bytes."), 1);
6351 		  DEBUG_printf(("1ipp_read_io: bad integer value length %d.", n));
6352 		  goto rollback;
6353 		}
6354 
6355 	        if ((*cb)(src, buffer, 4) < 4)
6356 		{
6357 	          DEBUG_puts("1ipp_read_io: Unable to read integer value.");
6358 		  goto rollback;
6359 		}
6360 
6361 		n = (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
6362 
6363                 if (attr->value_tag == IPP_TAG_RANGE)
6364                   value->range.lower = value->range.upper = n;
6365                 else
6366 		  value->integer = n;
6367 	        break;
6368 
6369 	    case IPP_TAG_BOOLEAN :
6370 		if (n != 1)
6371 		{
6372 		  _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP boolean value not 1 byte."),
6373 		                1);
6374 		  DEBUG_printf(("1ipp_read_io: bad boolean value length %d.", n));
6375 		  goto rollback;
6376 		}
6377 
6378 	        if ((*cb)(src, buffer, 1) < 1)
6379 		{
6380 	          DEBUG_puts("1ipp_read_io: Unable to read boolean value.");
6381 		  goto rollback;
6382 		}
6383 
6384                 value->boolean = (char)buffer[0];
6385 	        break;
6386 
6387 	    case IPP_TAG_UNSUPPORTED_VALUE :
6388 	    case IPP_TAG_DEFAULT :
6389 	    case IPP_TAG_UNKNOWN :
6390 	    case IPP_TAG_NOVALUE :
6391 	    case IPP_TAG_NOTSETTABLE :
6392 	    case IPP_TAG_DELETEATTR :
6393 	    case IPP_TAG_ADMINDEFINE :
6394 	       /*
6395 	        * These value types are not supposed to have values, however
6396 		* some vendors (Brother) do not implement IPP correctly and so
6397 		* we need to map non-empty values to text...
6398 		*/
6399 
6400 	        if (attr->value_tag == tag)
6401 		{
6402 		  if (n == 0)
6403 		    break;
6404 
6405 		  attr->value_tag = IPP_TAG_TEXT;
6406 		}
6407 
6408 	    case IPP_TAG_TEXT :
6409 	    case IPP_TAG_NAME :
6410 	    case IPP_TAG_RESERVED_STRING :
6411 	    case IPP_TAG_KEYWORD :
6412 	    case IPP_TAG_URI :
6413 	    case IPP_TAG_URISCHEME :
6414 	    case IPP_TAG_CHARSET :
6415 	    case IPP_TAG_LANGUAGE :
6416 	    case IPP_TAG_MIMETYPE :
6417 	        if (n > 0)
6418 	        {
6419 		  if ((*cb)(src, buffer, (size_t)n) < n)
6420 		  {
6421 		    DEBUG_puts("1ipp_read_io: unable to read string value.");
6422 		    goto rollback;
6423 		  }
6424 		}
6425 
6426 		buffer[n] = '\0';
6427 		value->string.text = _cupsStrAlloc((char *)buffer);
6428 		DEBUG_printf(("2ipp_read_io: value=\"%s\"", value->string.text));
6429 	        break;
6430 
6431 	    case IPP_TAG_DATE :
6432 		if (n != 11)
6433 		{
6434 		  _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP date value not 11 bytes."), 1);
6435 		  DEBUG_printf(("1ipp_read_io: bad date value length %d.", n));
6436 		  goto rollback;
6437 		}
6438 
6439 	        if ((*cb)(src, value->date, 11) < 11)
6440 		{
6441 	          DEBUG_puts("1ipp_read_io: Unable to read date value.");
6442 		  goto rollback;
6443 		}
6444 	        break;
6445 
6446 	    case IPP_TAG_RESOLUTION :
6447 		if (n != 9)
6448 		{
6449 		  _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
6450 		                _("IPP resolution value not 9 bytes."), 1);
6451 		  DEBUG_printf(("1ipp_read_io: bad resolution value length %d.", n));
6452 		  goto rollback;
6453 		}
6454 
6455 	        if ((*cb)(src, buffer, 9) < 9)
6456 		{
6457 	          DEBUG_puts("1ipp_read_io: Unable to read resolution value.");
6458 		  goto rollback;
6459 		}
6460 
6461                 value->resolution.xres  = (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
6462                 value->resolution.yres  = (buffer[4] << 24) | (buffer[5] << 16) | (buffer[6] << 8) | buffer[7];
6463                 value->resolution.units = (ipp_res_t)buffer[8];
6464 	        break;
6465 
6466 	    case IPP_TAG_RANGE :
6467 		if (n != 8)
6468 		{
6469 		  _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
6470 		                _("IPP rangeOfInteger value not 8 bytes."), 1);
6471 		  DEBUG_printf(("1ipp_read_io: bad rangeOfInteger value length "
6472 		                "%d.", n));
6473 		  goto rollback;
6474 		}
6475 
6476 	        if ((*cb)(src, buffer, 8) < 8)
6477 		{
6478 	          DEBUG_puts("1ipp_read_io: Unable to read range value.");
6479 		  goto rollback;
6480 		}
6481 
6482                 value->range.lower = (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
6483                 value->range.upper = (buffer[4] << 24) | (buffer[5] << 16) | (buffer[6] << 8) | buffer[7];
6484 	        break;
6485 
6486 	    case IPP_TAG_TEXTLANG :
6487 	    case IPP_TAG_NAMELANG :
6488 	        if (n < 4)
6489 		{
6490 		  if (tag == IPP_TAG_TEXTLANG)
6491 		    _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
6492 		                  _("IPP textWithLanguage value less than "
6493 		                    "minimum 4 bytes."), 1);
6494 		  else
6495 		    _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
6496 		                  _("IPP nameWithLanguage value less than "
6497 		                    "minimum 4 bytes."), 1);
6498 		  DEBUG_printf(("1ipp_read_io: bad stringWithLanguage value "
6499 		                "length %d.", n));
6500 		  goto rollback;
6501 		}
6502 
6503 	        if ((*cb)(src, buffer, (size_t)n) < n)
6504 		{
6505 	          DEBUG_puts("1ipp_read_io: Unable to read string w/language "
6506 		             "value.");
6507 		  goto rollback;
6508 		}
6509 
6510                 bufptr = buffer;
6511                 bufend = buffer + n;
6512 
6513 	       /*
6514 	        * text-with-language and name-with-language are composite
6515 		* values:
6516 		*
6517 		*    language-length
6518 		*    language
6519 		*    text-length
6520 		*    text
6521 		*/
6522 
6523 		n = (bufptr[0] << 8) | bufptr[1];
6524 
6525 		if ((bufptr + 2 + n + 2) > bufend || n >= (int)sizeof(string))
6526 		{
6527 		  _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
6528 		                _("IPP language length overflows value."), 1);
6529 		  DEBUG_printf(("1ipp_read_io: bad language value length %d.",
6530 		                n));
6531 		  goto rollback;
6532 		}
6533 		else if (n >= IPP_MAX_LANGUAGE)
6534 		{
6535 		  _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
6536 		                _("IPP language length too large."), 1);
6537 		  DEBUG_printf(("1ipp_read_io: bad language value length %d.",
6538 		                n));
6539 		  goto rollback;
6540 		}
6541 
6542 		memcpy(string, bufptr + 2, (size_t)n);
6543 		string[n] = '\0';
6544 
6545 		value->string.language = _cupsStrAlloc((char *)string);
6546 
6547                 bufptr += 2 + n;
6548 		n = (bufptr[0] << 8) | bufptr[1];
6549 
6550 		if ((bufptr + 2 + n) > bufend)
6551 		{
6552 		  _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
6553 		                _("IPP string length overflows value."), 1);
6554 		  DEBUG_printf(("1ipp_read_io: bad string value length %d.", n));
6555 		  goto rollback;
6556 		}
6557 
6558 		bufptr[2 + n] = '\0';
6559                 value->string.text = _cupsStrAlloc((char *)bufptr + 2);
6560 	        break;
6561 
6562             case IPP_TAG_BEGIN_COLLECTION :
6563 	       /*
6564 	        * Oh, boy, here comes a collection value, so read it...
6565 		*/
6566 
6567                 value->collection = ippNew();
6568 
6569                 if (n > 0)
6570 		{
6571 		  _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
6572 		                _("IPP begCollection value not 0 bytes."), 1);
6573 	          DEBUG_puts("1ipp_read_io: begCollection tag with value length "
6574 		             "> 0.");
6575 		  goto rollback;
6576 		}
6577 
6578 		if (ipp_read_io(src, cb, 1, ipp, value->collection, depth + 1) == IPP_STATE_ERROR)
6579 		{
6580 	          DEBUG_puts("1ipp_read_io: Unable to read collection value.");
6581 		  goto rollback;
6582 		}
6583                 break;
6584 
6585             case IPP_TAG_END_COLLECTION :
6586                 if (n > 0)
6587 		{
6588 		  _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
6589 		                _("IPP endCollection value not 0 bytes."), 1);
6590 	          DEBUG_puts("1ipp_read_io: endCollection tag with value length "
6591 		             "> 0.");
6592 		  goto rollback;
6593 		}
6594 
6595 		_cupsBufferRelease((char *)buffer);
6596 
6597 	        DEBUG_puts("1ipp_read_io: endCollection tag...");
6598 		return (ipp->state = IPP_STATE_DATA);
6599 
6600             case IPP_TAG_MEMBERNAME :
6601 	       /*
6602 	        * The value the name of the member in the collection, which
6603 		* we need to carry over...
6604 		*/
6605 
6606                 if (!attr)
6607                 {
6608 		  _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
6609 		                _("IPP memberName with no attribute."), 1);
6610 	          DEBUG_puts("1ipp_read_io: Member name without attribute.");
6611 		  goto rollback;
6612                 }
6613 		else if (n == 0)
6614 		{
6615 		  _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
6616 		                _("IPP memberName value is empty."), 1);
6617 	          DEBUG_puts("1ipp_read_io: Empty member name value.");
6618 		  goto rollback;
6619 		}
6620 		else if ((*cb)(src, buffer, (size_t)n) < n)
6621 		{
6622 	          DEBUG_puts("1ipp_read_io: Unable to read member name value.");
6623 		  goto rollback;
6624 		}
6625 
6626 		buffer[n] = '\0';
6627 		attr->name = _cupsStrAlloc((char *)buffer);
6628 
6629                /*
6630 	        * Since collection members are encoded differently than
6631 		* regular attributes, make sure we don't start with an
6632 		* empty value...
6633 		*/
6634 
6635                 attr->num_values --;
6636 
6637 		DEBUG_printf(("2ipp_read_io: member name=\"%s\"", attr->name));
6638 		break;
6639 
6640             case IPP_TAG_STRING :
6641             default : /* Other unsupported values */
6642                 if (tag == IPP_TAG_STRING && n > IPP_MAX_LENGTH)
6643 		{
6644 		  _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
6645 		                _("IPP octetString length too large."), 1);
6646 		  DEBUG_printf(("1ipp_read_io: bad octetString value length %d.",
6647 		                n));
6648 		  goto rollback;
6649 		}
6650 
6651                 value->unknown.length = n;
6652 
6653 	        if (n > 0)
6654 		{
6655 		  if ((value->unknown.data = malloc((size_t)n)) == NULL)
6656 		  {
6657 		    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to allocate IPP attribute."), 1);
6658 		    DEBUG_puts("1ipp_read_io: Unable to allocate value");
6659 		    goto rollback;
6660 		  }
6661 
6662 	          if ((*cb)(src, value->unknown.data, (size_t)n) < n)
6663 		  {
6664 	            DEBUG_puts("1ipp_read_io: Unable to read unsupported value.");
6665 		    goto rollback;
6666 		  }
6667 		}
6668 		else
6669 		  value->unknown.data = NULL;
6670 	        break;
6671 	  }
6672 
6673 	 /*
6674           * If blocking is disabled, stop here...
6675 	  */
6676 
6677           if (!blocking)
6678 	    break;
6679 	}
6680         break;
6681 
6682     case IPP_STATE_DATA :
6683         break;
6684 
6685     default :
6686         break; /* anti-compiler-warning-code */
6687   }
6688 
6689   DEBUG_printf(("1ipp_read_io: returning ipp->state=%d.", ipp->state));
6690   _cupsBufferRelease((char *)buffer);
6691 
6692   return (ipp->state);
6693 
6694   // If we get here, there was an error that required us to roll back the last
6695   // attribute read in order to keep the IPP message valid...
6696   rollback:
6697 
6698   _cupsBufferRelease((char *)buffer);
6699 
6700   if (attr)
6701     ippDeleteAttribute(ipp, attr);
6702 
6703   return (IPP_STATE_ERROR);
6704 }
6705 
6706 
6707 /*
6708  * 'ipp_set_error()' - Set a formatted, localized error string.
6709  */
6710 
6711 static void
ipp_set_error(ipp_status_t status,const char * format,...)6712 ipp_set_error(ipp_status_t status,	/* I - Status code */
6713               const char   *format,	/* I - Printf-style error string */
6714 	      ...)			/* I - Additional arguments as needed */
6715 {
6716   va_list	ap;			/* Pointer to additional args */
6717   char		buffer[2048];		/* Message buffer */
6718   cups_lang_t	*lang = cupsLangDefault();
6719 					/* Current language */
6720 
6721 
6722   va_start(ap, format);
6723   vsnprintf(buffer, sizeof(buffer), _cupsLangString(lang, format), ap);
6724   va_end(ap);
6725 
6726   _cupsSetError(status, buffer, 0);
6727 }
6728 
6729 
6730 /*
6731  * 'ipp_set_value()' - Get the value element from an attribute, expanding it as
6732  *                     needed.
6733  */
6734 
6735 static _ipp_value_t *			/* O  - IPP value element or NULL on error */
ipp_set_value(ipp_t * ipp,ipp_attribute_t ** attr,int element)6736 ipp_set_value(ipp_t           *ipp,	/* IO - IPP message */
6737               ipp_attribute_t **attr,	/* IO - IPP attribute */
6738               int             element)	/* I  - Value number (0-based) */
6739 {
6740   ipp_attribute_t	*temp,		/* New attribute pointer */
6741 			*current,	/* Current attribute in list */
6742 			*prev;		/* Previous attribute in list */
6743   int			alloc_values;	/* Allocated values */
6744 
6745 
6746  /*
6747   * If we are setting an existing value element, return it...
6748   */
6749 
6750   temp = *attr;
6751 
6752   if (temp->num_values <= 1)
6753     alloc_values = 1;
6754   else
6755     alloc_values = (temp->num_values + IPP_MAX_VALUES - 1) &
6756                    ~(IPP_MAX_VALUES - 1);
6757 
6758   if (element < alloc_values)
6759   {
6760     if (element >= temp->num_values)
6761       temp->num_values = element + 1;
6762 
6763     return (temp->values + element);
6764   }
6765 
6766  /*
6767   * Otherwise re-allocate the attribute - we allocate in groups of IPP_MAX_VALUE
6768   * values when num_values > 1.
6769   */
6770 
6771   if (alloc_values < IPP_MAX_VALUES)
6772     alloc_values = IPP_MAX_VALUES;
6773   else
6774     alloc_values += IPP_MAX_VALUES;
6775 
6776   DEBUG_printf(("4ipp_set_value: Reallocating for up to %d values.",
6777                 alloc_values));
6778 
6779  /*
6780   * Reallocate memory...
6781   */
6782 
6783   if ((temp = realloc(temp, sizeof(ipp_attribute_t) + (size_t)(alloc_values - 1) * sizeof(_ipp_value_t))) == NULL)
6784   {
6785     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to reallocate IPP attribute value."), 1);
6786     DEBUG_puts("4ipp_set_value: Unable to resize attribute.");
6787     return (NULL);
6788   }
6789 
6790  /*
6791   * Zero the new memory...
6792   */
6793 
6794   memset(temp->values + temp->num_values, 0, (size_t)(alloc_values - temp->num_values) * sizeof(_ipp_value_t));
6795 
6796   if (temp != *attr)
6797   {
6798    /*
6799     * Reset pointers in the list...
6800     */
6801 
6802 #ifndef __clang_analyzer__
6803     DEBUG_printf(("4debug_free: %p %s", (void *)*attr, temp->name));
6804 #endif /* !__clang_analyzer__ */
6805     DEBUG_printf(("4debug_alloc: %p %s %s%s (%d)", (void *)temp, temp->name, temp->num_values > 1 ? "1setOf " : "", ippTagString(temp->value_tag), temp->num_values));
6806 
6807     if (ipp->current == *attr && ipp->prev)
6808     {
6809      /*
6810       * Use current "previous" pointer...
6811       */
6812 
6813       prev = ipp->prev;
6814     }
6815     else
6816     {
6817      /*
6818       * Find this attribute in the linked list...
6819       */
6820 
6821       for (prev = NULL, current = ipp->attrs;
6822 	   current && current != *attr;
6823 	   prev = current, current = current->next);
6824 
6825       if (!current)
6826       {
6827        /*
6828 	* This is a serious error!
6829 	*/
6830 
6831 	*attr = temp;
6832 	_cupsSetError(IPP_STATUS_ERROR_INTERNAL,
6833 	              _("IPP attribute is not a member of the message."), 1);
6834 	DEBUG_puts("4ipp_set_value: Unable to find attribute in message.");
6835 	return (NULL);
6836       }
6837     }
6838 
6839     if (prev)
6840       prev->next = temp;
6841     else
6842       ipp->attrs = temp;
6843 
6844     ipp->current = temp;
6845     ipp->prev    = prev;
6846 
6847     if (ipp->last == *attr)
6848       ipp->last = temp;
6849 
6850     *attr = temp;
6851   }
6852 
6853  /*
6854   * Return the value element...
6855   */
6856 
6857   if (element >= temp->num_values)
6858     temp->num_values = element + 1;
6859 
6860   return (temp->values + element);
6861 }
6862 
6863 
6864 /*
6865  * 'ipp_write_file()' - Write IPP data to a file.
6866  */
6867 
6868 static ssize_t				/* O - Number of bytes written */
ipp_write_file(int * fd,ipp_uchar_t * buffer,size_t length)6869 ipp_write_file(int         *fd,		/* I - File descriptor */
6870                ipp_uchar_t *buffer,	/* I - Data to write */
6871                size_t      length)	/* I - Number of bytes to write */
6872 {
6873 #ifdef _WIN32
6874   return ((ssize_t)write(*fd, buffer, (unsigned)length));
6875 #else
6876   return (write(*fd, buffer, length));
6877 #endif /* _WIN32 */
6878 }
6879