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