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