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