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