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