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