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